-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Fix interpreter EH when exception escapes filter funclet #120811
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix interpreter EH when exception escapes filter funclet #120811
Conversation
The EH was crashing with interpreter when there was an unhandled exception in a filter funclet. It was asserting on x64 windows due to SSP not being correct, but the actual issue was more involved than just ensuring the SSP was correct. The problem happens due to the way the stack walking handles unwinding from the first interpreted frame. To better explain what was wrong, let me describe hat mechanism. That transition sets the IP to a special value InterpreterFrame::DummyCallerIP and SP to the address of the InterpreterFrame. The frame state is SFITER_NATIVE_MARKER_FRAME. When the stack frame iterator moves to the next frame, the state is SFITER_FRAME_FUNCTION, the explicit frame is the InterpreterFrame and the REGDISPLAY is set to the native context that was in REGDISPLAY before we switched to iterating over the interpreter frames. The SfiNext in the EH needs to handle the case when it gets the SFITER_NATIVE_MARKER_FRAME described above. It can either be transitioning to a managed caller of the interpreted core or it could have been a call to a funclet that doesn't have transition frame stored in the intepreter frame, so it doesn't have any context to move to. This doesn't cause any problem for catch or finally funclets, as that means a collided unwind that is detected and the REGDISPLAY is overwritten by the REGDISPLAY from the stack frame iterator of the exception that we've collided with. The only problem is for filter funclet, where we need to return that frame from the SfiNext and the EH uses its SP in the second pass to know when the 2nd pass is finished. The bug was that we have moved the stack frame iterator from the SFITER_NATIVE_MARKER_FRAME, which got REGDISPLAY with SP that was actually smaller than the SP of the last reported interpreted frame. That caused the 2nd pass to terminate prematurely on the very first frame, thinking it found the target frame. Thus finallys were not called and also the context was wrong. That lead to the assert in CallCatchFunclet. There was also a secondary problem that we were not saving and restoring SSP when the state moved from the special state with IP set to InterpreterFrame::DummyCallerIP and the SSP was the SSP in the InterpExecMethod, instead of the SSP in the DispatchManagedException that the rest of the context was pointing to. This change fixes both these issues and unhandled exceptions in filter funclets are correctly swallowed now.
Tagging subscribers to this area: @BrzVlad, @janvorli, @kg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a crash in the exception handling (EH) system when an unhandled exception escapes from a filter funclet in the interpreter. The issue involved incorrect stack pointer handling during unwinding and missing SSP (shadow stack pointer) save/restore logic.
Key Changes:
- Added SSP save/restore for interpreted frames on x64 Windows to maintain correct shadow stack state across frame transitions
- Fixed premature termination of the second pass of exception handling by properly detecting when unwinding from an interpreted filter funclet
- Corrected control flow logic in
SfiNextWorker
to handle cases where filter funclets have no transition frame
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
File | Description |
---|---|
src/coreclr/vm/stackwalk.h | Adds storage for SSP in StackFrameIterator for x64 Windows with interpreter enabled |
src/coreclr/vm/stackwalk.cpp | Implements SSP save/restore logic when transitioning between interpreted and native frames |
src/coreclr/vm/exceptionhandling.cpp | Fixes unwinding logic for interpreted filter funclets and corrects frame iteration |
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs | Updates structure size constants to account for new SSP field |
The EH was crashing with interpreter when there was an unhandled exception in a filter funclet. It was asserting on x64 windows due to SSP not being correct, but the actual issue was more involved than just ensuring the SSP was correct.
The problem happens due to the way the stack walking handles unwinding from the first interpreted frame. To better explain what was wrong, let me describe hat mechanism. That transition sets the IP to a special value InterpreterFrame::DummyCallerIP and SP to the address of the InterpreterFrame. The frame state is SFITER_NATIVE_MARKER_FRAME. When the stack frame iterator moves to the next frame, the state is SFITER_FRAME_FUNCTION, the explicit frame is the InterpreterFrame and the REGDISPLAY is set to the native context that was in REGDISPLAY before we switched to iterating over the interpreter frames.
The SfiNext in the EH needs to handle the case when it gets the SFITER_NATIVE_MARKER_FRAME described above. It can either be transitioning to a managed caller of the interpreted core or it could have been a call to a funclet that doesn't have transition frame stored in the intepreter frame, so it doesn't have any context to move to. This doesn't cause any problem for catch or finally funclets, as that means a collided unwind that is detected and the REGDISPLAY is overwritten by the REGDISPLAY from the stack frame iterator of the exception that we've collided with.
The only problem is for filter funclet, where we need to return that frame from the SfiNext and the EH uses its SP in the second pass to know when the 2nd pass is finished.
The bug was that we have moved the stack frame iterator from the SFITER_NATIVE_MARKER_FRAME, which got REGDISPLAY with SP that was actually smaller than the SP of the last reported interpreted frame. That caused the 2nd pass to terminate prematurely on the very first frame, thinking it found the target frame. Thus finallys were not called and also the context was wrong. That lead to the assert in CallCatchFunclet.
There was also a secondary problem that we were not saving and restoring SSP when the state moved from the special state with IP set to InterpreterFrame::DummyCallerIP and the SSP was the SSP in the InterpExecMethod, instead of the SSP in the DispatchManagedException that the rest of the context was pointing to.
This change fixes both these issues and unhandled exceptions in filter funclets are correctly swallowed now.