Skip to content

fix: make SIGSEGV handler chain depth thread-local - #1025

Open
maleadt wants to merge 1 commit into
intel:masterfrom
maleadt:fix-page-fault-handler-chain-race
Open

maleadt wants to merge 1 commit into
intel:masterfrom
maleadt:fix-page-fault-handler-chain-race

Conversation

@maleadt

@maleadt maleadt commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1009.

PageFaultManagerLinux tracks its position in the chain of previous SIGSEGV handlers with handlerIndex, a member shared by all threads. When a second thread faults while another one is still inside a chained handler, it computes an index before the start of previousPageFaultHandlers and calls a garbage function pointer (or, since e613839, hits the UNRECOVERABLE_IF). Managed runtimes run into this deterministically: on a GC safepoint every thread faults on the same protected page and waits inside the runtime's handler until all of them have arrived. This crashes multithreaded Julia programs as soon as NEO has installed its handler (JuliaGPU/oneAPI.jl#607).

A handler chain always runs on a single thread, so this keeps the depth per thread, the same way CpuPageFaultManager already keeps hostFunctionActive. The new ULT has two threads fault at once while the previous handler waits for both; it segfaults without the change.

Tested: the PageFaultManagerLinuxTest.* shared ULTs (18/18 pass with the change; the new test crashes without it) on 26.35.39758.10, since master doesn't build in our older-glibc environment. The same change, shipped as a patch in Julia's NEO_jll 26.35.39758+1, fixes the multithreaded GC crash from JuliaGPU/oneAPI.jl#607 on Tiger Lake (with debug keys forcing NEO to install its handler there).

PageFaultManagerLinux tracks its position in the chain of previous
SIGSEGV handlers with a counter shared by all threads. When a second
thread faults while another one is still inside a chained handler, it
reads before the start of previousPageFaultHandlers and calls a garbage
function pointer, or now hits the UNRECOVERABLE_IF. Managed runtimes run
into this deterministically: on a GC safepoint every thread faults on the
same protected page and waits inside the runtime's handler until all of
them have arrived.

A handler chain always runs on a single thread, so keep the depth per
thread.

Related-To: GSD-13581
Signed-off-by: Tim Besard <tim.besard@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[GSD-13581] PageFaultManagerLinux::callPreviousHandler is not thread-safe: concurrent SIGSEGVs on non-USM addresses crash the process

1 participant