summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>2022-04-30 18:35:33 (GMT)
committerGitHub <noreply@github.com>2022-04-30 18:35:33 (GMT)
commit868b1afa05c7847e6ffdd1808916719b511b4bcc (patch)
tree1befed8275e5b8cbcf5772be69f2f9bae9b7c41c
parent3a8e2b6e65fea1252477f6e29a384fa9a492ed06 (diff)
downloadcpython-868b1afa05c7847e6ffdd1808916719b511b4bcc.zip
cpython-868b1afa05c7847e6ffdd1808916719b511b4bcc.tar.gz
cpython-868b1afa05c7847e6ffdd1808916719b511b4bcc.tar.bz2
gh-92063: Enforce types in specialized PRECALL opcodes (GH-92068)
* Check the types of PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS * fix PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS as well * fix PRECALL_NO_KW_METHOD_DESCRIPTOR_O * fix PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST
-rw-r--r--Lib/test/test_descr.py27
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst2
-rw-r--r--Python/ceval.c43
3 files changed, 56 insertions, 16 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 378ff52..48d43d7 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -4726,6 +4726,33 @@ order (MRO) for bases """
with self.assertRaises(TypeError):
str.__add__(fake_str, "abc")
+ def test_specialized_method_calls_check_types(self):
+ # https://github.com/python/cpython/issues/92063
+ class Thing:
+ pass
+ thing = Thing()
+ for i in range(20):
+ with self.assertRaises(TypeError):
+ # PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS
+ list.sort(thing)
+ for i in range(20):
+ with self.assertRaises(TypeError):
+ # PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS
+ str.split(thing)
+ for i in range(20):
+ with self.assertRaises(TypeError):
+ # PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS
+ str.upper(thing)
+ for i in range(20):
+ with self.assertRaises(TypeError):
+ # PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST
+ str.strip(thing)
+ from collections import deque
+ for i in range(20):
+ with self.assertRaises(TypeError):
+ # PRECALL_NO_KW_METHOD_DESCRIPTOR_O
+ deque.append(thing, thing)
+
def test_repr_as_str(self):
# Issue #11603: crash or infinite loop when rebinding __str__ as
# __repr__.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst
new file mode 100644
index 0000000..d737ccc
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst
@@ -0,0 +1,2 @@
+The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` instruction
+now ensures methods are called only on objects of the correct type.
diff --git a/Python/ceval.c b/Python/ceval.c
index 1d2c643..f3329b5 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -5254,11 +5254,15 @@ handle_eval_breaker:
assert(call_shape.kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
DEOPT_IF(total_args != 2, PRECALL);
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
- PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
+ PyMethodDef *meth = callable->d_method;
DEOPT_IF(meth->ml_flags != METH_O, PRECALL);
+ PyObject *arg = TOP();
+ PyObject *self = SECOND();
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
STAT_INC(PRECALL, hit);
SKIP_CALL();
PyCFunction cfunc = meth->ml_meth;
@@ -5267,8 +5271,6 @@ handle_eval_breaker:
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
goto error;
}
- PyObject *arg = TOP();
- PyObject *self = SECOND();
PyObject *res = cfunc(self, arg);
_Py_LeaveRecursiveCall(tstate);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -5287,17 +5289,22 @@ handle_eval_breaker:
TARGET(PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
- PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
+ PyMethodDef *meth = callable->d_method;
DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), PRECALL);
+ PyTypeObject *d_type = callable->d_common.d_type;
+ PyObject *self = PEEK(total_args);
+ DEOPT_IF(!Py_IS_TYPE(self, d_type), PRECALL);
STAT_INC(PRECALL, hit);
SKIP_CALL();
int nargs = total_args-1;
STACK_SHRINK(nargs);
- _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
- PyObject *self = TOP();
- PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), call_shape.kwnames);
+ _PyCFunctionFastWithKeywords cfunc =
+ (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
+ PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
+ call_shape.kwnames);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
call_shape.kwnames = NULL;
@@ -5322,9 +5329,11 @@ handle_eval_breaker:
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
DEOPT_IF(total_args != 1, PRECALL);
- PyObject *callable = SECOND();
+ PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND();
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
- PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
+ PyMethodDef *meth = callable->d_method;
+ PyObject *self = TOP();
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
DEOPT_IF(meth->ml_flags != METH_NOARGS, PRECALL);
STAT_INC(PRECALL, hit);
SKIP_CALL();
@@ -5334,7 +5343,6 @@ handle_eval_breaker:
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
goto error;
}
- PyObject *self = TOP();
PyObject *res = cfunc(self, NULL);
_Py_LeaveRecursiveCall(tstate);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -5353,17 +5361,20 @@ handle_eval_breaker:
assert(call_shape.kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
/* Builtin METH_FASTCALL methods, without keywords */
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
- PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
+ PyMethodDef *meth = callable->d_method;
DEOPT_IF(meth->ml_flags != METH_FASTCALL, PRECALL);
+ PyObject *self = PEEK(total_args);
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
STAT_INC(PRECALL, hit);
SKIP_CALL();
- _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ _PyCFunctionFast cfunc =
+ (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
int nargs = total_args-1;
STACK_SHRINK(nargs);
- PyObject *self = TOP();
PyObject *res = cfunc(self, stack_pointer, nargs);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
/* Clear the stack of the arguments. */