mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-18 14:47:18 +03:00
`tsd_get`/`tsd_set` took the tsd address as a plain `&tsd_tls` -- `thread_pointer + const_offset`, which the compiler treats as loop-invariant. Under whole-program LTO it is hoisted out of the inlined malloc/free and kept in a callee-saved register across a user-space context switch (swapcontext, boost::context fibers); resumed on a different OS thread, the cached tsd/tcache still belongs to the previous thread and the two race -- heap corruption that reproduces only under LTO and is invisible to sanitizers. The write side is worse: tsd_set (from tsd_fetch_slow, on a thread's first allocation after migration) would copy the new thread's tsd over the previous thread's live one and register the wrong cleanup key. Route tsd_get/tsd_set in every GNU backend through JEMALLOC_TLS_ADDR(tsd_tls): a per-variable accessor that takes the address inside a noinline `memory`-barrier function, opaque to the optimizer. Correct for every TLS model, one call per access; MSVC keeps the plain address. The accessor is declared in every TU (JEMALLOC_TLS_ADDR_DECLARE) but defined once in src/tsd.c (JEMALLOC_TLS_ADDR_DEFINE, under JEMALLOC_TSD_C_): a per-TU `static` body is emitted at -O0 even when unused, pulling an undefined reference to the internal thread-local into the integration-test util objects, which link only the public shared library. DECLARE/DEFINE are invoked directly, never forwarded, so `##tlsvar` pastes the literal name rather than the expanded `je_tsd_tls`. |
||
|---|---|---|
| .. | ||
| jemalloc | ||
| msvc_compat | ||