From f937468e7c88c768a28ff4e653da051ffa30d86c Mon Sep 17 00:00:00 2001 From: Shamil Date: Sat, 18 Oct 2025 12:27:58 +0300 Subject: [PATCH 1/6] gh-140272: Fix memory leak in _gdbm.gdbm.clear() (GH-140274) --- .../2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst | 1 + Modules/_gdbmmodule.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst new file mode 100644 index 00000000000..666a45055f5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst @@ -0,0 +1 @@ +Fix memory leak in the :meth:`!clear` method of the :mod:`dbm.gnu` database. diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 87b84976f49..a6e0662ae74 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -673,8 +673,10 @@ _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls) } if (gdbm_delete(self->di_dbm, key) < 0) { PyErr_SetString(state->gdbm_error, "cannot delete item from database"); + free(key.dptr); return NULL; } + free(key.dptr); } Py_RETURN_NONE; } From 936de256a9542c58da72a8986658bb307b2f2175 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Oct 2025 13:54:40 +0300 Subject: [PATCH 2/6] Move the NEWS entry for gh-140272 to the correct place (GH-140290) --- .../2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Core_and_Builtins => Library}/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst (100%) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst b/Misc/NEWS.d/next/Library/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst similarity index 100% rename from Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst rename to Misc/NEWS.d/next/Library/2025-10-17-23-58-11.gh-issue-140272.lhY8uS.rst From 78e1d65a4d656e783bc28a02e113f59e4323e41a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Oct 2025 13:55:26 +0300 Subject: [PATCH 3/6] gh-140241: Fix documentation for the registry parameter of warnings.warn_explicit() (GH-140242) Co-authored-by: Petr Viktorin --- Doc/library/warnings.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index f9c8c4fc3a8..03b7a8dc378 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -480,14 +480,21 @@ Available Functions .. function:: warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None) This is a low-level interface to the functionality of :func:`warn`, passing in - explicitly the message, category, filename and line number, and optionally the - module name and the registry (which should be the ``__warningregistry__`` - dictionary of the module). The module name defaults to the filename with - ``.py`` stripped; if no registry is passed, the warning is never suppressed. + explicitly the message, category, filename and line number, and optionally + other arguments. *message* must be a string and *category* a subclass of :exc:`Warning` or *message* may be a :exc:`Warning` instance, in which case *category* will be ignored. + *module*, if supplied, should be the module name. + If no module is passed, the filename with ``.py`` stripped is used. + + *registry*, if supplied, should be the ``__warningregistry__`` dictionary + of the module. + If no registry is passed, each warning is treated as the first occurrence, + that is, filter actions ``"default"``, ``"module"`` and ``"once"`` are + handled as ``"always"``. + *module_globals*, if supplied, should be the global namespace in use by the code for which the warning is issued. (This argument is used to support displaying source for modules found in zipfiles or other non-filesystem import From c8729c9909e150989050ce2a435d018955bea41d Mon Sep 17 00:00:00 2001 From: Shamil Date: Sat, 18 Oct 2025 14:01:53 +0300 Subject: [PATCH 4/6] gh-140257: fix data race on eval_breaker during finalization (#140265) --- .../2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst | 2 ++ Python/pystate.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst new file mode 100644 index 00000000000..50f7e0e48ae --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-17-20-23-19.gh-issue-140257.8Txmem.rst @@ -0,0 +1,2 @@ +Fix data race between interpreter_clear() and take_gil() on eval_breaker +during finalization with daemon threads. diff --git a/Python/pystate.c b/Python/pystate.c index dbed609f29a..c402d89a161 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -763,10 +763,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->audit_hooks); - // At this time, all the threads should be cleared so we don't need atomic - // operations for instrumentation_version or eval_breaker. + // gh-140257: Threads have already been cleared, but daemon threads may + // still access eval_breaker atomically via take_gil() right before they + // hang. Use an atomic store to prevent data races during finalization. interp->ceval.instrumentation_version = 0; - tstate->eval_breaker = 0; + _Py_atomic_store_uintptr(&tstate->eval_breaker, 0); for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { interp->monitors.tools[i] = 0; From 58c44c2bf2e6d251548652a21d9ee27265ee6dea Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 18 Oct 2025 16:36:58 +0530 Subject: [PATCH 5/6] gh-140067: Fix memory leak in sub-interpreter creation (#140111) (#140261) Fix memory leak in sub-interpreter creation caused by overwriting of the previously used `_malloced` field. Now the pointer is stored in the first word of the memory block to avoid it being overwritten accidentally. Co-authored-by: Kumar Aditya --- Include/internal/pycore_interp_structs.h | 6 ------ Lib/test/test_threading.py | 1 + ...2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 1 + Python/pystate.c | 15 +++++++++------ 4 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 2124e76514f..badc97808c6 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -769,12 +769,6 @@ struct _is { * and should be placed at the beginning. */ struct _ceval_state ceval; - /* This structure is carefully allocated so that it's correctly aligned - * to avoid undefined behaviors during LOAD and STORE. The '_malloced' - * field stores the allocated pointer address that will later be freed. - */ - void *_malloced; - PyInterpreterState *next; int64_t id; diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index d0f0e8ab2f7..efd69a1f4fe 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1776,6 +1776,7 @@ def task(): self.assertEqual(os.read(r_interp, 1), DONE) @cpython_only + @support.skip_if_sanitizer(thread=True, memory=True) def test_daemon_threads_fatal_error(self): import_module("_testcapi") subinterp_code = f"""if 1: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst new file mode 100644 index 00000000000..3c5a828101d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst @@ -0,0 +1 @@ +Fix memory leak in sub-interpreter creation. diff --git a/Python/pystate.c b/Python/pystate.c index c402d89a161..af7828d6a03 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -457,16 +457,19 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) static PyInterpreterState * alloc_interpreter(void) { + // Aligned allocation for PyInterpreterState. + // the first word of the memory block is used to store + // the original pointer to be used later to free the memory. size_t alignment = _Alignof(PyInterpreterState); - size_t allocsize = sizeof(PyInterpreterState) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; } - PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment); - assert(_Py_IS_ALIGNED(interp, alignment)); - interp->_malloced = mem; - return interp; + void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); + ((void **)ptr)[-1] = mem; + assert(_Py_IS_ALIGNED(ptr, alignment)); + return ptr; } static void @@ -481,7 +484,7 @@ free_interpreter(PyInterpreterState *interp) interp->obmalloc = NULL; } assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState))); - PyMem_RawFree(interp->_malloced); + PyMem_RawFree(((void **)interp)[-1]); } } From d86ad870cc112d4bd36165398368826746555730 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Sat, 18 Oct 2025 22:35:24 +0800 Subject: [PATCH 6/6] gh-140251: colorize import statement formatting in asyncio console (#140252) --- Lib/asyncio/__main__.py | 5 ++++- .../Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 10bfca3cf96..d078ebfa4ce 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -107,7 +107,10 @@ def run(self): if CAN_USE_PYREPL: theme = get_theme().syntax ps1 = f"{theme.prompt}{ps1}{theme.reset}" - console.write(f"{ps1}import asyncio\n") + import_line = f'{theme.keyword}import{theme.reset} asyncio' + else: + import_line = "import asyncio" + console.write(f"{ps1}{import_line}\n") if CAN_USE_PYREPL: from _pyrepl.simple_interact import ( diff --git a/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst b/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst new file mode 100644 index 00000000000..cb08e02429b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-17-12-33-01.gh-issue-140251.esM-OX.rst @@ -0,0 +1 @@ +Colorize the default import statement ``import asyncio`` in asyncio REPL.