Skip to content

Commit 17f9941

Browse files
gh-109118: Fix runtime crash when NameError happens in PEP 695 function (#109123)
1 parent e9e2ca7 commit 17f9941

File tree

7 files changed

+189
-106
lines changed

7 files changed

+189
-106
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 38 additions & 52 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_type_params.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,3 +956,43 @@ class NewStyle[T]:
956956
for case in cases:
957957
with self.subTest(case=case):
958958
weakref.ref(case)
959+
960+
961+
class TypeParamsRuntimeTest(unittest.TestCase):
962+
def test_name_error(self):
963+
# gh-109118: This crashed the interpreter due to a refcounting bug
964+
code = """
965+
class name_2[name_5]:
966+
class name_4[name_5](name_0):
967+
pass
968+
"""
969+
with self.assertRaises(NameError):
970+
run_code(code)
971+
972+
# Crashed with a slightly different stack trace
973+
code = """
974+
class name_2[name_5]:
975+
class name_4[name_5: name_5](name_0):
976+
pass
977+
"""
978+
with self.assertRaises(NameError):
979+
run_code(code)
980+
981+
def test_broken_class_namespace(self):
982+
code = """
983+
class WeirdMapping(dict):
984+
def __missing__(self, key):
985+
if key == "T":
986+
raise RuntimeError
987+
raise KeyError(key)
988+
989+
class Meta(type):
990+
def __prepare__(name, bases):
991+
return WeirdMapping()
992+
993+
class MyClass[V](metaclass=Meta):
994+
class Inner[U](T):
995+
pass
996+
"""
997+
with self.assertRaises(RuntimeError):
998+
run_code(code)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix interpreter crash when a NameError is raised inside the type parameters
2+
of a generic class.

Python/abstract_interp_cases.c.h

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,7 +1286,7 @@ dummy_func(
12861286
}
12871287
}
12881288

1289-
op(_LOAD_LOCALS, ( -- locals)) {
1289+
inst(LOAD_LOCALS, ( -- locals)) {
12901290
locals = LOCALS();
12911291
if (locals == NULL) {
12921292
_PyErr_SetString(tstate, PyExc_SystemError,
@@ -1296,15 +1296,11 @@ dummy_func(
12961296
Py_INCREF(locals);
12971297
}
12981298

1299-
macro(LOAD_LOCALS) = _LOAD_LOCALS;
1300-
1301-
op(_LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
1299+
inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) {
13021300
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
13031301
if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) {
1304-
Py_DECREF(mod_or_class_dict);
13051302
goto error;
13061303
}
1307-
Py_DECREF(mod_or_class_dict);
13081304
if (v == NULL) {
13091305
v = PyDict_GetItemWithError(GLOBALS(), name);
13101306
if (v != NULL) {
@@ -1325,11 +1321,41 @@ dummy_func(
13251321
}
13261322
}
13271323
}
1324+
DECREF_INPUTS();
13281325
}
13291326

1330-
macro(LOAD_NAME) = _LOAD_LOCALS + _LOAD_FROM_DICT_OR_GLOBALS;
1331-
1332-
macro(LOAD_FROM_DICT_OR_GLOBALS) = _LOAD_FROM_DICT_OR_GLOBALS;
1327+
inst(LOAD_NAME, (-- v)) {
1328+
PyObject *mod_or_class_dict = LOCALS();
1329+
if (mod_or_class_dict == NULL) {
1330+
_PyErr_SetString(tstate, PyExc_SystemError,
1331+
"no locals found");
1332+
ERROR_IF(true, error);
1333+
}
1334+
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
1335+
if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) {
1336+
goto error;
1337+
}
1338+
if (v == NULL) {
1339+
v = PyDict_GetItemWithError(GLOBALS(), name);
1340+
if (v != NULL) {
1341+
Py_INCREF(v);
1342+
}
1343+
else if (_PyErr_Occurred(tstate)) {
1344+
goto error;
1345+
}
1346+
else {
1347+
if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) {
1348+
goto error;
1349+
}
1350+
if (v == NULL) {
1351+
_PyEval_FormatExcCheckArg(
1352+
tstate, PyExc_NameError,
1353+
NAME_ERROR_MSG, name);
1354+
goto error;
1355+
}
1356+
}
1357+
}
1358+
}
13331359

13341360
family(LOAD_GLOBAL, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = {
13351361
LOAD_GLOBAL_MODULE,

Python/executor_cases.c.h

Lines changed: 39 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)