| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
…ests Python/jit.c's jit_alloc()/jit_free() mmap and munmap the JIT's machine-code buffers directly. Valgrind's leak checker does not track raw mmap'd memory as a "block", so a forgotten jit_free() -- the shape of pythongh-142476, a real ASan-found leak in allocate_executor -- is completely invisible to it. Measured on a machine-code JIT build (--enable-experimental-jit, LLVM 21) with a forgotten-free deliberately injected into jit_free() and eight executors created and dropped: without --with-valgrind: definitely lost: 0 bytes in 0 blocks with --with-valgrind: definitely lost: 139,264 bytes in 10 blocks and in the second case with a backtrace through jit_alloc -> _PyJIT_Compile -> make_executor_from_uops -> stop_tracing_and_jit. Background noise was byte-identical between the two runs (120 bytes possibly lost, 646 still reachable), so the only difference is the injected JIT leak. The injection was reverted before committing. Guarded by the existing --with-valgrind configure option, and includes valgrind/valgrind.h -- the header that configure already probes for and that defines both macros used here -- mirroring Objects/obmalloc.c. No VALGRIND_DISCARD_TRANSLATIONS is added. Valgrind's munmap and mprotect syscall wrappers already discard cached translations for any range that held translated code, in architecture-independent code (coregrind/m_syswrap/syswrap-generic.c). Confirmed experimentally on both x86-64 and aarch64: with a control that does go stale, CPython's mmap/write-once/mprotect/munmap pattern never executes a stale translation, even under --smc-check=none.
|
Noting for whoever picks this up: the red CI is unrelated to this The only failing job is Windows / Build and test (Win32, switch-case), test.test_asyncio.test_sslproto.SelectorStartTLSTests
.test_shutdown_corrupted_ssl_sends_close_notify
... Warning -- Uncaught thread exception: AssertionError
== Tests result: ENV CHANGED ==
ENV CHANGED here means the test leaked an uncaught exception from a This PR touches two files — Python/jit.c and a NEWS entry — so there Happy to rebase onto current main to get a fresh run if that would |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Python/jit.c's jit_alloc() and jit_free() mmap and munmap the JIT's machine-code buffers directly. Valgrind's leak checker does not track raw mmap'd memory as a "block", so a forgotten jit_free() — the shape of gh-142476, a real ASan-found leak in allocate_executor — is completely invisible to it.
This adds VALGRIND_MALLOCLIKE_BLOCK / VALGRIND_FREELIKE_BLOCK around those two functions, guarded by the existing --with-valgrind configure option, mirroring the pattern vendored mimalloc already uses in Include/internal/mimalloc/mimalloc/track.h.
To measure the effect I injected a forgotten free into jit_free() on a machine-code JIT build (--enable-experimental-jit, LLVM 21) and ran a script that creates eight executors and drops them:
and in the second case with a usable backtrace:
Background noise was byte-identical between the two runs (120 bytes possibly lost, 646 bytes still reachable), so the injected leak is the only difference. The injection is not part of this patch.
The include is valgrind/valgrind.h — the header configure already probes for under --with-valgrind, and the one that defines both macros used here, so no build-system change is needed. Objects/obmalloc.c includes the same header.
On VALGRIND_DISCARD_TRANSLATIONS
I deliberately did not add any. It seemed like the obvious thing for a JIT, but it turns out to be unnecessary here.
Valgrind's munmap and mprotect syscall wrappers already discard cached translations for any range that previously held translated code, and that logic lives in architecture-independent code (coregrind/m_syswrap/syswrap-generic.c). I also checked it experimentally with a small driver that emits code, executes it enough times to guarantee a cached translation, changes it, and executes again — with a control case that genuinely does go stale so the harness is known to detect the failure. CPython's mmap → write once → mprotect → execute → munmap-whole-region pattern never executed a stale translation on either x86-64 (Valgrind 3.25.1) or aarch64 (Valgrind 3.19.0), in any --smc-check mode including none.
A side effect worth recording: since that is already true, --smc-check=none is safe for CPython today with no code change. On a tight JIT-compiled integer loop that is worth about 10% over the all-non-file default (median of three runs each: 11.33 s vs 10.17 s, ranges 11.22-11.45 and 10.12-10.36, so non-overlapping). That workload is close to a best case — it is almost entirely JIT-executed — so a realistic program should expect less.
Testing
Builds cleanly both with and without --with-valgrind; test_capi and test_optimizer pass (1,501 tests) on the --with-valgrind build, and under valgrind memcheck.