Crash report
What happened?
Computing __parameters__ of a generic alias whose argument is a self-referential (or merely very deeply nested) list/tuple crashes the interpreter with a C-stack overflow. _Py_make_parameters (Objects/genericaliasobject.c) recurses into nested list/tuple args without any recursion guard (Py_EnterRecursiveCall), so a cyclic argument recurses forever.
Minimal reproducer:
L = []
L.append(L) # L = [L], self-referential
list[L].__parameters__ # SIGSEGV
list[L] on its own is fine — __parameters__ is computed lazily, and the crash is in that computation (anything that triggers it — using the alias as a base class, typing.get_args(), etc. — crashes the same way).
A non-cyclic but deeply nested list crashes it too, which shows the path is simply unguarded rather than something cycle-specific:
x = [0]
for _ in range(500_000):
x = [x]
list[x].__parameters__ # SIGSEGV (C stack overflow)
Backtrace (debug build); the interpreter dies in _Py_make_parameters self-recursion at genericaliasobject.c:231:
#0 gc_alloc Python/gc.c:2013 <- SIGSEGV at the stack guard page
#1 _PyObject_GC_NewVar Python/gc.c:2055
#2 tuple_alloc Objects/tupleobject.c:57
#3 PyTuple_FromArray Objects/tupleobject.c:442
#4 PyList_AsTuple Objects/listobject.c:3269
#5 _Py_make_parameters Objects/genericaliasobject.c:192
#6 _Py_make_parameters Objects/genericaliasobject.c:231 <- unguarded recursion
#7 _Py_make_parameters Objects/genericaliasobject.c:231
... (repeats until the C stack overflows) ...
Analysis
_Py_make_parameters collects type parameters from an alias's arguments. For an argument that is a list/tuple with no __parameters__, it recurses into it:
if (!subparams && (PyTuple_Check(t) || PyList_Check(t))) {
// Recursively call _Py_make_parameters for lists/tuples and
// add the results to the current parameters.
subparams = _Py_make_parameters(t); // Objects/genericaliasobject.c:231
...
}
There is no Py_EnterRecursiveCall / Py_LeaveRecursiveCall in this function — nor anywhere in genericaliasobject.c — so a self-referential argument recurses without bound and overflows the C stack (SIGSEGV) instead of raising RecursionError.
Fix direction: wrap the recursive descent in Py_EnterRecursiveCall(...) / Py_LeaveRecursiveCall(), as other recursive C paths do, so the overflow becomes a catchable RecursionError.
Not free-threading- or JIT-specific: reproduces on GIL and free-threaded, release and debug builds.
(Found via fuzzing with fusil; the same reproducer also crashes RustPython, whose make_parameters_from_slice has the identical unguarded recursion. Reduced and reviewed by hand; draft prepared with Claude Code.)
CPython versions tested on:
3.14, CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main:a1d580430c8, Jul 18 2026, 20:16:15) [Clang 21.1.8 (6ubuntu1)]
Linked PRs
Crash report
What happened?
Computing
__parameters__of a generic alias whose argument is a self-referential (or merely very deeply nested)list/tuplecrashes the interpreter with a C-stack overflow._Py_make_parameters(Objects/genericaliasobject.c) recurses into nestedlist/tupleargs without any recursion guard (Py_EnterRecursiveCall), so a cyclic argument recurses forever.Minimal reproducer:
list[L]on its own is fine —__parameters__is computed lazily, and the crash is in that computation (anything that triggers it — using the alias as a base class,typing.get_args(), etc. — crashes the same way).A non-cyclic but deeply nested list crashes it too, which shows the path is simply unguarded rather than something cycle-specific:
Backtrace (debug build); the interpreter dies in
_Py_make_parametersself-recursion atgenericaliasobject.c:231:Analysis
_Py_make_parameterscollects type parameters from an alias's arguments. For an argument that is alist/tuplewith no__parameters__, it recurses into it:There is no
Py_EnterRecursiveCall/Py_LeaveRecursiveCallin this function — nor anywhere ingenericaliasobject.c— so a self-referential argument recurses without bound and overflows the C stack (SIGSEGV) instead of raisingRecursionError.Fix direction: wrap the recursive descent in
Py_EnterRecursiveCall(...)/Py_LeaveRecursiveCall(), as other recursive C paths do, so the overflow becomes a catchableRecursionError.Not free-threading- or JIT-specific: reproduces on GIL and free-threaded, release and debug builds.
(Found via fuzzing with fusil; the same reproducer also crashes RustPython, whose
make_parameters_from_slicehas the identical unguarded recursion. Reduced and reviewed by hand; draft prepared with Claude Code.)CPython versions tested on:
3.14, CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main:a1d580430c8, Jul 18 2026, 20:16:15) [Clang 21.1.8 (6ubuntu1)]
Linked PRs
__parameters__inGenericAlias#154277__parameters__inGenericAlias(GH-154277) #154306__parameters__inGenericAlias(GH-154277) #154339__parameters__inGenericAlias(GH-154277) #154340_Py_make_parameters#154341_Py_make_parameters(GH-154341) #154342_Py_make_parameters(GH-154341) #154343