mirror of https://github.com/python/cpython
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:
parent
3a8e2b6e65
commit
868b1afa05
|
@ -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__.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` instruction
|
||||||
|
now ensures methods are called only on objects of the correct type.
|
|
@ -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. */
|
||||||
|
|
Loading…
Reference in New Issue