diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-30-18-30-00.gh-issue-148286.Qv8Rk3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-30-18-30-00.gh-issue-148286.Qv8Rk3.rst new file mode 100644 index 000000000000000..487a5ee912877d3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-30-18-30-00.gh-issue-148286.Qv8Rk3.rst @@ -0,0 +1,5 @@ +Track JIT-allocated machine-code buffers with Valgrind client requests, so +that memcheck's leak checker can see them. ``jit_alloc()`` and ``jit_free()`` +use ``mmap`` and ``munmap`` directly, which Valgrind does not track as +allocations, making a forgotten free invisible. Guarded by the existing +:option:`--with-valgrind` configure option. diff --git a/Python/jit.c b/Python/jit.c index 50e606a7a0963f6..d63080104e74a5a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -39,6 +39,13 @@ #include #endif +#ifdef WITH_VALGRIND + // VALGRIND_MALLOCLIKE_BLOCK and VALGRIND_FREELIKE_BLOCK live in + // valgrind.h, which is the header configure already probes for + // --with-valgrind (see also Objects/obmalloc.c). + #include +#endif + static size_t get_page_size(void) { @@ -139,6 +146,13 @@ jit_alloc(size_t size) jit_error("unable to allocate memory"); return NULL; } +#ifdef WITH_VALGRIND + // Let memcheck track this JIT code region like a heap allocation, so its + // leak checker and addressability/definedness tracking apply to it. The + // whole region is always freed in one jit_free() call (never partially), + // matching the "one MALLOCLIKE_BLOCK per FREELIKE_BLOCK" contract. + VALGRIND_MALLOCLIKE_BLOCK(memory, size, /*redzone=*/0, /*is_zeroed=*/0); +#endif return memory; } @@ -147,6 +161,9 @@ jit_free(unsigned char *memory, size_t size) { assert(size); assert(size % get_page_size() == 0); +#ifdef WITH_VALGRIND + VALGRIND_FREELIKE_BLOCK(memory, /*redzone=*/0); +#endif #ifdef MS_WINDOWS int failed = !VirtualFree(memory, 0, MEM_RELEASE); #else