Skip to content

Commit 335820f

Browse files
authored
Fix interpreter EH when exception escapes filter funclet (#120811)
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.
1 parent d3c6f06 commit 335820f

File tree

4 files changed

+51
-23
lines changed

4 files changed

+51
-23
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/AsmOffsets.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,13 @@ class AsmOffsets
5959
#if TARGET_64BIT
6060
public const int OFFSETOF__REGDISPLAY__m_pCurrentContext = 0x8;
6161
#if FEATURE_INTERPRETER
62+
#if TARGET_AMD64 && !TARGET_UNIX
63+
public const int SIZEOF__StackFrameIterator = 0x178;
64+
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x170;
65+
#else
6266
public const int SIZEOF__StackFrameIterator = 0x170;
6367
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x168;
68+
#endif
6469
#else
6570
public const int SIZEOF__StackFrameIterator = 0x150;
6671
public const int OFFSETOF__StackFrameIterator__m_AdjustedControlPC = 0x148;

src/coreclr/vm/exceptionhandling.cpp

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,6 +3116,17 @@ void CallCatchFunclet(OBJECTREF throwable, BYTE* pHandlerIP, REGDISPLAY* pvRegDi
31163116
exInfo->m_ScannedStackRange.ExtendUpperBound(exInfo->m_frameIter.m_crawl.GetRegisterSet()->SP);
31173117
DWORD_PTR dwResumePC = 0;
31183118
UINT_PTR callerTargetSp = 0;
3119+
3120+
#ifdef FEATURE_INTERPRETER
3121+
if (GetControlPC(pvRegDisplay) == InterpreterFrame::DummyCallerIP)
3122+
{
3123+
// This is a case when we have unwound out of an interpreted filter funclet. The "Next" moves the
3124+
// REGDISPLAY to the native code context that was there before we started to iterate over the
3125+
// interpreted frames.
3126+
exInfo->m_frameIter.Next();
3127+
}
3128+
#endif // FEATURE_INTERPRETER
3129+
31193130
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
31203131
size_t targetSSP = exInfo->m_frameIter.m_crawl.GetRegisterSet()->SSP;
31213132
// Verify the SSP points to the slot that matches the ControlPC of the frame containing the catch funclet.
@@ -3949,32 +3960,33 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR
39493960
if (isNativeTransition &&
39503961
(GetIP(pThis->m_crawl.GetRegisterSet()->pCurrentContext) == InterpreterFrame::DummyCallerIP))
39513962
{
3952-
// The callerIP is InterpreterFrame::DummyCallerIP when we are going to unwind from the first interpreted frame belonging to an InterpreterFrame.
3953-
// That means it is at a transition where non-interpreted code called interpreted one.
3954-
// Move the stack frame iterator to the InterpreterFrame and extract the IP of the real caller of the interpreted code.
3955-
retVal = pThis->Next();
3956-
_ASSERTE(retVal != SWA_FAILED);
3957-
_ASSERTE(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION);
39583963
_ASSERTE(pThis->m_crawl.GetFrame()->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame);
39593964
InterpreterFrame *pInterpreterFrame = (InterpreterFrame *)pThis->m_crawl.GetFrame();
3960-
// Move to the caller of the interpreted code
3961-
retVal = pThis->Next();
3962-
_ASSERTE(retVal != SWA_FAILED);
3963-
if (pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD)
3965+
// If the GetReturnAddress returns 0, it means the caller is InterpreterCodeManager::CallFunclet.
3966+
// We don't have any TransitionFrame to update the regdisplay from in that case.
3967+
if (pInterpreterFrame->GetReturnAddress() != 0)
39643968
{
3965-
// The caller of the interpreted code is not managed.
3966-
if (pInterpreterFrame->GetReturnAddress() != 0)
3969+
// The callerIP is InterpreterFrame::DummyCallerIP when we are going to unwind from the first interpreted frame belonging to an InterpreterFrame.
3970+
// That means it is at a transition where non-interpreted code called interpreted one.
3971+
// Move the stack frame iterator to the InterpreterFrame and extract the IP of the real caller of the interpreted code.
3972+
retVal = pThis->Next();
3973+
_ASSERTE(retVal != SWA_FAILED);
3974+
_ASSERTE(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION);
3975+
_ASSERTE(pThis->m_crawl.GetFrame()->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame);
3976+
// Move to the caller of the interpreted code
3977+
retVal = pThis->Next();
3978+
_ASSERTE(retVal != SWA_FAILED);
3979+
if (pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD)
3980+
{
3981+
// The caller is a managed code, so we can update the regdisplay to point to it.
3982+
pInterpreterFrame->UpdateRegDisplay(pThis->m_crawl.GetRegisterSet(), /* updateFloats */ true);
3983+
}
3984+
else
39673985
{
3968-
// The caller is not InterpreterCodeManager::CallFunclet, so we can update the regdisplay
3969-
// (The CallFunclet has no TransitionFrame to update from)
3970-
pInterpreterFrame->UpdateRegDisplay(pThis->m_crawl.GetRegisterSet(), /* updateFloats */ true);
3986+
// The caller of the interpreted code is managed.
3987+
isNativeTransition = false;
39713988
}
39723989
}
3973-
else
3974-
{
3975-
// The caller of the interpreted code is managed.
3976-
isNativeTransition = false;
3977-
}
39783990
}
39793991
#endif // FEATURE_INTERPRETER
39803992

@@ -4021,12 +4033,12 @@ CLR_BOOL SfiNextWorker(StackFrameIterator* pThis, uint* uExCollideClauseIdx, CLR
40214033
// Propagating to CallDescrWorkerInternal, filter funclet or CallEHFunclet.
40224034
if (IsCallDescrWorkerInternalReturnAddress(GetIP(pThis->m_crawl.GetRegisterSet()->pCurrentContext)))
40234035
{
4024-
EH_LOG((LL_INFO100, "SfiNext: the native frame is CallDescrWorkerInternal"));
4036+
EH_LOG((LL_INFO100, "SfiNext: the native frame is CallDescrWorkerInternal\n"));
40254037
isPropagatingToNativeCode = TRUE;
40264038
}
40274039
else if (doingFuncletUnwind && codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo))
40284040
{
4029-
EH_LOG((LL_INFO100, "SfiNext: current frame is filter funclet"));
4041+
EH_LOG((LL_INFO100, "SfiNext: current frame is filter funclet\n"));
40304042
isPropagatingToNativeCode = TRUE;
40314043
}
40324044
}

src/coreclr/vm/stackwalk.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2812,7 +2812,9 @@ void StackFrameIterator::ProcessCurrentFrame(void)
28122812
m_interpExecMethodSP = GetSP(pRD->pCurrentContext);
28132813
m_interpExecMethodFP = GetFP(pRD->pCurrentContext);
28142814
m_interpExecMethodFirstArgReg = GetFirstArgReg(pRD->pCurrentContext);
2815-
2815+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
2816+
m_interpExecMethodSSP = pRD->SSP;
2817+
#endif
28162818
((PTR_InterpreterFrame)m_crawl.pFrame)->SetContextToInterpMethodContextFrame(pRD->pCurrentContext);
28172819
if (pRD->pCurrentContext->ContextFlags & CONTEXT_EXCEPTION_ACTIVE)
28182820
{
@@ -2833,6 +2835,9 @@ void StackFrameIterator::ProcessCurrentFrame(void)
28332835
SetSP(pRD->pCurrentContext, m_interpExecMethodSP);
28342836
SetFP(pRD->pCurrentContext, m_interpExecMethodFP);
28352837
SetFirstArgReg(pRD->pCurrentContext, m_interpExecMethodFirstArgReg);
2838+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
2839+
pRD->SSP = m_interpExecMethodSSP;
2840+
#endif
28362841
SyncRegDisplayToCurrentContext(pRD);
28372842
}
28382843
}
@@ -2844,6 +2849,9 @@ void StackFrameIterator::ProcessCurrentFrame(void)
28442849
m_interpExecMethodSP = GetSP(pRD->pCurrentContext);
28452850
m_interpExecMethodFP = GetFP(pRD->pCurrentContext);
28462851
m_interpExecMethodFirstArgReg = GetFirstArgReg(pRD->pCurrentContext);
2852+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
2853+
m_interpExecMethodSSP = pRD->SSP;
2854+
#endif
28472855
}
28482856
}
28492857
#endif // FEATURE_INTERPRETER

src/coreclr/vm/stackwalk.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,9 @@ class StackFrameIterator
744744
TADDR m_interpExecMethodSP;
745745
TADDR m_interpExecMethodFP;
746746
TADDR m_interpExecMethodFirstArgReg;
747+
#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
748+
TADDR m_interpExecMethodSSP;
749+
#endif // TARGET_AMD64 && TARGET_WINDOWS
747750
#endif // FEATURE_INTERPRETER
748751

749752
#if defined(RECORD_RESUMABLE_FRAME_SP)

0 commit comments

Comments
 (0)