Skip to content

Commit 868b1af

Browse files
authored
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
1 parent 3a8e2b6 commit 868b1af

File tree

3 files changed

+56
-16
lines changed

3 files changed

+56
-16
lines changed

Lib/test/test_descr.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4726,6 +4726,33 @@ class FakeStr:
47264726
with self.assertRaises(TypeError):
47274727
str.__add__(fake_str, "abc")
47284728

4729+
def test_specialized_method_calls_check_types(self):
4730+
# https://github.com/python/cpython/issues/92063
4731+
class Thing:
4732+
pass
4733+
thing = Thing()
4734+
for i in range(20):
4735+
with self.assertRaises(TypeError):
4736+
# PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS
4737+
list.sort(thing)
4738+
for i in range(20):
4739+
with self.assertRaises(TypeError):
4740+
# PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS
4741+
str.split(thing)
4742+
for i in range(20):
4743+
with self.assertRaises(TypeError):
4744+
# PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS
4745+
str.upper(thing)
4746+
for i in range(20):
4747+
with self.assertRaises(TypeError):
4748+
# PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST
4749+
str.strip(thing)
4750+
from collections import deque
4751+
for i in range(20):
4752+
with self.assertRaises(TypeError):
4753+
# PRECALL_NO_KW_METHOD_DESCRIPTOR_O
4754+
deque.append(thing, thing)
4755+
47294756
def test_repr_as_str(self):
47304757
# Issue #11603: crash or infinite loop when rebinding __str__ as
47314758
# __repr__.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The ``PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS`` instruction
2+
now ensures methods are called only on objects of the correct type.

Python/ceval.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5254,11 +5254,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
52545254
assert(call_shape.kwnames == NULL);
52555255
int is_meth = is_method(stack_pointer, oparg);
52565256
int total_args = oparg + is_meth;
5257-
PyObject *callable = PEEK(total_args + 1);
5257+
PyMethodDescrObject *callable =
5258+
(PyMethodDescrObject *)PEEK(total_args + 1);
52585259
DEOPT_IF(total_args != 2, PRECALL);
52595260
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
5260-
PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
5261+
PyMethodDef *meth = callable->d_method;
52615262
DEOPT_IF(meth->ml_flags != METH_O, PRECALL);
5263+
PyObject *arg = TOP();
5264+
PyObject *self = SECOND();
5265+
DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
52625266
STAT_INC(PRECALL, hit);
52635267
SKIP_CALL();
52645268
PyCFunction cfunc = meth->ml_meth;
@@ -5267,8 +5271,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
52675271
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
52685272
goto error;
52695273
}
5270-
PyObject *arg = TOP();
5271-
PyObject *self = SECOND();
52725274
PyObject *res = cfunc(self, arg);
52735275
_Py_LeaveRecursiveCall(tstate);
52745276
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -5287,17 +5289,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
52875289
TARGET(PRECALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
52885290
int is_meth = is_method(stack_pointer, oparg);
52895291
int total_args = oparg + is_meth;
5290-
PyObject *callable = PEEK(total_args + 1);
5292+
PyMethodDescrObject *callable =
5293+
(PyMethodDescrObject *)PEEK(total_args + 1);
52915294
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
5292-
PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
5295+
PyMethodDef *meth = callable->d_method;
52935296
DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), PRECALL);
5297+
PyTypeObject *d_type = callable->d_common.d_type;
5298+
PyObject *self = PEEK(total_args);
5299+
DEOPT_IF(!Py_IS_TYPE(self, d_type), PRECALL);
52945300
STAT_INC(PRECALL, hit);
52955301
SKIP_CALL();
52965302
int nargs = total_args-1;
52975303
STACK_SHRINK(nargs);
5298-
_PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
5299-
PyObject *self = TOP();
5300-
PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), call_shape.kwnames);
5304+
_PyCFunctionFastWithKeywords cfunc =
5305+
(_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
5306+
PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
5307+
call_shape.kwnames);
53015308
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
53025309
call_shape.kwnames = NULL;
53035310

@@ -5322,9 +5329,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
53225329
int is_meth = is_method(stack_pointer, oparg);
53235330
int total_args = oparg + is_meth;
53245331
DEOPT_IF(total_args != 1, PRECALL);
5325-
PyObject *callable = SECOND();
5332+
PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND();
53265333
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
5327-
PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
5334+
PyMethodDef *meth = callable->d_method;
5335+
PyObject *self = TOP();
5336+
DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
53285337
DEOPT_IF(meth->ml_flags != METH_NOARGS, PRECALL);
53295338
STAT_INC(PRECALL, hit);
53305339
SKIP_CALL();
@@ -5334,7 +5343,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
53345343
if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
53355344
goto error;
53365345
}
5337-
PyObject *self = TOP();
53385346
PyObject *res = cfunc(self, NULL);
53395347
_Py_LeaveRecursiveCall(tstate);
53405348
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -5353,17 +5361,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
53535361
assert(call_shape.kwnames == NULL);
53545362
int is_meth = is_method(stack_pointer, oparg);
53555363
int total_args = oparg + is_meth;
5356-
PyObject *callable = PEEK(total_args + 1);
5364+
PyMethodDescrObject *callable =
5365+
(PyMethodDescrObject *)PEEK(total_args + 1);
53575366
/* Builtin METH_FASTCALL methods, without keywords */
53585367
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), PRECALL);
5359-
PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
5368+
PyMethodDef *meth = callable->d_method;
53605369
DEOPT_IF(meth->ml_flags != METH_FASTCALL, PRECALL);
5370+
PyObject *self = PEEK(total_args);
5371+
DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), PRECALL);
53615372
STAT_INC(PRECALL, hit);
53625373
SKIP_CALL();
5363-
_PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
5374+
_PyCFunctionFast cfunc =
5375+
(_PyCFunctionFast)(void(*)(void))meth->ml_meth;
53645376
int nargs = total_args-1;
53655377
STACK_SHRINK(nargs);
5366-
PyObject *self = TOP();
53675378
PyObject *res = cfunc(self, stack_pointer, nargs);
53685379
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
53695380
/* Clear the stack of the arguments. */

0 commit comments

Comments
 (0)