diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2023-07-27 21:51:34 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-27 21:51:34 (GMT) |
commit | abaf89d908304891c99c6a0450cf7e160c17cdd3 (patch) | |
tree | c05549d0e395f57e1a53eb47689b3e7d4ce0bb58 | |
parent | 5daf19d763826a977a596b6fbc035ee03c0deafc (diff) | |
download | cpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.zip cpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.tar.gz cpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.tar.bz2 |
[3.12] gh-104621: Check for Incompatible Extensions in import_find_extension() (gh-107184) (gh-107360)
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>
-rw-r--r-- | Lib/test/test_capi/check_config.py | 2 | ||||
-rw-r--r-- | Lib/test/test_import/__init__.py | 42 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst | 1 | ||||
-rw-r--r-- | Python/import.c | 21 |
4 files changed, 50 insertions, 16 deletions
diff --git a/Lib/test/test_capi/check_config.py b/Lib/test/test_capi/check_config.py index aaedd82..eb99ae1 100644 --- a/Lib/test/test_capi/check_config.py +++ b/Lib/test/test_capi/check_config.py @@ -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'] diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index ee9fe4d..cc6e8d4 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -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 index 0000000..86c9762 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst @@ -0,0 +1 @@ +Unsupported modules now always fail to be imported. diff --git a/Python/import.c b/Python/import.c index d10c5ce..a93a645 100644 --- a/Python/import.c +++ b/Python/import.c @@ -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; } |