Skip to content

Accept a faulted frame in the shadow stack assert of CallCatchFunclet - #135531

Open
pcshrosbree wants to merge 2 commits into
dotnet:mainfrom
pcshrosbree:cet-catch-funclet-assert
Open

pcshrosbree wants to merge 2 commits into
dotnet:mainfrom
pcshrosbree:cet-catch-funclet-assert

Conversation

@pcshrosbree

Copy link
Copy Markdown
Contributor

Fixes #135530.

On windows-x64 with shadow stacks, a Checked runtime asserts in CallCatchFunclet when a thread abort that the runtime raises again at the end of a catch handler is caught by another handler in the same method.

EECodeManager::ResumeAfterCatch sends such a thread to ThrowControlForThread, and RedirectForThrowControl records in the FaultingExceptionFrame the SSP at the point where the thread was redirected. That frame was interrupted rather than stopped at a call, so the slot below its SSP does not hold its ControlPC, and the assert's last clause fails when the catch handler is in that frame. The SSP points at the frame's return address, which is what resuming the frame needs. The assert now also accepts a handler frame that has faulted. Release builds are unchanged.

Test: new src/tests/Regressions/coreclr/GitHub_135530, the case from the issue. It is disabled under the interpreter until #135520 (fixes #135463) is in; the interpreter fails it for that reason, not this one.

Validation (Checked):

main this PR
windows-x64 (CET enabled), JIT assert in CallCatchFunclet pass
windows-x64, JIT with TieredCompilation=0, JITMinOpts, JitStress 1 and 2, ReadyToRun=0, GCStress 0x3 and 0xC pass
linux-x64 and osx-arm64, JIT, same configurations pass pass (the assert is Windows x64 only)

The other probes from the issue investigation also print the same on windows-x64 as on linux-x64 with this change, including an abort raised again right in front of a try block that catches it.

Note

This PR was prepared with AI assistance (Anthropic Claude, reviewed by OpenAI Codex). The builds and runs it cites were done at the PR's base commit.

A thread abort raised again at the end of a catch handler is thrown from
ThrowControlForThread, which records the SSP of the frame the thread was
redirected from. That frame was interrupted rather than stopped at a call,
so the slot below its SSP does not hold its ControlPC, and the assert
failed on Windows x64 with shadow stacks when the abort was caught in the
same frame. The SSP itself is the right value to resume the frame with.

Fixes dotnet#135530
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Oct 9, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be <CLRTestPriority>1</CLRTestPriority> test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 4fd5087.

@jkotas
jkotas requested a review from janvorli October 9, 2026 20:24
It aborts a thread and is a niche case.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-VM-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

2 participants