From 69dd9f7bb85a574eba879f585fc22c4e1ecf01f4 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 30 Apr 2022 00:07:54 -0400 Subject: [PATCH 1/7] Check the types of PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS --- Python/ceval.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 1d2c6432d062f7..61ee407d7178fd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5287,17 +5287,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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_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; From 60098e8c483dede31173f247bd2d7bec27f0cda4 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 30 Apr 2022 00:16:14 -0400 Subject: [PATCH 2/7] Add tests --- Lib/test/test_descr.py | 12 ++++++++++++ Python/ceval.c | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 378ff5227e221a..74a7d9ec205d75 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4726,6 +4726,18 @@ class FakeStr: 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): + list.sort(thing) + for i in range(20): + with self.assertRaises(TypeError): + str.split(thing) + def test_repr_as_str(self): # Issue #11603: crash or infinite loop when rebinding __str__ as # __repr__. diff --git a/Python/ceval.c b/Python/ceval.c index 61ee407d7178fd..23e1627c502101 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5294,7 +5294,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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_TYPE(self) != d_type, PRECALL); + DEOPT_IF(!Py_IS_TYPE(self, d_type), PRECALL); STAT_INC(PRECALL, hit); SKIP_CALL(); int nargs = total_args-1; From 2cfc2298dbccc49055b11864d7eba3095940443f Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 30 Apr 2022 04:26:03 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst 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 00000000000000..8469456ca59060 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst @@ -0,0 +1 @@ +The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` now ensures methods are called only on objects of the correct type. From 70b8ae1ba56bbd9a01da38bdd58da60d7bb853e2 Mon Sep 17 00:00:00 2001 From: Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com> Date: Sat, 30 Apr 2022 00:27:14 -0400 Subject: [PATCH 4/7] Update Misc/NEWS.d/next/Core and Builtins/2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst --- .../2022-04-30-04-26-01.gh-issue-92063.vHnhf6.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 8469456ca59060..d737ccc24481f5 100644 --- 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 @@ -1 +1,2 @@ -The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` now ensures methods are called only on objects of the correct type. +The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` instruction +now ensures methods are called only on objects of the correct type. From 41e4aeb8d383d45c3191d77ca2c06d668b89d0a9 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 30 Apr 2022 01:20:31 -0400 Subject: [PATCH 5/7] fix PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS as well --- Lib/test/test_descr.py | 3 +++ Python/ceval.c | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 74a7d9ec205d75..240835971b04bd 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4737,6 +4737,9 @@ class Thing: for i in range(20): with self.assertRaises(TypeError): str.split(thing) + for i in range(20): + with self.assertRaises(TypeError): + str.upper(thing) def test_repr_as_str(self): # Issue #11603: crash or infinite loop when rebinding __str__ as diff --git a/Python/ceval.c b/Python/ceval.c index 23e1627c502101..61baa084f8774f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5327,9 +5327,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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(); @@ -5339,7 +5341,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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)); From 3d33c3d9672727787e831676d567ccfc6df98e38 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 30 Apr 2022 01:52:47 -0400 Subject: [PATCH 6/7] fix PRECALL_NO_KW_METHOD_DESCRIPTOR_O --- Lib/test/test_descr.py | 4 ++++ Python/ceval.c | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 240835971b04bd..9d12daa2ac2703 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4740,6 +4740,10 @@ class Thing: for i in range(20): with self.assertRaises(TypeError): str.upper(thing) + from collections import deque + for i in range(20): + with self.assertRaises(TypeError): + deque.append(thing, thing) def test_repr_as_str(self): # Issue #11603: crash or infinite loop when rebinding __str__ as diff --git a/Python/ceval.c b/Python/ceval.c index 61baa084f8774f..79afacd75a44eb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5254,11 +5254,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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)); From adb1504e59c2070849319f52b8bdb5b87fa00b33 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 30 Apr 2022 02:17:50 -0400 Subject: [PATCH 7/7] fix PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST --- Lib/test/test_descr.py | 8 ++++++++ Python/ceval.c | 11 +++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 9d12daa2ac2703..48d43d7af85d9e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4733,16 +4733,24 @@ class Thing: 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): diff --git a/Python/ceval.c b/Python/ceval.c index 79afacd75a44eb..f3329b5d9d454d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5361,17 +5361,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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. */