mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
[3.13] gh-128632: fix segfault on nested __classdict__ type param (GH-128744) (#132085)
(cherry picked from commit 891c61c1fa
)
Co-authored-by: Tomasz Pytel <tompytel@gmail.com>
This commit is contained in:
parent
d8986b7f5e
commit
900dc2b034
4 changed files with 53 additions and 13 deletions
|
@ -2470,6 +2470,25 @@ if x:
|
|||
|
||||
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 = ""
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Disallow ``__classdict__`` as the name of a type parameter. Using this
|
||||
name would previously crash the interpreter in some circumstances.
|
|
@ -503,7 +503,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);
|
||||
|
@ -514,14 +514,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;
|
||||
|
@ -533,6 +533,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;
|
||||
|
|
|
@ -2292,12 +2292,27 @@ 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);
|
||||
PyErr_RangedSyntaxLocationObject(st->st_filename,
|
||||
tp->lineno,
|
||||
tp->col_offset + 1,
|
||||
tp->end_lineno,
|
||||
tp->end_col_offset + 1);
|
||||
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;
|
||||
}
|
||||
|
||||
st->st_cur->ste_can_see_class_scope = is_in_class;
|
||||
if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) {
|
||||
|
@ -2341,12 +2356,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)) {
|
||||
VISIT_QUIT(st, 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")) {
|
||||
VISIT_QUIT(st, 0);
|
||||
}
|
||||
break;
|
||||
|
@ -2356,7 +2371,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")) {
|
||||
VISIT_QUIT(st, 0);
|
||||
}
|
||||
break;
|
||||
|
@ -2366,7 +2381,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")) {
|
||||
VISIT_QUIT(st, 0);
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue