https://github.com/python/cpython/commit/891c61c1fa480928dd60cce8bbc8764630c95025
commit: 891c61c1fa480928dd60cce8bbc8764630c95025
branch: main
author: Tomasz Pytel <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2025-04-04T06:23:35-07:00
summary:
gh-128632: fix segfault on nested __classdict__ type param (#128744)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-20-11-28.gh-issue-128632.ryhnKs.rst
M Lib/test/test_syntax.py
M Python/assemble.c
M Python/symtable.c
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 02d10c1961e28d..c5408b37fe5629 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -2690,6 +2690,25 @@ def test_continuation_bad_indentation(self):
self.assertRaises(IndentationError, exec, code)
+ @support.cpython_only
+ def test_disallowed_type_param_names(self):
+ # See gh-128632
+
+ self._check_error(f"class A[__classdict__]: pass",
+ f"reserved name '__classdict__' cannot be used for
type parameter")
+ self._check_error(f"def f[__classdict__](): pass",
+ f"reserved name '__classdict__' cannot be used for
type parameter")
+ self._check_error(f"type T[__classdict__] = tuple[__classdict__]",
+ f"reserved name '__classdict__' cannot be used for
type parameter")
+
+ # These compilations are here to make sure __class__, __classcell__
and __classdictcell__
+ # don't break in the future like __classdict__ did in this case.
+ for name in ('__class__', '__classcell__', '__classdictcell__'):
+ compile(f"""
+class A:
+ class B[{name}]: pass
+ """, "<testcase>", mode="exec")
+
@support.cpython_only
def test_nested_named_except_blocks(self):
code = ""
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-20-11-28.gh-issue-128632.ryhnKs.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-20-11-28.gh-issue-128632.ryhnKs.rst
new file mode 100644
index 00000000000000..8cb23fc2d9e78e
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-20-11-28.gh-issue-128632.ryhnKs.rst
@@ -0,0 +1,2 @@
+Disallow ``__classdict__`` as the name of a type parameter. Using this
+name would previously crash the interpreter in some circumstances.
diff --git a/Python/assemble.c b/Python/assemble.c
index 6fdc0c86c0fe39..5efaf0d84a1f31 100644
--- a/Python/assemble.c
+++ b/Python/assemble.c
@@ -516,7 +516,7 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd,
int nlocalsplus,
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
// This counter mirrors the fix done in fix_cell_offsets().
- int numdropped = 0;
+ int numdropped = 0, cellvar_offset = -1;
pos = 0;
while (PyDict_Next(umd->u_cellvars, &pos, &k, &v)) {
int has_name = PyDict_Contains(umd->u_varnames, k);
@@ -527,14 +527,14 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd,
int nlocalsplus,
continue;
}
- int offset = PyLong_AsInt(v);
- if (offset == -1 && PyErr_Occurred()) {
+ cellvar_offset = PyLong_AsInt(v);
+ if (cellvar_offset == -1 && PyErr_Occurred()) {
return ERROR;
}
- assert(offset >= 0);
- offset += nlocals - numdropped;
- assert(offset < nlocalsplus);
- _Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds);
+ assert(cellvar_offset >= 0);
+ cellvar_offset += nlocals - numdropped;
+ assert(cellvar_offset < nlocalsplus);
+ _Py_set_localsplus_info(cellvar_offset, k, CO_FAST_CELL, names, kinds);
}
pos = 0;
@@ -546,6 +546,10 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd,
int nlocalsplus,
assert(offset >= 0);
offset += nlocals - numdropped;
assert(offset < nlocalsplus);
+ /* XXX If the assertion below fails it is most likely because a freevar
+ was added to u_freevars with the wrong index due to not taking into
+ account cellvars already present, see gh-128632. */
+ assert(offset > cellvar_offset);
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
}
return SUCCESS;
diff --git a/Python/symtable.c b/Python/symtable.c
index 748bd447dab0ad..35c9a0e295c60c 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -2569,11 +2569,21 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
static int
symtable_visit_type_param_bound_or_default(
struct symtable *st, expr_ty e, identifier name,
- void *key, const char *ste_scope_info)
+ type_param_ty tp, const char *ste_scope_info)
{
+ if (_PyUnicode_Equal(name, &_Py_ID(__classdict__))) {
+
+ PyObject *error_msg = PyUnicode_FromFormat("reserved name '%U' cannot
be "
+ "used for type parameter",
name);
+ PyErr_SetObject(PyExc_SyntaxError, error_msg);
+ Py_DECREF(error_msg);
+ SET_ERROR_LOCATION(st->st_filename, LOCATION(tp));
+ return 0;
+ }
+
if (e) {
int is_in_class = st->st_cur->ste_can_see_class_scope;
- if (!symtable_enter_block(st, name, TypeVariableBlock, key,
LOCATION(e))) {
+ if (!symtable_enter_block(st, name, TypeVariableBlock, (void *)tp,
LOCATION(e))) {
return 0;
}
@@ -2615,12 +2625,12 @@ symtable_visit_type_param(struct symtable *st,
type_param_ty tp)
// The only requirement for the key is that it is unique and it
matches the logic in
// compile.c where the scope is retrieved.
if (!symtable_visit_type_param_bound_or_default(st,
tp->v.TypeVar.bound, tp->v.TypeVar.name,
- (void *)tp,
ste_scope_info)) {
+ tp, ste_scope_info)) {
return 0;
}
if (!symtable_visit_type_param_bound_or_default(st,
tp->v.TypeVar.default_value, tp->v.TypeVar.name,
- (void *)((uintptr_t)tp
+ 1), "a TypeVar default")) {
+
(type_param_ty)((uintptr_t)tp + 1), "a TypeVar default")) {
return 0;
}
break;
@@ -2630,7 +2640,7 @@ symtable_visit_type_param(struct symtable *st,
type_param_ty tp)
}
if (!symtable_visit_type_param_bound_or_default(st,
tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name,
- (void *)tp, "a
TypeVarTuple default")) {
+ tp, "a TypeVarTuple
default")) {
return 0;
}
break;
@@ -2640,7 +2650,7 @@ symtable_visit_type_param(struct symtable *st,
type_param_ty tp)
}
if (!symtable_visit_type_param_bound_or_default(st,
tp->v.ParamSpec.default_value, tp->v.ParamSpec.name,
- (void *)tp, "a
ParamSpec default")) {
+ tp, "a ParamSpec
default")) {
return 0;
}
break;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]