From 082b7f630cd7c93be26ac0182c2faed242b89dae Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 20 Jul 2026 20:16:28 +0300 Subject: [PATCH] gh-154275: Do not crash on deeply nested `__parameters__` in `GenericAlias` --- Lib/test/test_typing.py | 13 ++++++++ ...-07-20-20-16-08.gh-issue-154275.l961cA.rst | 2 ++ Objects/genericaliasobject.c | 30 +++++++++++-------- 3 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-20-20-16-08.gh-issue-154275.l961cA.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c1f340230159db6..106ffdede6fd4ef 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -52,6 +52,8 @@ from test.support import ( captured_stderr, cpython_only, requires_docstrings, import_helper, run_code, subTests, EqualToForwardRef, + exceeds_recursion_limit, skip_if_huge_c_stack, skip_wasi_stack_overflow, + skip_emscripten_stack_overflow, ) from test.typinganndata import ( ann_module695, mod_generics_cache, _typed_dict_helper, @@ -5094,6 +5096,17 @@ class MM2(collections.abc.MutableMapping, MutableMapping[str, str]): pass self.assertEqual(MM2.__bases__, (collections.abc.MutableMapping, Generic)) + @cpython_only + @skip_if_huge_c_stack() + @skip_wasi_stack_overflow() + @skip_emscripten_stack_overflow() + def test_parameters_deep_recursion(self): + x = [0] + for _ in range(exceeds_recursion_limit()): + x = [x] + with self.assertRaisesRegex(RecursionError, "in __parameter__ calculation"): + list[x].__parameters__ + def test_orig_bases(self): T = TypeVar('T') class C(typing.Dict[str, T]): ... diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-20-20-16-08.gh-issue-154275.l961cA.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-20-20-16-08.gh-issue-154275.l961cA.rst new file mode 100644 index 000000000000000..998e91c77098e75 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-20-20-16-08.gh-issue-154275.l961cA.rst @@ -0,0 +1,2 @@ +Fix a crash when getting deeply nested ``__parameters__`` from a +:class:`types.GenericAlias` objects. diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 4e85927def7ea78..5caf5c7086c87a0 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -186,6 +186,10 @@ PyObject * _Py_make_parameters(PyObject *args) { assert(PyTuple_Check(args) || PyList_Check(args)); + if (Py_EnterRecursiveCall(" in __parameter__ calculation")) { + return NULL; + } + const bool is_args_list = PyList_Check(args); PyObject *tuple_args = NULL; if (is_args_list) { @@ -210,9 +214,7 @@ _Py_make_parameters(PyObject *args) } int rc = PyObject_HasAttrWithError(t, &_Py_ID(__typing_subst__)); if (rc < 0) { - Py_DECREF(parameters); - Py_XDECREF(tuple_args); - return NULL; + goto error; } if (rc) { iparam += tuple_add(parameters, iparam, t); @@ -221,18 +223,14 @@ _Py_make_parameters(PyObject *args) PyObject *subparams; if (PyObject_GetOptionalAttr(t, &_Py_ID(__parameters__), &subparams) < 0) { - Py_DECREF(parameters); - Py_XDECREF(tuple_args); - return NULL; + goto error; } 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); if (subparams == NULL) { - Py_DECREF(parameters); - Py_XDECREF(tuple_args); - return NULL; + goto error; } } if (subparams && PyTuple_Check(subparams)) { @@ -243,7 +241,7 @@ _Py_make_parameters(PyObject *args) if (_PyTuple_Resize(¶meters, len) < 0) { Py_DECREF(subparams); Py_XDECREF(tuple_args); - return NULL; + goto cleanup; } } for (Py_ssize_t j = 0; j < len2; j++) { @@ -256,13 +254,19 @@ _Py_make_parameters(PyObject *args) } if (iparam < len) { if (_PyTuple_Resize(¶meters, iparam) < 0) { - Py_XDECREF(parameters); - Py_XDECREF(tuple_args); - return NULL; + goto error; } } Py_XDECREF(tuple_args); + Py_LeaveRecursiveCall(); return parameters; + +error: + Py_XDECREF(parameters); + Py_XDECREF(tuple_args); +cleanup: + Py_LeaveRecursiveCall(); + return NULL; } /* If obj is a generic alias, substitute type variables params