https://github.com/python/cpython/commit/25883627946b7cbcf7286acfe2a2019382375325
commit: 25883627946b7cbcf7286acfe2a2019382375325
branch: main
author: Brittany Reynoso <[email protected]>
committer: Yhg1s <[email protected]>
date: 2026-10-14T13:51:36Z
summary:
gh-159288: Avoid segfault by adding missing recursion checks when walking lazy
import chains (#159289)
Add missing recursion checks when walking lazy import chains
`lazy import a.b.c as d` nests one placeholder per name component.
lazy_import_path() and lazy_import_replay_from() recursed over that
chain without a recursion check, so a very long dotted name could
overflow the C stack. Raise RecursionError instead, as the rest of
lazy import resolution does.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-10-13-04-03-48.gh-issue-159288.vhqBQG.rst
M Lib/test/test_lazy_import/__init__.py
M Objects/lazyimportobject.c
diff --git a/Lib/test/test_lazy_import/__init__.py
b/Lib/test/test_lazy_import/__init__.py
index f1169a2aa206bfb..31006f804fd6d51 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -308,6 +308,19 @@ def test_lazy_import_type_attribute_error_message(self):
""")
assert_python_ok("-c", code)
+ @support.run_with_limited_c_stack(depth=10_000, size=256 * 1024)
+ def test_deeply_dotted_name(self):
+ """A long dotted name must not overflow the C stack."""
+ m = types.SimpleNamespace()
+ # Every lookup in m.m.m... succeeds, so resolve() only fails if the
+ # C stack runs out.
+ m.m = m
+ ns = {"__builtins__": {"__lazy_import__": __lazy_import__,
+ "__import__": lambda *args: m}}
+ exec("lazy import " + ".".join(["m"] * 10_000) + " as x", ns)
+ with contextlib.suppress(RecursionError):
+ ns["x"].resolve()
+
class SyntaxRestrictionTests(LazyImportTestCase):
"""Tests for syntax restrictions on lazy imports."""
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-13-04-03-48.gh-issue-159288.vhqBQG.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-13-04-03-48.gh-issue-159288.vhqBQG.rst
new file mode 100644
index 000000000000000..2d969e8cbac8c4c
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-13-04-03-48.gh-issue-159288.vhqBQG.rst
@@ -0,0 +1,3 @@
+Fix a crash when walking a very long chain of lazy import placeholders, as
+created by an aliased dotted import such as ``lazy import a.b.c as d``.
+:exc:`RecursionError` is now raised instead.
diff --git a/Objects/lazyimportobject.c b/Objects/lazyimportobject.c
index bb35ddcdae1755e..aafd2ce764a1cbc 100644
--- a/Objects/lazyimportobject.c
+++ b/Objects/lazyimportobject.c
@@ -204,7 +204,11 @@ static PyObject *
lazy_import_path(PyLazyImportObject *m)
{
if (PyLazyImport_CheckExact(m->lz_from)) {
+ if (_Py_EnterRecursiveCall(" while getting the name of a lazy
import")) {
+ return NULL;
+ }
PyObject *base = lazy_import_path((PyLazyImportObject *)m->lz_from);
+ _Py_LeaveRecursiveCall();
if (base == NULL) {
return NULL;
}
@@ -277,8 +281,12 @@ lazy_import_replay_from(PyThreadState *tstate, PyObject
*mod,
if (!PyLazyImport_CheckExact(lz->lz_from)) {
return Py_NewRef(mod);
}
+ if (_Py_EnterRecursiveCallTstate(tstate, " while resolving a lazy
import")) {
+ return NULL;
+ }
PyObject *from = lazy_import_replay_from(
tstate, mod, (PyLazyImportObject *)lz->lz_from);
+ _Py_LeaveRecursiveCallTstate(tstate);
if (from == NULL) {
return NULL;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]