Skip to content

Commit c86496c

Browse files
ZeroIntensitysobolevnvstinnermarkshannon
authored andcommitted
[3.12] gh-131998: Fix NULL dereference when using an unbound method descriptor in a specialized code path (GH-132000)
(cherry picked from commit ac3c439) Co-authored-by: Peter Bierma <zintensitydev@gmail.com> Co-authored-by: sobolevn <mail@sobolevn.me> Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Mark Shannon <mark@hotpy.org>
1 parent da0b9b9 commit c86496c

4 files changed

Lines changed: 80 additions & 49 deletions

File tree

‎Lib/test/test_types.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
MISSING_C_DOCSTRINGS,
66
)
77
from test.test_import import no_rerun
8+
from test.support.script_helper import assert_python_ok
9+
from test.support.import_helper import import_fresh_module
10+
811
import collections.abc
912
from collections import namedtuple
1013
import copy
@@ -643,6 +646,24 @@ def test_traceback_and_frame_types(self):
643646
self.assertIsInstance(exc.__traceback__, types.TracebackType)
644647
self.assertIsInstance(exc.__traceback__.tb_frame, types.FrameType)
645648

649+
def test_call_unbound_crash(self):
650+
# GH-131998: The specialized instruction would get tricked into dereferencing
651+
# a bound "self" that didn't exist if subsequently called unbound.
652+
code = """if True:
653+
654+
def call(part):
655+
[] + ([] + [])
656+
part.pop()
657+
658+
for _ in range(3):
659+
call(['a'])
660+
try:
661+
call(list)
662+
except TypeError:
663+
pass
664+
"""
665+
assert_python_ok("-c", code)
666+
646667

647668
class UnionTests(unittest.TestCase):
648669

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash when using an unbound method :term:`descriptor` object in a
2+
function where a bound method descriptor was used.

‎Python/bytecodes.c‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3108,13 +3108,15 @@ dummy_func(
31083108
args--;
31093109
total_args++;
31103110
}
3111+
DEOPT_IF(total_args == 0, CALL);
31113112
PyMethodDescrObject *callable =
31123113
(PyMethodDescrObject *)PEEK(total_args + 1);
31133114
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
31143115
PyMethodDef *meth = callable->d_method;
31153116
DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL);
31163117
PyTypeObject *d_type = callable->d_common.d_type;
31173118
PyObject *self = args[0];
3119+
assert(self != NULL);
31183120
DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL);
31193121
STAT_INC(CALL, hit);
31203122
int nargs = total_args - 1;
@@ -3175,11 +3177,13 @@ dummy_func(
31753177
}
31763178
PyMethodDescrObject *callable =
31773179
(PyMethodDescrObject *)PEEK(total_args + 1);
3180+
DEOPT_IF(total_args == 0, CALL);
31783181
/* Builtin METH_FASTCALL methods, without keywords */
31793182
DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
31803183
PyMethodDef *meth = callable->d_method;
31813184
DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL);
31823185
PyObject *self = args[0];
3186+
assert(self != NULL);
31833187
DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
31843188
STAT_INC(CALL, hit);
31853189
_PyCFunctionFast cfunc =

0 commit comments

Comments
 (0)