https://github.com/python/cpython/commit/891c61c1fa480928dd60cce8bbc8764630c95025 commit: 891c61c1fa480928dd60cce8bbc8764630c95025 branch: main author: Tomasz Pytel <tompy...@gmail.com> committer: JelleZijlstra <jelle.zijls...@gmail.com> 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 -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com