From afd0746671ae594dbf6b189d2d6ab2ebfe21fa94 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Fri, 5 Jun 2026 11:57:28 +0200 Subject: [PATCH] fix: critical section for PyDict_Next in _pickle.c --- ...5-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst | 3 +++ Modules/_pickle.c | 20 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst b/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst new file mode 100644 index 00000000000000..3a12e26cf736f7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst @@ -0,0 +1,3 @@ +Fix a crash in free-threaded builds that occurs when pickling by name +objects without a ``__module__`` attribute while :data:`sys.modules` +is concurrently being modified. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 8989202f2d3c3e..d23d58ef884cc4 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -1966,17 +1966,29 @@ whichmodule(PyObject *global, PyObject *dotted_path) return NULL; } if (PyDict_CheckExact(modules)) { + PyObject *found_name = NULL; + int error = 0; i = 0; + Py_BEGIN_CRITICAL_SECTION(modules); while (PyDict_Next(modules, &i, &module_name, &module)) { if (_checkmodule(module_name, module, global, dotted_path) == 0) { - Py_DECREF(modules); - return Py_NewRef(module_name); + found_name = Py_NewRef(module_name); + break; } if (PyErr_Occurred()) { - Py_DECREF(modules); - return NULL; + error = 1; + break; } } + Py_END_CRITICAL_SECTION(); + if (error) { + Py_DECREF(modules); + return NULL; + } + if (found_name != NULL) { + Py_DECREF(modules); + return found_name; + } } else { PyObject *iterator = PyObject_GetIter(modules);