Skip to content

gh-161407: Allocate audit hook entries with malloc() - #161409

Open
maikschulze wants to merge 1 commit into
python:mainfrom
maikschulze:gh-161407-audit-hook-malloc
Open

maikschulze wants to merge 1 commit into
python:mainfrom
maikschulze:gh-161407-audit-hook-malloc

Conversation

@maikschulze

Copy link
Copy Markdown

PySys_AddAuditHook() can be called before Py_Initialize(), but the hook's list entry was allocated with PyMem_RawMalloc() and freed with PyMem_RawFree() at finalization. When the startup configuration changes the raw allocator in between, the entry is freed with a different allocator than it was allocated with, and the process crashes at exit. That happens with PYTHONMALLOC=mimalloc on builds with the GIL since 3.15, and whenever the debug hooks are installed (-X dev, the *_debug allocators).

This allocates and frees the entries with plain malloc() / free() (_PyMem_RawMalloc() / _PyMem_RawFree()), which nothing at startup replaces.

I first used _PyMem_DefaultRawMalloc() / _PyMem_DefaultRawFree(), like other pre-initialization data. In debug builds those go through the debug hooks, whose underlying allocator PYTHONMALLOC=mimalloc_debug replaces, so a Py_DEBUG build still crashed.

The new test, test_embed.AuditingTests.test_audit_hook_survives_allocator_change, runs _testembed test_audit, which adds a hook before Py_Initialize(), under the affected PYTHONMALLOC values and in development mode. Without the fix it fails in every subtest.

Tested on macOS (release with the GIL, free-threaded, Py_DEBUG) and Windows (release with and without the GIL): the new test, test_audit, test_embed, test_sys and test_capi.test_mem. The full test suite passes on macOS (release build with the GIL).

Affected since 3.13 (debug hooks) and 3.15 (PYTHONMALLOC=mimalloc).

I used an AI assistant to investigate this and draft the patch; I reviewed and tested the changes myself.

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.
@python-cla-bot

python-cla-bot Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

Comment thread Python/sysmodule.c

_Py_AuditHookEntry *e = (_Py_AuditHookEntry*)PyMem_RawMalloc(
sizeof(_Py_AuditHookEntry));
/* Use malloc(), not PyMem_RawMalloc(): a hook can be added before

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I don't think that it's worth it to bother with _PyMem_RawMalloc() wrapper (and pass NULL as context). I would prefer to use malloc() / free() directly.

The "new" PyInitConfig C API also uses directly malloc() / free() to avoid troubles with changing the memory allocator.

Comment thread Lib/test/test_embed.py
def test_audit(self):
self.run_embedded_interpreter("test_audit")

def test_audit_hook_survives_allocator_change(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that adding a test is useful, since Python/sysmodule.c no longer depends on the Python memory allocator (PYTHONMALLOC) with this change. I suggest removing the test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants