diff options
author | Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com> | 2022-04-30 18:35:33 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-30 18:35:33 (GMT) |
commit | 868b1afa05c7847e6ffdd1808916719b511b4bcc (patch) | |
tree | 1befed8275e5b8cbcf5772be69f2f9bae9b7c41c | |
parent | 3a8e2b6e65fea1252477f6e29a384fa9a492ed06 (diff) | |
download | cpython-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.py | 27 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst | 2 | ||||
-rw-r--r-- | Python/ceval.c | 43 |
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. */ |