Skip to content

Commit 1d11627

Browse files
[3.14] pythongh-140067: Fix memory leak in sub-interpreter creation (pythonGH-140111) (python#140118)
* [3.14] pythongh-140067: Fix memory leak in sub-interpreter creation (pythonGH-140111) 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. (cherry picked from commit 59547a2) Co-authored-by: Shamil <[email protected]> Co-authored-by: Kumar Aditya <[email protected]>
1 parent 3ca7ea1 commit 1d11627

File tree

4 files changed

+12
-10
lines changed

4 files changed

+12
-10
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -768,10 +768,7 @@ struct _is {
768768
* and should be placed at the beginning. */
769769
struct _ceval_state ceval;
770770

771-
/* This structure is carefully allocated so that it's correctly aligned
772-
* to avoid undefined behaviors during LOAD and STORE. The '_malloced'
773-
* field stores the allocated pointer address that will later be freed.
774-
*/
771+
// unused, kept for ABI compatibility
775772
void *_malloced;
776773

777774
PyInterpreterState *next;

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ def task():
17271727
self.assertEqual(os.read(r_interp, 1), DONE)
17281728

17291729
@cpython_only
1730+
@support.skip_if_sanitizer(thread=True, memory=True)
17301731
def test_daemon_threads_fatal_error(self):
17311732
import_module("_testcapi")
17321733
subinterp_code = f"""if 1:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix memory leak in sub-interpreter creation.

Python/pystate.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -565,16 +565,19 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime)
565565
static PyInterpreterState *
566566
alloc_interpreter(void)
567567
{
568+
// Aligned allocation for PyInterpreterState.
569+
// the first word of the memory block is used to store
570+
// the original pointer to be used later to free the memory.
568571
size_t alignment = _Alignof(PyInterpreterState);
569-
size_t allocsize = sizeof(PyInterpreterState) + alignment - 1;
572+
size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1;
570573
void *mem = PyMem_RawCalloc(1, allocsize);
571574
if (mem == NULL) {
572575
return NULL;
573576
}
574-
PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment);
575-
assert(_Py_IS_ALIGNED(interp, alignment));
576-
interp->_malloced = mem;
577-
return interp;
577+
void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment);
578+
((void **)ptr)[-1] = mem;
579+
assert(_Py_IS_ALIGNED(ptr, alignment));
580+
return ptr;
578581
}
579582

580583
static void
@@ -589,7 +592,7 @@ free_interpreter(PyInterpreterState *interp)
589592
interp->obmalloc = NULL;
590593
}
591594
assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState)));
592-
PyMem_RawFree(interp->_malloced);
595+
PyMem_RawFree(((void **)interp)[-1]);
593596
}
594597
}
595598

0 commit comments

Comments
 (0)