Skip to content

Conversation

@tommcdon
Copy link
Member

This PR fixes a scenario where we fail when resuming from a CALL instruction that is currently patched with a breakpoint. The fix will only be enabled when executing on Windows X64 when resuming from a breakpoint on a CALL instruction and the Out-of-Process SetThreadContext debugger feature is enabled. Out of process SetThreadContext is only enabled when 1) CET is enabled, -OR- 2) DOTNET_OutOfProcessSetContext=1 environment variable is set.  

Background

Debugger support for CET was added in .NET 7 on #7357. In .NET 9, support for CET has been enabled by default on Windows X64. When CET is detected, the .NET debugger alters the thread context from out of process instead of in-process. In general non-optimized debugging works with this fix; however, we have found that resuming from a breakpoint on a CALL instruction triggers a CET failure (often seen as 0xc0000409 or Fail Fast). This can be reproduced by attempting to step through optimized code, such as a Ready-To-Run method.

The debugger currently copies the patched instruction into a special buffer. When program execution resumes from a breakpoint, it will set thread context into the patch buffer, single-step over the instruction, then resume execution back in the original program execution stream. When we single-step over a CALL instruction, the return address on the stack points back to the patch buffer (instead of the original program stream). To account for this, we adjust the return address on the stack to point back to the instruction immediately after the CALL instruction. Unfortunately, if CET Is enabled, the shadow stack will not match the actual stack, resulting in a failure.

This PR changes fixes the shadow stack issue for resuming from CALL instructions by performing an in-place single-step over the CALL instruction in the normal program stream (not the patch buffer). To address concerns around slipping breakpoints (as we are now replacing the original instruction into the program stream when resuming from a breakpoint over a CALL instruction), we suspend all threads during the replace/single-step/re-patch sequence of events. There is new code in CordbProcess to coordinate this extra sequence of events.

We will only perform an in-place single-step when Out-of-Process Execution Control is enabled -and- we resume from breakpoints on CALL instructions.

While testing, I also found and fixed an issue with Thread::ReadyForAsyncException caused by a non-initialized CONTEXT and Windows attempting to retrieve the extended XSTATE data in the context.

…ft side, and resume threads on the right side before the left side is notified
…otePatch/RemoveRemotePatch for patch writing
@tommcdon tommcdon requested a review from janvorli October 11, 2024 22:15
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @tommcdon
See info in area-owners.md if you want to be subscribed.

@mikelle-rogers
Copy link
Member

mikelle-rogers commented Oct 14, 2024 via email

Copy link
Member

@noahfalk noahfalk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. All my suggestions are stylistic so none of them are necessary if you need to get this merged quickly to meet checkin deadlines.

Co-authored-by: Noah Falk <noahfalk@users.noreply.github.com>
@tommcdon tommcdon merged commit a636e6d into dotnet:main Oct 15, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Nov 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants