Skip to content

Commit bf301eb

Browse files
committed
gh-161407: Allocate audit hook entries with malloc()
A hook added with PySys_AddAuditHook() before Py_Initialize() was allocated with PyMem_RawMalloc() before the startup configuration was applied, and freed with PyMem_RawFree() at finalization. When PYTHONMALLOC=mimalloc, -X dev or a debug allocator changed the raw allocator in between, the entry was freed with a different allocator and the process crashed at exit. Allocate and free the entries with plain malloc() and free(), which nothing replaces at startup, and test _testembed test_audit under the affected settings.
1 parent 7054b71 commit bf301eb

3 files changed

Lines changed: 29 additions & 3 deletions

File tree

‎Lib/test/test_embed.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,24 @@ def test_open_code_hook(self):
20622062
def test_audit(self):
20632063
self.run_embedded_interpreter("test_audit")
20642064

2065+
def test_audit_hook_survives_allocator_change(self):
2066+
# A hook added before Py_Initialize() is freed at finalization, after
2067+
# PYTHONMALLOC changed the raw allocator: the entry must not be
2068+
# allocated with one allocator and freed with another.
2069+
allocators = ['debug']
2070+
if not support.Py_GIL_DISABLED:
2071+
allocators.append('malloc_debug')
2072+
if support.with_mimalloc():
2073+
allocators.extend(['mimalloc', 'mimalloc_debug'])
2074+
for allocator in allocators:
2075+
with self.subTest(allocator=allocator):
2076+
self.run_embedded_interpreter(
2077+
"test_audit", env={'PYTHONMALLOC': allocator})
2078+
# The development mode installs the debug hooks too
2079+
with self.subTest(dev_mode=True):
2080+
self.run_embedded_interpreter(
2081+
"test_audit", env={'PYTHONDEVMODE': '1'})
2082+
20652083
def test_audit_tuple(self):
20662084
self.run_embedded_interpreter("test_audit_tuple")
20672085

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash at exit when an audit hook was added with :c:func:`PySys_AddAuditHook`
2+
before :c:func:`Py_Initialize` and the raw memory allocator changed during startup, for
3+
example with ``PYTHONMALLOC=mimalloc`` or in the :ref:`Python Development Mode <devmode>`.

‎Python/sysmodule.c‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Data members:
3232
#include "pycore_pylifecycle.h" // _PyErr_WriteUnraisableDefaultHook()
3333
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
3434
#include "pycore_pymem.h" // _PyMem_DefaultRawFree()
35+
#include "pycore_pymem_init.h" // _PyMem_RawMalloc()
3536
#include "pycore_pystate.h" // _PyThreadState_GET()
3637
#include "pycore_pystats.h" // _Py_PrintSpecializationStats()
3738
#include "pycore_runtime.h" // _PyRuntimeState_Get*()
@@ -448,7 +449,7 @@ _PySys_ClearAuditHooks(PyThreadState *ts)
448449
runtime->audit_hooks.head = NULL;
449450
while (e) {
450451
n = e->next;
451-
PyMem_RawFree(e);
452+
_PyMem_RawFree(NULL, e);
452453
e = n;
453454
}
454455
}
@@ -496,8 +497,12 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
496497
}
497498
}
498499

499-
_Py_AuditHookEntry *e = (_Py_AuditHookEntry*)PyMem_RawMalloc(
500-
sizeof(_Py_AuditHookEntry));
500+
/* Use malloc(), not PyMem_RawMalloc(): a hook can be added before
501+
Py_Initialize(), and the entry is freed during finalization, after
502+
PYTHONMALLOC may have changed the raw allocator (and, in debug builds,
503+
the allocator under the debug hooks). */
504+
_Py_AuditHookEntry *e = (_Py_AuditHookEntry*)_PyMem_RawMalloc(
505+
NULL, sizeof(_Py_AuditHookEntry));
501506
if (!e) {
502507
if (tstate != NULL) {
503508
_PyErr_NoMemory(tstate);

0 commit comments

Comments
 (0)