From 595adea94eca39e3ec9874a1d8601d9216ccd3af Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Tue, 14 Oct 2025 16:57:55 +0300 Subject: [PATCH 1/5] bpo-140067: Fix memory leak in subinterpreter creation (remove redundant malloced pointer) --- Include/internal/pycore_interp_structs.h | 6 ------ Python/pystate.c | 15 +++++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 2124e76514f1af..badc97808c6132 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/Python/pystate.c b/Python/pystate.c index dbed609f29aa07..fb7f8177ab2204 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 0fd2715cc17318bd32f079b2eb0456abd33d1082 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Tue, 14 Oct 2025 17:08:03 +0300 Subject: [PATCH 2/5] add blurb --- .../2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst 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 00000000000000..9c00be5def8ed2 --- /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 subinterpreter creation detected by AddressSanitizer. From e9c673593c23d21a8a765002b1f8052fcb112e7d Mon Sep 17 00:00:00 2001 From: Shamil Date: Tue, 14 Oct 2025 17:16:06 +0300 Subject: [PATCH 3/5] Update Misc/NEWS.d/next/Core_and_Builtins/2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst Co-authored-by: Kumar Aditya --- .../2025-10-14-17-07-37.gh-issue-140067.ID2gOm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 9c00be5def8ed2..3c5a828101d9a8 100644 --- 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 @@ -1 +1 @@ -Fix memory leak in subinterpreter creation detected by AddressSanitizer. +Fix memory leak in sub-interpreter creation. From 0afd99e1c9f66a4793cc250093b480abfe9e0e18 Mon Sep 17 00:00:00 2001 From: Shamil Date: Tue, 14 Oct 2025 17:16:31 +0300 Subject: [PATCH 4/5] Update Python/pystate.c Co-authored-by: Kumar Aditya --- Python/pystate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pystate.c b/Python/pystate.c index fb7f8177ab2204..71c436607dfb7d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -461,7 +461,7 @@ alloc_interpreter(void) // 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) + sizeof(void*) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; From 47fef0248a57264068ed3d3ebdd383f96dd1c01b Mon Sep 17 00:00:00 2001 From: Shamil Date: Tue, 14 Oct 2025 17:16:40 +0300 Subject: [PATCH 5/5] Update Python/pystate.c Co-authored-by: Kumar Aditya --- Python/pystate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pystate.c b/Python/pystate.c index 71c436607dfb7d..bf6e4e56e6df87 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -466,7 +466,7 @@ alloc_interpreter(void) if (mem == NULL) { return NULL; } - void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void*), alignment); + void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); ((void **)ptr)[-1] = mem; assert(_Py_IS_ALIGNED(ptr, alignment)); return ptr;