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]

Reply via email to