[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 75c974f535)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Miss Islington (bot) 2023-07-27 14:51:34 -07:00 committed by GitHub
parent 5daf19d763
commit abaf89d908
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 16 deletions

View 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']

View 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()

View file

@ -0,0 +1 @@
Unsupported modules now always fail to be imported.

View 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;
}