From e160e5ce22285365507aec70d21787073aa258d7 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 21 Feb 2026 09:48:12 +0000 Subject: [PATCH 1/3] Add input validation to `_PyImport_LazyImportModuleLevelObject` --- Lib/test/test_import/test_lazy_imports.py | 9 +++++++++ .../2026-02-21-09-47-45.gh-issue-145058.e-RBw-.rst | 2 ++ Python/import.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-02-21-09-47-45.gh-issue-145058.e-RBw-.rst diff --git a/Lib/test/test_import/test_lazy_imports.py b/Lib/test/test_import/test_lazy_imports.py index a4c9c14ae2b5f8..6d2791e654b388 100644 --- a/Lib/test/test_import/test_lazy_imports.py +++ b/Lib/test/test_import/test_lazy_imports.py @@ -393,6 +393,15 @@ def test_dunder_lazy_import_used(self): import test.test_import.data.lazy_imports.dunder_lazy_import_used self.assertIn("test.test_import.data.lazy_imports.basic2", sys.modules) + def test_dunder_lazy_import_non_string_name(self): + """__lazy_import__ should reject non-string name arguments.""" + with self.assertRaises(TypeError): + __lazy_import__(b"") + with self.assertRaises(TypeError): + __lazy_import__(123) + with self.assertRaises(TypeError): + __lazy_import__(None) + def test_dunder_lazy_import_builtins(self): """__lazy_import__ should use module's __builtins__ for __import__.""" from test.test_import.data.lazy_imports import dunder_lazy_import_builtins diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-21-09-47-45.gh-issue-145058.e-RBw-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-21-09-47-45.gh-issue-145058.e-RBw-.rst new file mode 100644 index 00000000000000..05eb296f96ec6d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-21-09-47-45.gh-issue-145058.e-RBw-.rst @@ -0,0 +1,2 @@ +Fix a crash when :func:`!__lazy_import__` is passed a non-string argument, +by raising an :exc:`TypeError` instead. diff --git a/Python/import.c b/Python/import.c index c20c55727d2f94..1bef9aaa2ae08c 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4468,6 +4468,20 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) { + if (name == NULL) { + _PyErr_SetString(tstate, PyExc_ValueError, "Empty module name"); + return NULL; + } + if (!PyUnicode_Check(name)) { + _PyErr_SetString(tstate, PyExc_TypeError, + "module name must be a string"); + return NULL; + } + if (level < 0) { + _PyErr_SetString(tstate, PyExc_ValueError, "level must be >= 0"); + return NULL; + } + PyObject *abs_name = get_abs_name(tstate, name, globals, level); if (abs_name == NULL) { return NULL; From ea28d2bc1a82e9b5732e00b2fa693cb3a319ed69 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 21 Feb 2026 10:26:53 +0000 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Victor Stinner --- Lib/test/test_import/test_lazy_imports.py | 16 ++++++++-------- Python/import.c | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_import/test_lazy_imports.py b/Lib/test/test_import/test_lazy_imports.py index 6d2791e654b388..dc185c070acc62 100644 --- a/Lib/test/test_import/test_lazy_imports.py +++ b/Lib/test/test_import/test_lazy_imports.py @@ -393,14 +393,14 @@ def test_dunder_lazy_import_used(self): import test.test_import.data.lazy_imports.dunder_lazy_import_used self.assertIn("test.test_import.data.lazy_imports.basic2", sys.modules) - def test_dunder_lazy_import_non_string_name(self): - """__lazy_import__ should reject non-string name arguments.""" - with self.assertRaises(TypeError): - __lazy_import__(b"") - with self.assertRaises(TypeError): - __lazy_import__(123) - with self.assertRaises(TypeError): - __lazy_import__(None) + def test_dunder_lazy_import_invalid_arguments(self): + """__lazy_import__ should reject invalid arguments.""" + for invalid_name in (b"", 123, None): + with self.assertRaises(TypeError): + __lazy_import__(invalid_name) + + with self.assertRaises(ValueError): + __lazy_import__("sys", level=-1) def test_dunder_lazy_import_builtins(self): """__lazy_import__ should use module's __builtins__ for __import__.""" diff --git a/Python/import.c b/Python/import.c index 1bef9aaa2ae08c..19ad2d8c0356c0 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4473,8 +4473,8 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, return NULL; } if (!PyUnicode_Check(name)) { - _PyErr_SetString(tstate, PyExc_TypeError, - "module name must be a string"); + _PyErr_Format(tstate, PyExc_TypeError, + "module name must be a string, got %T", name); return NULL; } if (level < 0) { From 5b01fb51fe6c52c32f898489c0256db084ec035c Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 21 Feb 2026 10:27:50 +0000 Subject: [PATCH 3/3] Apply suggestion from @vstinner Co-authored-by: Victor Stinner --- Python/import.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Python/import.c b/Python/import.c index 19ad2d8c0356c0..4c234a4a70437c 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4468,10 +4468,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) { - if (name == NULL) { - _PyErr_SetString(tstate, PyExc_ValueError, "Empty module name"); - return NULL; - } + assert(name != NULL); if (!PyUnicode_Check(name)) { _PyErr_Format(tstate, PyExc_TypeError, "module name must be a string, got %T", name);