]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-104621: Check for Incompatible Extensions in import_find_extension() (gh...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 27 Jul 2023 21:51:34 +0000 (14:51 -0700)
committerGitHub <noreply@github.com>
Thu, 27 Jul 2023 21:51:34 +0000 (21:51 +0000)
gh-104621: Check for Incompatible Extensions in import_find_extension() (gh-107184)

This fixes a bug where incompatible modules could still be imported if attempted multiple times.
(cherry picked from commit 75c974f5353685f338344618ad7344e64c2293d0)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
Lib/test/test_capi/check_config.py
Lib/test/test_import/__init__.py
Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst [new file with mode: 0644]
Python/import.c

index aaedd82f39af50a15c277aec0fec5a220e0b799e..eb99ae16f2b69ec35c7524813cd524a0cb111977 100644 (file)
@@ -12,7 +12,7 @@ def import_singlephase():
     try:
         import _testsinglephase
     except ImportError:
-        sys.modules.pop('_testsinglephase')
+        sys.modules.pop('_testsinglephase', None)
         return False
     else:
         del sys.modules['_testsinglephase']
index ee9fe4d574dd423778cae53cbaeaef2eff2643bd..cc6e8d4e640cd649aa13d10160947e064e719cdf 100644 (file)
@@ -97,7 +97,6 @@ def require_frozen(module, *, skip=True):
 def require_pure_python(module, *, skip=False):
     _require_loader(module, SourceFileLoader, skip)
 
-
 def remove_files(name):
     for f in (name + ".py",
               name + ".pyc",
@@ -128,19 +127,34 @@ def _ready_to_import(name=None, source=""):
                 del sys.modules[name]
 
 
-def requires_subinterpreters(meth):
-    """Decorator to skip a test if subinterpreters are not supported."""
-    return unittest.skipIf(_interpreters is None,
-                           'subinterpreters required')(meth)
+if _testsinglephase is not None:
+    def restore__testsinglephase(*, _orig=_testsinglephase):
+        # We started with the module imported and want to restore
+        # it to its nominal state.
+        _orig._clear_globals()
+        _testinternalcapi.clear_extension('_testsinglephase', _orig.__file__)
+        import _testsinglephase
 
 
 def requires_singlephase_init(meth):
     """Decorator to skip if single-phase init modules are not supported."""
+    if not isinstance(meth, type):
+        def meth(self, _meth=meth):
+            try:
+                return _meth(self)
+            finally:
+                restore__testsinglephase()
     meth = cpython_only(meth)
     return unittest.skipIf(_testsinglephase is None,
                            'test requires _testsinglephase module')(meth)
 
 
+def requires_subinterpreters(meth):
+    """Decorator to skip a test if subinterpreters are not supported."""
+    return unittest.skipIf(_interpreters is None,
+                           'subinterpreters required')(meth)
+
+
 class ModuleSnapshot(types.SimpleNamespace):
     """A representation of a module for testing.
 
@@ -1943,6 +1957,20 @@ class SubinterpImportTests(unittest.TestCase):
         with self.subTest(f'{module}: strict, fresh'):
             self.check_compatible_fresh(module, strict=True, isolated=True)
 
+    @requires_subinterpreters
+    @requires_singlephase_init
+    def test_disallowed_reimport(self):
+        # See https://github.com/python/cpython/issues/104621.
+        script = textwrap.dedent('''
+            import _testsinglephase
+            print(_testsinglephase)
+            ''')
+        interpid = _interpreters.create()
+        with self.assertRaises(_interpreters.RunFailedError):
+            _interpreters.run_string(interpid, script)
+        with self.assertRaises(_interpreters.RunFailedError):
+            _interpreters.run_string(interpid, script)
+
 
 class TestSinglePhaseSnapshot(ModuleSnapshot):
 
@@ -2002,6 +2030,10 @@ class SinglephaseInitTests(unittest.TestCase):
         # Start fresh.
         cls.clean_up()
 
+    @classmethod
+    def tearDownClass(cls):
+        restore__testsinglephase()
+
     def tearDown(self):
         # Clean up the module.
         self.clean_up()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst
new file mode 100644 (file)
index 0000000..86c9762
--- /dev/null
@@ -0,0 +1 @@
+Unsupported modules now always fail to be imported.
index d10c5ce63a2c8b2aa9d92a4a4d6956e84f37b363..a93a6450285cc13850e47b5efd831b63a667f38d 100644 (file)
@@ -1222,6 +1222,15 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
         return NULL;
     }
 
+    /* It may have been successfully imported previously
+       in an interpreter that allows legacy modules
+       but is not allowed in the current interpreter. */
+    const char *name_buf = PyUnicode_AsUTF8(name);
+    assert(name_buf != NULL);
+    if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
+        return NULL;
+    }
+
     PyObject *mod, *mdict;
     PyObject *modules = MODULES(tstate->interp);
 
@@ -3712,16 +3721,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
 
     PyThreadState *tstate = _PyThreadState_GET();
     mod = import_find_extension(tstate, name, path);
-    if (mod != NULL) {
-        const char *name_buf = PyUnicode_AsUTF8(name);
-        assert(name_buf != NULL);
-        if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
-            Py_DECREF(mod);
-            mod = NULL;
-        }
-        goto finally;
-    }
-    else if (PyErr_Occurred()) {
+    if (mod != NULL || _PyErr_Occurred(tstate)) {
+        assert(mod == NULL || !_PyErr_Occurred(tstate));
         goto finally;
     }