Fix x86 GCStress race with indirect calls before epilogs #72592
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
For partially interruptible methods there is a mismatch between where GC
stress runs GCs and where normal return address hijacking would run GCs.
GC stress runs the GC on the first instruction after a call returns,
where there for partially interruptible methods is no GC info that says
to protect GC pointers in return registers (thanks @jkotas for the explanation).
This means GC stress needs to do some special work to figure out that these
need to be protected.
The way it does that is the following:
For direct call sites, in
GCCoverageInfo::SprinkleBreakpointsit getsthe target MD of each call site and places a special illegal
instruction right after the call that the GC stress handler will use
to figure out which (if any) registers have GC pointers that need
protection
For indirect call sites, in
GCCoverageInfo::SprinkleBreakpointit willfirst place an illegal instruction on the call instruction so that the
GC stress handler will break there. Once the GC stress handler breaks,
it computes the target address and gets the target MD from that, and
then places the illegal instruction on the next instruction like
above.
GCCoverageInfo::SprinkleBreakpointsruns right after jitting and shouldtherefore not race with anything. However, the GC stress handler can
race with any other thread accessing the same function. That makes the
latter problematic on x86 where unwinding reads the epilog code to work.
In this particular case thread A is about to unwind through the epilog
when thread B stops on an indirect call right before the epilog. Thread
B then overwrites the first instruction of the epilog, causing thread A
to unwind incorrectly.
The issue seems to be a long-standing one, but we are hitting it after
#65738 where we started using indirect calls more often.
UnwindStackFramealready has access to the GC stress saved code and isactually already using it for other unwinding. To fix the issue, make it
use the saved code for unwinding epilogs as well.
Fix #68431