Skip to content

Commit 3d3e7b0

Browse files
authored
[3.12] gh-128632: fix segfault on nested __classdict__ type param (GH… (#132090)
(cherry picked from commit 891c61c) Co-authored-by: Tomasz Pytel <[email protected]>
1 parent 05213af commit 3d3e7b0

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

Lib/test/test_syntax.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,25 @@ def test_continuation_bad_indentation(self):
22442244

22452245
self.assertRaises(IndentationError, exec, code)
22462246

2247+
@support.cpython_only
2248+
def test_disallowed_type_param_names(self):
2249+
# See gh-128632
2250+
2251+
self._check_error(f"class A[__classdict__]: pass",
2252+
f"reserved name '__classdict__' cannot be used for type parameter")
2253+
self._check_error(f"def f[__classdict__](): pass",
2254+
f"reserved name '__classdict__' cannot be used for type parameter")
2255+
self._check_error(f"type T[__classdict__] = tuple[__classdict__]",
2256+
f"reserved name '__classdict__' cannot be used for type parameter")
2257+
2258+
# These compilations are here to make sure __class__, __classcell__ and __classdictcell__
2259+
# don't break in the future like __classdict__ did in this case.
2260+
for name in ('__class__', '__classcell__', '__classdictcell__'):
2261+
compile(f"""
2262+
class A:
2263+
class B[{name}]: pass
2264+
""", "<testcase>", mode="exec")
2265+
22472266
@support.cpython_only
22482267
def test_nested_named_except_blocks(self):
22492268
code = ""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Disallow ``__classdict__`` as the name of a type parameter. Using this
2+
name would previously crash the interpreter in some circumstances.

Python/symtable.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,24 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
21912191
VISIT_QUIT(st, 1);
21922192
}
21932193

2194+
static int
2195+
symtable_visit_type_param_check_reserved_name(struct symtable *st, type_param_ty tp, identifier name)
2196+
{
2197+
if (_PyUnicode_Equal(name, &_Py_ID(__classdict__))) {
2198+
PyObject *error_msg = PyUnicode_FromFormat("reserved name '%U' cannot be "
2199+
"used for type parameter", name);
2200+
PyErr_SetObject(PyExc_SyntaxError, error_msg);
2201+
Py_DECREF(error_msg);
2202+
PyErr_RangedSyntaxLocationObject(st->st_filename,
2203+
tp->lineno,
2204+
tp->col_offset + 1,
2205+
tp->end_lineno,
2206+
tp->end_col_offset + 1);
2207+
return 0;
2208+
}
2209+
return 1;
2210+
}
2211+
21942212
static int
21952213
symtable_visit_type_param(struct symtable *st, type_param_ty tp)
21962214
{
@@ -2201,6 +2219,8 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
22012219
}
22022220
switch(tp->kind) {
22032221
case TypeVar_kind:
2222+
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.TypeVar.name))
2223+
VISIT_QUIT(st, 0);
22042224
if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
22052225
VISIT_QUIT(st, 0);
22062226
if (tp->v.TypeVar.bound) {
@@ -2219,10 +2239,14 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
22192239
}
22202240
break;
22212241
case TypeVarTuple_kind:
2242+
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.TypeVarTuple.name))
2243+
VISIT_QUIT(st, 0);
22222244
if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
22232245
VISIT_QUIT(st, 0);
22242246
break;
22252247
case ParamSpec_kind:
2248+
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.ParamSpec.name))
2249+
VISIT_QUIT(st, 0);
22262250
if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
22272251
VISIT_QUIT(st, 0);
22282252
break;

0 commit comments

Comments
 (0)