8000 gh-128632: fix segfault on nested __classdict__ type param (#128744) · python/cpython@891c61c · GitHub
[go: up one dir, main page]

Skip to content

Commit 891c61c

Browse files
authored
gh-128632: fix segfault on nested __classdict__ type param (#128744)
1 parent d1db43c commit 891c61c

File tree

4 files changed

+48
-13
lines changed

4 files changed

+48
-13
lines changed

Lib/test/test_syntax.py

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

26912691
self.assertRaises(IndentationError, exec, code)
26922692

2693+
@support.cpython_only
2694+
def test_disallowed_type_param_names(self):
2695+
# See gh-128632
2696+
2697+
self._check_error(f"class A[__classdict__]: pass",
2698+
f"reserved name '__classdict__' cannot be used for type parameter")
2699+
self._check_error(f"def f[__classdict__](): pass",
2700+
f"reserved name '__classdict__' cannot be used for type parameter")
2701+
self._check_error(f"type T[__classdict__] = tuple[__classdict__]",
2702+
f"reserved name '__classdict__' cannot be used for type parameter")
2703+
2704+
# These compilations are here to make sure __class__, __classcell__ and __classdictcell__
2705+
# don't break in the future like __classdict__ did in this case.
2706+
for name in ('__class__', '__classcell__', '__classdictcell__'):
2707+
compile(f"""
2708+
class A:
2709+
class B[{name}]: pass
2710+
""", "<testcase>", mode="exec")
2711+
26932712
@support.cpython_only
26942713
def test_nested_named_except_blocks(self):
26952714
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/assemble.c

Lines changed: 11 additions & 7 deletions
< 8000 td data-grid-cell-id="diff-b784be7a543a2653face407b541812c20b1f75c77c9ee9b109ca2220921d99d0-548-551-2" data-line-anchor="diff-b784be7a543a2653face407b541812c20b1f75c77c9ee9b109ca2220921d99d0R551" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionLine-bgColor, var(--diffBlob-addition-bgColor-line));padding-right:24px" tabindex="-1" valign="top" class="focusable-grid-cell diff-text-cell right-side-diff-cell left-side">+
account cellvars already present, see gh-128632. */
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
516516
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);
517517

518518
// This counter mirrors the fix done in fix_cell_offsets().
519-
int numdropped = 0;
519+
int numdropped = 0, cellvar_offset = -1;
520520
pos = 0;
521521
while (PyDict_Next(umd->u_cellvars, &pos, &k, &v)) {
522522
int has_name = PyDict_Contains(umd->u_varnames, k);
@@ -527,14 +527,14 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
527527
continue;
528528
}
529529

530-
int offset = PyLong_AsInt(v);
531-
if (offset == -1 && PyErr_Occurred()) {
530+
cellvar_offset = PyLong_AsInt(v);
531+
if (cellvar_offset == -1 && PyErr_Occurred()) {
532532
return ERROR;
533533
}
534-
assert(offset >= 0);
535-
offset += nlocals - numdropped;
536-
assert(offset < nlocalsplus);
537-
_Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds);
534+
assert(cellvar_offset >= 0);
535+
cellvar_offset += nlocals - numdropped;
536+
assert(cellvar_offset < nlocalsplus);
537+
_Py_set_localsplus_info(cellvar_offset, k, CO_FAST_CELL, names, kinds);
538538
}
539539

540540
pos = 0;
@@ -546,6 +546,10 @@ compute_localsplus_info(_PyCompile_CodeUnitMetadata *umd, int nlocalsplus,
546546
assert(offset >= 0);
547547
offset += nlocals - numdropped;
548548
assert(offset < nlocalsplus);
549+
/* XXX If the assertion below fails it is most likely because a freevar
550+
was added to u_freevars with the wrong index due to not taking into
551
552+
assert(offset > cellvar_offset);
549553
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
550554
}
551555
return SUCCESS;

Python/symtable.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2569,11 +2569,21 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
25692569
static int
25702570
symtable_visit_type_param_bound_or_default(
25712571
struct symtable *st, expr_ty e, identifier name,
2572-
void *key, const char *ste_scope_info)
2572+
type_param_ty tp, const char *ste_scope_info)
25732573
{
2574+
if (_PyUnicode_Equal(name, &_Py_ID(__classdict__))) {
2575+
2576+
PyObject *error_msg = PyUnicode_FromFormat("reserved name '%U' cannot be "
2577+
"used for type parameter", name);
2578+
PyErr_SetObject(PyExc_SyntaxError, error_msg);
2579+
Py_DECREF(error_msg);
2580+
SET_ERROR_LOCATION(st->st_filename, LOCATION(tp));
2581+
return 0;
2582+
}
2583+
25742584
if (e) {
25752585
int is_in_class = st->st_cur->ste_can_see_class_scope;
2576-
if (!symtable_enter_block(st, name, TypeVariableBlock, key, LOCATION(e))) {
2586+
if (!symtable_enter_block(st, name, TypeVariableBlock, (void *)tp, LOCATION(e))) {
25772587
return 0;
25782588
}
25792589

@@ -2615,12 +2625,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
26152625
// The only requirement for the key is that it is unique and it matches the logic in
26162626
// compile.c where the scope is retrieved.
26172627
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name,
2618-
(void *)tp, ste_scope_info)) {
2628+
tp, ste_scope_info)) {
26192629
return 0;
26202630
}
26212631

26222632
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name,
2623-
(void *)((uintptr_t)tp + 1), "a TypeVar default")) {
2633+
(type_param_ty)((uintptr_t)tp + 1), "a TypeVar default")) {
26242634
return 0;
26252635
}
26262636
break;
@@ -2630,7 +2640,7 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
26302640
}
26312641

26322642
if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name,
2633-
(void *)tp, "a TypeVarTuple default")) {
2643+
tp, "a TypeVarTuple default")) {
26342644
return 0;
26352645
}
26362646
break;
@@ -2640,7 +2650,7 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
26402650
}
26412651

26422652
if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name,
2643-
(void *)tp, "a ParamSpec default")) {
2653+
tp, "a ParamSpec default")) {
26442654
return 0;
26452655
}
26462656
break;

0 commit comments

Comments
 (0)
0