Skip to content

Commit 75c974f

Browse files
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.
1 parent b72947a commit 75c974f

File tree

4 files changed

+50
-16
lines changed

4 files changed

+50
-16
lines changed

Lib/test/test_capi/check_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def import_singlephase():
1212
try:
1313
import _testsinglephase
1414
except ImportError:
15-
sys.modules.pop('_testsinglephase')
15+
sys.modules.pop('_testsinglephase', None)
1616
return False
1717
else:
1818
del sys.modules['_testsinglephase']

Lib/test/test_import/__init__.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ def require_frozen(module, *, skip=True):
9797
def require_pure_python(module, *, skip=False):
9898
_require_loader(module, SourceFileLoader, skip)
9999

100-
101100
def remove_files(name):
102101
for f in (name + ".py",
103102
name + ".pyc",
@@ -147,19 +146,34 @@ def _ready_to_import(name=None, source=""):
147146
del sys.modules[name]
148147

149148

150-
def requires_subinterpreters(meth):
151-
"""Decorator to skip a test if subinterpreters are not supported."""
152-
return unittest.skipIf(_interpreters is None,
153-
'subinterpreters required')(meth)
149+
if _testsinglephase is not None:
150+
def restore__testsinglephase(*, _orig=_testsinglephase):
151+
# We started with the module imported and want to restore
152+
# it to its nominal state.
153+
_orig._clear_globals()
154+
_testinternalcapi.clear_extension('_testsinglephase', _orig.__file__)
155+
import _testsinglephase
154156

155157

156158
def requires_singlephase_init(meth):
157159
"""Decorator to skip if single-phase init modules are not supported."""
160+
if not isinstance(meth, type):
161+
def meth(self, _meth=meth):
162+
try:
163+
return _meth(self)
164+
finally:
165+
restore__testsinglephase()
158166
meth = cpython_only(meth)
159167
return unittest.skipIf(_testsinglephase is None,
160168
'test requires _testsinglephase module')(meth)
161169

162170

171+
def requires_subinterpreters(meth):
172+
"""Decorator to skip a test if subinterpreters are not supported."""
173+
return unittest.skipIf(_interpreters is None,
174+
'subinterpreters required')(meth)
175+
176+
163177
class ModuleSnapshot(types.SimpleNamespace):
164178
"""A representation of a module for testing.
165179
@@ -1962,6 +1976,20 @@ def test_isolated_config(self):
19621976
with self.subTest(f'{module}: strict, fresh'):
19631977
self.check_compatible_fresh(module, strict=True, isolated=True)
19641978

1979+
@requires_subinterpreters
1980+
@requires_singlephase_init
1981+
def test_disallowed_reimport(self):
1982+
# See https://github.com/python/cpython/issues/104621.
1983+
script = textwrap.dedent('''
1984+
import _testsinglephase
1985+
print(_testsinglephase)
1986+
''')
1987+
interpid = _interpreters.create()
1988+
with self.assertRaises(_interpreters.RunFailedError):
1989+
_interpreters.run_string(interpid, script)
1990+
with self.assertRaises(_interpreters.RunFailedError):
1991+
_interpreters.run_string(interpid, script)
1992+
19651993

19661994
class TestSinglePhaseSnapshot(ModuleSnapshot):
19671995

@@ -2017,6 +2045,10 @@ def setUpClass(cls):
20172045
# Start fresh.
20182046
cls.clean_up()
20192047

2048+
@classmethod
2049+
def tearDownClass(cls):
2050+
restore__testsinglephase()
2051+
20202052
def tearDown(self):
20212053
# Clean up the module.
20222054
self.clean_up()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unsupported modules now always fail to be imported.

Python/import.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,15 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
12151215
return NULL;
12161216
}
12171217

1218+
/* It may have been successfully imported previously
1219+
in an interpreter that allows legacy modules
1220+
but is not allowed in the current interpreter. */
1221+
const char *name_buf = PyUnicode_AsUTF8(name);
1222+
assert(name_buf != NULL);
1223+
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
1224+
return NULL;
1225+
}
1226+
12181227
PyObject *mod, *mdict;
12191228
PyObject *modules = MODULES(tstate->interp);
12201229

@@ -3704,16 +3713,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
37043713

37053714
PyThreadState *tstate = _PyThreadState_GET();
37063715
mod = import_find_extension(tstate, name, path);
3707-
if (mod != NULL) {
3708-
const char *name_buf = PyUnicode_AsUTF8(name);
3709-
assert(name_buf != NULL);
3710-
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
3711-
Py_DECREF(mod);
3712-
mod = NULL;
3713-
}
3714-
goto finally;
3715-
}
3716-
else if (PyErr_Occurred()) {
3716+
if (mod != NULL || _PyErr_Occurred(tstate)) {
3717+
assert(mod == NULL || !_PyErr_Occurred(tstate));
37173718
goto finally;
37183719
}
37193720

0 commit comments

Comments
 (0)