]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-144601: Avoid sharing exception objects raised in a `PyInit` function acros...
authorPeter Bierma <zintensitydev@gmail.com>
Mon, 16 Feb 2026 16:05:55 +0000 (11:05 -0500)
committerGitHub <noreply@github.com>
Mon, 16 Feb 2026 16:05:55 +0000 (16:05 +0000)
(cherry picked from commit fd6b639a49dd1143c6fd8729fc49f17b3114a965)

Lib/test/test_import/__init__.py
Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst [new file with mode: 0644]
Modules/_testsinglephase.c
Python/import.c

index 8f3fd8ec2c35f9f97aa42fc3a39dfaae540e22ea..4d2c263bebd2728b2d1759c06614cb5d81c90fbd 100644 (file)
@@ -42,6 +42,7 @@ from test.support import (
     requires_gil_enabled,
     Py_GIL_DISABLED,
     force_not_colorized_test_class,
+    catch_unraisable_exception
 )
 from test.support.import_helper import (
     forget, make_legacy_pyc, unlink, unload, ready_to_import,
@@ -2559,6 +2560,32 @@ class SubinterpImportTests(unittest.TestCase):
         excsnap = _interpreters.run_string(interpid, script)
         self.assertIsNot(excsnap, None)
 
+    @requires_subinterpreters
+    def test_pyinit_function_raises_exception(self):
+        # gh-144601: PyInit functions that raised exceptions would cause a
+        # crash when imported from a subinterpreter.
+        import _testsinglephase
+        filename = _testsinglephase.__file__
+        script = f"""if True:
+        from test.test_import import import_extension_from_file
+
+        import_extension_from_file('_testsinglephase_raise_exception', {filename!r})"""
+
+        interp = _interpreters.create()
+        try:
+            with catch_unraisable_exception() as cm:
+                exception = _interpreters.run_string(interp, script)
+                unraisable = cm.unraisable
+        finally:
+            _interpreters.destroy(interp)
+
+        self.assertIsNotNone(exception)
+        self.assertIsNotNone(exception.type.__name__, "ImportError")
+        self.assertIsNotNone(exception.msg, "failed to import from subinterpreter due to exception")
+        self.assertIsNotNone(unraisable)
+        self.assertIs(unraisable.exc_type, RuntimeError)
+        self.assertEqual(str(unraisable.exc_value), "evil")
+
 
 class TestSinglePhaseSnapshot(ModuleSnapshot):
     """A representation of a single-phase init module for testing.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst b/Misc/NEWS.d/next/Core and Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
new file mode 100644 (file)
index 0000000..1c7772e
--- /dev/null
@@ -0,0 +1,2 @@
+Fix crash when importing a module whose ``PyInit`` function raises an
+exception from a subinterpreter.
index 2c59085d15b5beed94a289206eac0df35a558957..f74b964faf35fbc3a7fd6cc59aa7aaaaf48a0fa3 100644 (file)
@@ -799,3 +799,11 @@ PyInit__testsinglephase_circular(void)
     }
     return Py_NewRef(static_module_circular);
 }
+
+
+PyMODINIT_FUNC
+PyInit__testsinglephase_raise_exception(void)
+{
+    PyErr_SetString(PyExc_RuntimeError, "evil");
+    return NULL;
+}
index eb955d194745ff1fa11ea645e9aef3968d67ad9a..f34e32c95828fdc8b3d360a0b08e9d8daa4eac55 100644 (file)
@@ -2089,13 +2089,29 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
     }
 
 main_finally:
+    if (rc < 0) {
+        _Py_ext_module_loader_result_apply_error(&res, name_buf);
+    }
+
     /* Switch back to the subinterpreter. */
     if (switched) {
+        // gh-144601: The exception object can't be transferred across
+        // interpreters. Instead, we print out an unraisable exception, and
+        // then raise a different exception for the calling interpreter.
+        if (rc < 0) {
+            assert(PyErr_Occurred());
+            PyErr_FormatUnraisable("Exception while importing from subinterpreter");
+        }
         assert(main_tstate != tstate);
         switch_back_from_main_interpreter(tstate, main_tstate, mod);
         /* Any module we got from the init function will have to be
          * reloaded in the subinterpreter. */
         mod = NULL;
+        if (rc < 0) {
+            PyErr_SetString(PyExc_ImportError,
+                            "failed to import from subinterpreter due to exception");
+            goto error;
+        }
     }
 
     /*****************************************************************/
@@ -2104,7 +2120,6 @@ main_finally:
 
     /* Finally we handle the error return from _PyImport_RunModInitFunc(). */
     if (rc < 0) {
-        _Py_ext_module_loader_result_apply_error(&res, name_buf);
         goto error;
     }