Fix recursive lazy imports and error path in bytecodes.c

This commit is contained in:
Pablo Galindo Salgado 2025-09-23 19:20:36 +01:00 committed by Dino Viehland
parent f9880bfd5f
commit f3f5795e31
6 changed files with 18 additions and 6 deletions

View file

@ -57,6 +57,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyO
#define IMPORTS_INIT \ #define IMPORTS_INIT \
{ \ { \
DLOPENFLAGS_INIT \ DLOPENFLAGS_INIT \
.lazy_import_resolution_depth = 0, \
.find_and_load = { \ .find_and_load = { \
.header = 1, \ .header = 1, \
}, \ }, \

View file

@ -316,6 +316,8 @@ struct _import_state {
PyObject *lazy_import_func; PyObject *lazy_import_func;
int lazy_imports_mode; int lazy_imports_mode;
PyObject *lazy_imports_filter; PyObject *lazy_imports_filter;
/* Counter to prevent recursive lazy import creation */
int lazy_import_resolution_depth;
/* The global import lock. */ /* The global import lock. */
_PyRecursiveMutex lock; _PyRecursiveMutex lock;
/* diagnostic info in PyImport_ImportModuleLevelObject() */ /* diagnostic info in PyImport_ImportModuleLevelObject() */

View file

@ -1835,7 +1835,7 @@ dummy_func(
PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res); PyObject *res_o = PyStackRef_AsPyObjectBorrow(*res);
if (PyLazyImport_CheckExact(res_o)) { if (PyLazyImport_CheckExact(res_o)) {
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o); PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o);
if (PyDict_SetItem(GLOBALS(), name, l_v) < 0) { if (l_v != NULL && PyDict_SetItem(GLOBALS(), name, l_v) < 0) {
JUMP_TO_LABEL(error); JUMP_TO_LABEL(error);
} }
res_o = l_v; res_o = l_v;

View file

@ -2491,7 +2491,7 @@
_PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o); PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o);
stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer = _PyFrame_GetStackPointer(frame);
if (PyDict_SetItem(GLOBALS(), name, l_v) < 0) { if (l_v != NULL && PyDict_SetItem(GLOBALS(), name, l_v) < 0) {
JUMP_TO_LABEL(error); JUMP_TO_LABEL(error);
} }
res_o = l_v; res_o = l_v;

View file

@ -9194,7 +9194,7 @@
_PyFrame_SetStackPointer(frame, stack_pointer); _PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o); PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, res_o);
stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer = _PyFrame_GetStackPointer(frame);
if (PyDict_SetItem(GLOBALS(), name, l_v) < 0) { if (l_v != NULL && PyDict_SetItem(GLOBALS(), name, l_v) < 0) {
JUMP_TO_LABEL(error); JUMP_TO_LABEL(error);
} }
res_o = l_v; res_o = l_v;

View file

@ -3738,6 +3738,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
PyObject *globals = PyEval_GetGlobals(); PyObject *globals = PyEval_GetGlobals();
// Increment counter to prevent recursive lazy import creation
PyInterpreterState *interp = tstate->interp;
interp->imports.lazy_import_resolution_depth++;
if (full) { if (full) {
obj = _PyEval_ImportNameWithImport(tstate, obj = _PyEval_ImportNameWithImport(tstate,
lz->lz_import_func, lz->lz_import_func,
@ -3749,6 +3753,7 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
} else { } else {
PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot); PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot);
if (name == NULL) { if (name == NULL) {
interp->imports.lazy_import_resolution_depth--;
goto error; goto error;
} }
obj = _PyEval_ImportNameWithImport(tstate, obj = _PyEval_ImportNameWithImport(tstate,
@ -3761,6 +3766,9 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
Py_DECREF(name); Py_DECREF(name);
} }
// Decrement counter
interp->imports.lazy_import_resolution_depth--;
if (obj == NULL) { if (obj == NULL) {
goto error; goto error;
} }
@ -3942,7 +3950,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
goto error; goto error;
} }
if (globals != NULL && // Only check __lazy_modules__ if we're not already resolving a lazy import
if (interp->imports.lazy_import_resolution_depth == 0 && globals != NULL &&
PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) { PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) {
goto error; goto error;
} }
@ -3965,7 +3974,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
goto error; goto error;
} }
if (lazy_modules != NULL) { if (interp->imports.lazy_import_resolution_depth == 0 && lazy_modules != NULL) {
// Check and see if the module is opting in w/o syntax for backwards compatibility // Check and see if the module is opting in w/o syntax for backwards compatibility
// with older Python versions. // with older Python versions.
int contains = PySequence_Contains(lazy_modules, name); int contains = PySequence_Contains(lazy_modules, name);
@ -3988,7 +3997,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
return NULL; return NULL;
} }
final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals, final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals,
locals, fromlist, level); locals, fromlist, level);
Py_DECREF(import_func); Py_DECREF(import_func);
goto error; goto error;