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
This commit is contained in:
Dennis Sweeney 2022-04-30 14:35:33 -04:00 committed by GitHub
parent 3a8e2b6e65
commit 868b1afa05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 16 deletions

View File

@ -4726,6 +4726,33 @@ order (MRO) for bases """
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
str.__add__(fake_str, "abc") 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): def test_repr_as_str(self):
# Issue #11603: crash or infinite loop when rebinding __str__ as # Issue #11603: crash or infinite loop when rebinding __str__ as
# __repr__. # __repr__.

View File

@ -0,0 +1,2 @@
The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` instruction
now ensures methods are called only on objects of the correct type.

View File

@ -5254,11 +5254,15 @@ handle_eval_breaker:
assert(call_shape.kwnames == NULL); assert(call_shape.kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg); int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth; 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(total_args != 2, PRECALL);
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), 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); 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); STAT_INC(PRECALL, hit);
SKIP_CALL(); SKIP_CALL();
PyCFunction cfunc = meth->ml_meth; PyCFunction cfunc = meth->ml_meth;
@ -5267,8 +5271,6 @@ handle_eval_breaker:
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) { if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
goto error; goto error;
} }
PyObject *arg = TOP();
PyObject *self = SECOND();
PyObject *res = cfunc(self, arg); PyObject *res = cfunc(self, arg);
_Py_LeaveRecursiveCall(tstate); _Py_LeaveRecursiveCall(tstate);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -5287,17 +5289,22 @@ handle_eval_breaker:
TARGET(PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { TARGET(PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
int is_meth = is_method(stack_pointer, oparg); int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth; 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); 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); 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); STAT_INC(PRECALL, hit);
SKIP_CALL(); SKIP_CALL();
int nargs = total_args-1; int nargs = total_args-1;
STACK_SHRINK(nargs); STACK_SHRINK(nargs);
_PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; _PyCFunctionFastWithKeywords cfunc =
PyObject *self = TOP(); (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), call_shape.kwnames); PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
call_shape.kwnames);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
call_shape.kwnames = NULL; call_shape.kwnames = NULL;
@ -5322,9 +5329,11 @@ handle_eval_breaker:
int is_meth = is_method(stack_pointer, oparg); int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth; int total_args = oparg + is_meth;
DEOPT_IF(total_args != 1, PRECALL); DEOPT_IF(total_args != 1, PRECALL);
PyObject *callable = SECOND(); PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND();
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL); 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); DEOPT_IF(meth->ml_flags != METH_NOARGS, PRECALL);
STAT_INC(PRECALL, hit); STAT_INC(PRECALL, hit);
SKIP_CALL(); SKIP_CALL();
@ -5334,7 +5343,6 @@ handle_eval_breaker:
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) { if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
goto error; goto error;
} }
PyObject *self = TOP();
PyObject *res = cfunc(self, NULL); PyObject *res = cfunc(self, NULL);
_Py_LeaveRecursiveCall(tstate); _Py_LeaveRecursiveCall(tstate);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -5353,17 +5361,20 @@ handle_eval_breaker:
assert(call_shape.kwnames == NULL); assert(call_shape.kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg); int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth; 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 */ /* Builtin METH_FASTCALL methods, without keywords */
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), 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_FASTCALL, PRECALL); 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); STAT_INC(PRECALL, hit);
SKIP_CALL(); SKIP_CALL();
_PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth; _PyCFunctionFast cfunc =
(_PyCFunctionFast)(void(*)(void))meth->ml_meth;
int nargs = total_args-1; int nargs = total_args-1;
STACK_SHRINK(nargs); STACK_SHRINK(nargs);
PyObject *self = TOP();
PyObject *res = cfunc(self, stack_pointer, nargs); PyObject *res = cfunc(self, stack_pointer, nargs);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
/* Clear the stack of the arguments. */ /* Clear the stack of the arguments. */