diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 271361ae816449..fa4f5e013d4b17 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1253,6 +1253,20 @@ class Spec2: origin = "a\x00b" _imp.create_dynamic(Spec2()) + def test_create_builtin(self): + class Spec: + name = None + spec = Spec() + + with self.assertRaisesRegex(TypeError, 'name must be string, not NoneType'): + _imp.create_builtin(spec) + + spec.name = "" + + # gh-142029 + with self.assertRaisesRegex(ValueError, 'name must not be empty'): + _imp.create_builtin(spec) + def test_filter_syntax_warnings_by_module(self): module_re = r'test\.test_import\.data\.syntax_warnings\z' unload('test.test_import.data.syntax_warnings') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-28-16-45-07.gh-issue-142029.JuXiKu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-28-16-45-07.gh-issue-142029.JuXiKu.rst new file mode 100644 index 00000000000000..b4cd284804de98 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-28-16-45-07.gh-issue-142029.JuXiKu.rst @@ -0,0 +1,2 @@ +Raise :exc:`ValueError` instead of crashing when empty string is used as a name +in ``_imp.create_builtin()``. diff --git a/Python/import.c b/Python/import.c index e91c95b40d94bf..4dd247fac27654 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4420,6 +4420,12 @@ _imp_create_builtin(PyObject *module, PyObject *spec) return NULL; } + if (PyUnicode_GetLength(name) == 0) { + PyErr_Format(PyExc_ValueError, "name must not be empty"); + Py_DECREF(name); + return NULL; + } + PyObject *mod = create_builtin(tstate, name, spec, NULL); Py_DECREF(name); return mod; diff --git a/Python/pystate.c b/Python/pystate.c index 2956e785405a0e..4bf89a234266e4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1765,16 +1765,23 @@ PyThreadState_Clear(PyThreadState *tstate) struct _Py_freelists *freelists = _Py_freelists_GET(); _PyObject_ClearFreeLists(freelists, 1); + // Flush the thread's local GC allocation count to the global count + // before the thread state is cleared, otherwise the count is lost. + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; + _Py_atomic_add_int(&tstate->interp->gc.young.count, + (int)tstate_impl->gc.alloc_count); + tstate_impl->gc.alloc_count = 0; + // Merge our thread-local refcounts into the type's own refcount and // free our local refcount array. - _PyObject_FinalizePerThreadRefcounts((_PyThreadStateImpl *)tstate); + _PyObject_FinalizePerThreadRefcounts(tstate_impl); // Remove ourself from the biased reference counting table of threads. _Py_brc_remove_thread(tstate); // Release our thread-local copies of the bytecode for reuse by another // thread - _Py_ClearTLBCIndex((_PyThreadStateImpl *)tstate); + _Py_ClearTLBCIndex(tstate_impl); #endif // Merge our queue of pointers to be freed into the interpreter queue.