From 9ae216b8b01a6a2135b9afa000bd5de6357a1184 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sat, 23 May 2026 01:59:46 +0200 Subject: [PATCH 01/11] Resume after catch for interpreter via native EH This changes the way resume after catch in interpreted code works. It removes the need to use libunwind and uses C++ exception handling to unwind the stack. This is basically the same way as we use for WASM, except for some extra handling of cases when the ResumeAfterCatchException would flow into managed frames. In those cases, we skip the managed frames using the explicit frames. --- src/coreclr/vm/amd64/umthunkstub.S | 2 +- src/coreclr/vm/arm64/asmhelpers.S | 2 +- src/coreclr/vm/arm64/stubs.cpp | 2 +- src/coreclr/vm/dllimport.cpp | 2 + src/coreclr/vm/eetwain.cpp | 36 +--------------- src/coreclr/vm/excep.cpp | 40 ----------------- src/coreclr/vm/exceptionhandling.cpp | 57 +++++++++++++++++++++++++ src/coreclr/vm/exceptionhandling.h | 5 +++ src/coreclr/vm/exceptmacros.h | 40 ++++++++++++++++- src/coreclr/vm/frames.cpp | 2 +- src/coreclr/vm/frames.h | 17 -------- src/coreclr/vm/gchelpers.cpp | 2 + src/coreclr/vm/i386/umthunkstub.S | 2 +- src/coreclr/vm/interpexec.cpp | 14 +----- src/coreclr/vm/interpexec.h | 1 - src/coreclr/vm/jithelpers.cpp | 7 +++ src/coreclr/vm/loongarch64/asmhelpers.S | 2 +- src/coreclr/vm/prestub.cpp | 18 +++++--- src/coreclr/vm/qcall.h | 4 +- src/coreclr/vm/riscv64/asmhelpers.S | 2 +- src/coreclr/vm/virtualcallstub.cpp | 8 ++++ 21 files changed, 142 insertions(+), 123 deletions(-) diff --git a/src/coreclr/vm/amd64/umthunkstub.S b/src/coreclr/vm/amd64/umthunkstub.S index 8855d710570993..c0f5112be65b82 100644 --- a/src/coreclr/vm/amd64/umthunkstub.S +++ b/src/coreclr/vm/amd64/umthunkstub.S @@ -8,7 +8,7 @@ // // METHODDESC_REGISTER: UMEntryThunkData* // -NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix +NESTED_ENTRY TheUMEntryPrestub, _TEXT, NoHandler PUSH_ARGUMENT_REGISTERS // +8 for alignment alloc_stack (SIZEOF_MAX_FP_ARG_SPILL + 8) diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index b60f2a118be155..ac595987fc200b 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -83,7 +83,7 @@ LEAF_END ThePreStubPatch, _TEXT // // x12 = UMEntryThunkData* // -NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix +NESTED_ENTRY TheUMEntryPrestub, _TEXT, NoHandler // Save arguments and return address PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -224 diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 89e9c3f8b45727..444252aaa36c57 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -406,7 +406,7 @@ void InlinedCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateF pRD->pCurrentContextPointers->Fp = (DWORD64 *)&m_pCalleeSavedFP; #ifdef FEATURE_INTERPRETER - if ((m_Next != FRAME_TOP) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) + if ((m_Next != FRAME_TOP) && (m_Next != NULL) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) { // If the next frame is an interpreter frame, we also need to set the first argument register to point to the interpreter frame. SetFirstArgReg(pRD->pCurrentContext, dac_cast(m_Next)); diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 830cc01e966fa1..99b56382bdf81e 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -6016,6 +6016,7 @@ EXTERN_C void* PInvokeImportWorker(PInvokeMethodDesc* pMD) } CONTRACTL_END; + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(GetThread()->GetFrame()); INSTALL_MANAGED_EXCEPTION_DISPATCHER; // this function is called by CLR to native assembly stubs which are called by // managed code as a result, we need an unwind and continue handler to translate @@ -6026,6 +6027,7 @@ EXTERN_C void* PInvokeImportWorker(PInvokeMethodDesc* pMD) UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; return pMD->GetPInvokeTarget(); } diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 12261ea5d4d492..4812fadd44d762 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -1910,41 +1910,7 @@ void InterpreterCodeManager::ResumeAfterCatch(CONTEXT *pContext, size_t targetSS { TADDR resumeSP = GetSP(pContext); TADDR resumeIP = GetIP(pContext); -#ifdef TARGET_WASM - throw ResumeAfterCatchException(resumeSP, resumeIP); -#else - Thread *pThread = GetThread(); - InterpreterFrame * pInterpreterFrame = (InterpreterFrame*)pThread->GetFrame(); - - ClrCaptureContext(pContext); - - TADDR targetSP = pInterpreterFrame->GetInterpExecMethodSP(); - - // We are resuming in interpreter frame. So we need to skip all native, JIT and AOT generated frames until we reach - // the resumeSP - do - { - if (ExecutionManager::IsManagedCode(GetIP(pContext))) - { - // JIT / AOT generated managed code - Thread::VirtualUnwindCallFrame(pContext); - } - else - { -#ifdef TARGET_UNIX - PAL_VirtualUnwind(pContext); -#else - Thread::VirtualUnwindCallFrame(pContext); -#endif - } - } - while (GetSP(pContext) != targetSP); - -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) - targetSSP = pInterpreterFrame->GetInterpExecMethodSSP(); -#endif - ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, targetSSP, resumeSP, resumeIP); -#endif // TARGET_WASM + ThrowResumeAfterCatchException(resumeSP, resumeIP); } #if defined(HOST_AMD64) && defined(HOST_WINDOWS) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index fafc3bc5219aad..511d1a790d78f5 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -2109,13 +2109,11 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable) GCPROTECT_END(); } -#ifdef TARGET_WASM EXCEPTION_DISPOSITION SetTargetFrame(PAL_SEHException& ex, UINT_PTR targetSP) { ex.TargetFrameSp = targetSP; return EXCEPTION_CONTINUE_SEARCH; } -#endif // TARGET_WASM VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object *exceptionObj, UINT_PTR targetSP) { @@ -2130,7 +2128,6 @@ VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object * // On WASM, the exception needs to keep propagating until it reaches the target frame. On other platforms, // this is ensured by unwinding stack up to the target frame before propagating the exception. This // difference is due to the fact that on WASM we don't have native stack unwinding support. -#ifdef TARGET_WASM struct Param { Object *exceptionObj; @@ -2140,17 +2137,12 @@ VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object * PAL_TRY(Param *, pParam, ¶m) { OBJECTREF throwable = ObjectToOBJECTREF(pParam->exceptionObj); -#else - OBJECTREF throwable = ObjectToOBJECTREF(exceptionObj); -#endif // TARGET_WASM RealCOMPlusThrowWorker(throwable); -#ifdef TARGET_WASM } PAL_EXCEPT(SetTargetFrame(ex, __param->targetSP)) { } PAL_ENDTRY -#endif // TARGET_WASM UNREACHABLE(); } @@ -6708,38 +6700,6 @@ void ThrowResumeAfterCatchException(TADDR resumeSP, TADDR resumeIP) throw ResumeAfterCatchException(resumeSP, resumeIP); } -VOID DECLSPEC_NORETURN UnwindAndContinueResumeAfterCatch(TADDR resumeSP, TADDR resumeIP) -{ - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_TRIGGERS; - STATIC_CONTRACT_MODE_ANY; - - CONTEXT context; - ClrCaptureContext(&context); - - // Unwind to the caller of the Ex.RhThrowEx / Ex.RhThrowHwEx - Thread::VirtualUnwindToFirstManagedCallFrame(&context); - -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) - size_t targetSSP = GetSSPForFrameOnCurrentStack(GetIP(&context)); -#else - size_t targetSSP = 0; -#endif - - // Skip all managed frames upto a native frame - while (ExecutionManager::IsManagedCode(GetIP(&context))) - { - Thread::VirtualUnwindCallFrame(&context); -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) - if (targetSSP != 0) - { - targetSSP += sizeof(size_t); - } -#endif - } - - ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, &context, targetSSP, resumeSP, resumeIP); -} #endif // FEATURE_INTERPRETER thread_local DWORD t_dwCurrentExceptionCode; diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 8df1522022fbaf..73a3fbe998bd5e 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -950,6 +950,61 @@ static VOID UpdateContextForPropagationCallback( UpdateContextForPropagationCallback(ex.ManagedToNativeExceptionCallback, ex.ManagedToNativeExceptionCallbackContext, startContext); } +#ifndef HOST_WASM + +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext) +{ +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + TADDR targetSSP = GetSSPForFrameOnCurrentStack(GetIP(pContext)); +#else + TADDR targetSSP = 0; +#endif + + while (ExecutionManager::IsManagedCode(GetIP(pContext))) + { + Thread::VirtualUnwindCallFrame(pContext); +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + targetSSP += 8; +#endif + } + + TADDR resumeSP; + TADDR resumeIP; + ex.GetResumeContext(&resumeSP, &resumeIP); + _ASSERTE(resumeSP != 0 && resumeIP != 0); + + ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, targetSSP, resumeSP, resumeIP); +} + +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame) +{ + // The frame should have been popped already + _ASSERTE(pFrame->PtrNextFrame() == NULL); + + REGDISPLAY rd = {}; + T_CONTEXT context = {}; + context.ContextFlags = CONTEXT_FULL; + FillRegDisplay(&rd, &context); + pFrame->UpdateRegDisplay(&rd, FALSE /* updateFloats */); + + if (!ExecutionManager::IsManagedCode(GetIP(rd.pCurrentContext))) + { + // Native caller (interpreter stubs) + throw; + } + + EECodeInfo codeInfo(GetIP(rd.pCurrentContext)); + if (codeInfo.IsInterpretedCode()) + { + // PInvoke called from the interpreted code + throw; + } + + RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, rd.pCurrentContext); +} + +#endif // HOST_WASM + extern void* g_hostingApiReturnAddress; VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException) @@ -1447,6 +1502,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) exInfo.TakeExceptionPointersOwnership(ex); } + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exInfo.m_ptrs.ContextRecord); // m_exception is GC-reported via ExInfo chain scanning in ScanStackRoots. // Do NOT also GCPROTECT it - reporting the same location twice corrupts // the GC's relocation logic (see clr-code-guide.md §2.1.5). @@ -1458,6 +1514,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) throwHwEx.InvokeDirect(exceptionCode, &exInfo); DispatchExSecondPass(&exInfo); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; UNREACHABLE(); } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index f060df53dd53d4..ea281decf812b0 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -87,6 +87,11 @@ class ResumeAfterCatchException *pResumeIP = m_resumeIP; } }; + +#ifndef HOST_WASM +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext); +#endif // HOST_WASM + #endif // FEATURE_INTERPRETER void DECLSPEC_NORETURN ExecuteFunctionBelowContext(PCODE functionPtr, CONTEXT *pContext, size_t targetSSP, size_t arg1 = 0, size_t arg2 = 0); diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index c72b673c898ad5..aa81cd5e97701a 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -206,8 +206,44 @@ void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pE VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException, bool nativeRethrow); #ifdef FEATURE_INTERPRETER -VOID DECLSPEC_NORETURN UnwindAndContinueResumeAfterCatch(TADDR resumeSP, TADDR resumeIP); -#endif // FEATURE_INTERPRETER +class ResumeAfterCatchException; +#endif + +#if defined(FEATURE_INTERPRETER) && !defined(HOST_WASM) +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame); + +// Install / uninstall handler at a native to managed code boundary. + +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext) \ + CONTEXT *__pResumeAfterCatchContext = pContext; \ + try \ + { + +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pFrame) \ + Frame *__pResumeAfterCatchFrame = pFrame; \ + try \ + { + +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT \ + } \ + catch (const ResumeAfterCatchException& ex) \ + { \ + RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, __pResumeAfterCatchContext); \ + } + +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME \ + } \ + catch (const ResumeAfterCatchException& ex) \ + { \ + RethrowResumeAfterCatchException(ex, __pResumeAfterCatchFrame); \ + } + +#else // FEATURE_INTERPRETER && !HOST_WASM +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pFrame) +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext) +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT +#endif // FEATURE_INTERPRETER && !HOST_WASM #ifdef TARGET_UNIX VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException); diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 53ddec159d3714..473e8e7a7df8fe 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -687,7 +687,7 @@ void InlinedCallFrame::UpdateFloatingPointRegisters_Impl(const PREGDISPLAY pRD, if (IsInInterpreter()) { InterpreterFrame *pInterpreterFrame = (InterpreterFrame *)m_Next; - pInterpreterFrame->UpdateFloatingPointRegisters(pRD, pInterpreterFrame->GetInterpExecMethodSP()); + pInterpreterFrame->UpdateFloatingPointRegisters(pRD, 0 /* unused for interpreter frame*/); pInterpreterFrame->SetContextToInterpMethodContextFrame(pRD->pCurrentContext); return; } diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 74a3c131e8e2e7..e5a55e6585074b 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2383,20 +2383,6 @@ class InterpreterFrame : public FramedMethodFrame } #endif // HOST_AMD64 && HOST_WINDOWS -#ifndef TARGET_WASM - void SetInterpExecMethodSP(TADDR sp) - { - LIMITED_METHOD_CONTRACT; - m_SP = sp; - } - - TADDR GetInterpExecMethodSP() - { - LIMITED_METHOD_CONTRACT; - return m_SP; - } -#endif // TARGET_WASM - void SetIsFaulting(bool isFaulting) { LIMITED_METHOD_CONTRACT; @@ -2441,9 +2427,6 @@ class InterpreterFrame : public FramedMethodFrame // Saved SSP of the InterpExecMethod for resuming after catch into interpreter frames. TADDR m_SSP; #endif // HOST_AMD64 && HOST_WINDOWS -#ifndef TARGET_WASM - TADDR m_SP; -#endif // TARGET_WASM PTR_Object m_continuation; friend struct cdac_data; diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index c9e3020948fe3e..ba24ad29f6c5bd 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -180,6 +180,7 @@ EXTERN_C void RhExceptionHandling_FailedAllocation_Helper(MethodTable* pMT, bool pFrame->Push(CURRENT_THREAD); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(&frame); INSTALL_MANAGED_EXCEPTION_DISPATCHER; INSTALL_UNWIND_AND_CONTINUE_HANDLER; @@ -191,6 +192,7 @@ EXTERN_C void RhExceptionHandling_FailedAllocation_Helper(MethodTable* pMT, bool UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; pFrame->Pop(CURRENT_THREAD); } diff --git a/src/coreclr/vm/i386/umthunkstub.S b/src/coreclr/vm/i386/umthunkstub.S index 6dc63a7702530b..8130537c465aff 100644 --- a/src/coreclr/vm/i386/umthunkstub.S +++ b/src/coreclr/vm/i386/umthunkstub.S @@ -8,7 +8,7 @@ // // eax = UMEntryThunkData* // -NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix +NESTED_ENTRY TheUMEntryPrestub, _TEXT, NoHandler #define STK_ALIGN_PADDING 8 sub esp, STK_ALIGN_PADDING push eax // UMEntryThunkData* diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4ef4ad40c9617f..37e9d6e841c899 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -194,14 +194,6 @@ static size_t CreateDispatchTokenForMethod(MethodDesc* pMD) } } -#ifdef TARGET_WASM -// Unused on WASM -#define SAVE_THE_LOWEST_SP do {} while (0) -#else -// Save the lowest SP in the current method so that we can identify it by that during stackwalk -#define SAVE_THE_LOWEST_SP pInterpreterFrame->SetInterpExecMethodSP((TADDR)GetCurrentSP()) -#endif // !TARGET_WASM - // Call invoker helpers provided by platform. void InvokeManagedMethod(ManagedMethodParam *pParam); void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet, PCODE callTarget); @@ -1382,8 +1374,6 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr MethodDesc* targetMethod; uint32_t opcode; - SAVE_THE_LOWEST_SP; - MAIN_LOOP: try { @@ -3342,7 +3332,6 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); pChildFrame->pNext = NULL; pFrame->pNext = pChildFrame; - SAVE_THE_LOWEST_SP; } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, LOCAL_VAR_ADDR(callArgsOffset, int8_t)); pFrame = pChildFrame; @@ -3443,7 +3432,6 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); pChildFrame->pNext = NULL; pFrame->pNext = pChildFrame; - SAVE_THE_LOWEST_SP; } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, callArgsAddress); pFrame = pChildFrame; @@ -4277,7 +4265,6 @@ do \ pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); pChildFrame->pNext = NULL; pFrame->pNext = pChildFrame; - SAVE_THE_LOWEST_SP; } // Set the frame to the same values as the caller frame. pChildFrame->ReInit(pFrame, pFrame->startIp, pFrame->pRetVal, pFrame->pStack); @@ -4637,6 +4624,7 @@ do \ } catch (const ResumeAfterCatchException& ex) { + GCX_COOP_NO_DTOR(); TADDR resumeSP; TADDR resumeIP; ex.GetResumeContext(&resumeSP, &resumeIP); diff --git a/src/coreclr/vm/interpexec.h b/src/coreclr/vm/interpexec.h index 1192ddadf0cdbe..0fca7879b73189 100644 --- a/src/coreclr/vm/interpexec.h +++ b/src/coreclr/vm/interpexec.h @@ -119,7 +119,6 @@ extern "C" void ExecuteInterpretedMethodFromUnmanaged(MethodDesc* pMD, int8_t* a CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod); // Arguments are bundled in a struct to force register passing on ARM32. -// This ensures the current SP value saved by the SAVE_THE_LOWEST_SP into the InterpreterFrame precisely matches the SP that stack walking reports for the InterpExecMethod. // Passing arguments on stack on ARM32 would result in reporting SP after the arguments were pushed, which is different. struct ManagedMethodParam { diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a71a92fc5519e4..48b00c0b39bcaa 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -802,7 +802,10 @@ EXTERN_C HCIMPL2(void, IL_Throw_Impl, Object* obj, TransitionBlock* transitionB DispatchManagedException(kNullReferenceException); NormalizeThrownObject(&oref); + + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); DispatchManagedException(oref, exceptionFrame.GetContext()); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; FC_CAN_TRIGGER_GC_END(); UNREACHABLE(); @@ -830,7 +833,9 @@ EXTERN_C HCIMPL1(void, IL_Rethrow_Impl, TransitionBlock* transitionBlock) FC_CAN_TRIGGER_GC(); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); DispatchRethrownManagedException(exceptionFrame.GetContext()); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; FC_CAN_TRIGGER_GC_END(); UNREACHABLE(); @@ -862,7 +867,9 @@ EXTERN_C HCIMPL2(void, IL_ThrowExact_Impl, Object* obj, TransitionBlock* transi FC_CAN_TRIGGER_GC(); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); DispatchManagedException(oref, exceptionFrame.GetContext(), NULL, ExKind::RethrowFlag); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; FC_CAN_TRIGGER_GC_END(); UNREACHABLE(); diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S index 3b47125fa8dfdb..95532fe618825d 100644 --- a/src/coreclr/vm/loongarch64/asmhelpers.S +++ b/src/coreclr/vm/loongarch64/asmhelpers.S @@ -453,7 +453,7 @@ NESTED_END StubDispatchFixupStub, _TEXT // // $t2 = UMEntryThunkData* // -NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix +NESTED_ENTRY TheUMEntryPrestub, _TEXT, NoHandler // Save arguments and return address // $fp,$ra diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 67836befe68743..a2ca3838c8220e 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1876,6 +1876,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method { bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(&frame); INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; @@ -1930,6 +1931,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; } EX_CATCH { @@ -2038,23 +2040,25 @@ extern "C" void* STDCALL ExecuteInterpretedMethod(TransitionBlock* pTransitionBl frames.interpMethodContextFrame.pStack = sp; frames.interpMethodContextFrame.pRetVal = (retBuff != NULL) ? (int8_t*)retBuff : sp; + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(&frames.interpreterFrame); InterpExecMethod(&frames.interpreterFrame, &frames.interpMethodContextFrame, threadContext); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; ArgumentRegisters *pArgumentRegisters = (ArgumentRegisters*)(((uint8_t*)pTransitionBlock) + TransitionBlock::GetOffsetOfArgumentRegisters()); - #if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) pArgumentRegisters->RCX = (INT_PTR)*frames.interpreterFrame.GetContinuationPtr(); - #elif defined(TARGET_ARM64) +#elif defined(TARGET_ARM64) pArgumentRegisters->x[2] = (INT64)*frames.interpreterFrame.GetContinuationPtr(); - #elif defined(TARGET_ARM) +#elif defined(TARGET_ARM) pArgumentRegisters->r[2] = (INT64)*frames.interpreterFrame.GetContinuationPtr(); - #elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) pArgumentRegisters->a[2] = (INT64)*frames.interpreterFrame.GetContinuationPtr(); - #elif defined(TARGET_WASM) +#elif defined(TARGET_WASM) // We do not yet have an ABI for WebAssembly native code to handle here. - #else +#else #error Unsupported architecture - #endif +#endif frames.interpreterFrame.Pop(); diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index 079f93780d40df..a64bf4a2d7836c 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -110,12 +110,14 @@ #endif // !TARGET_UNIX #define BEGIN_QCALL \ + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(GetThread()->GetFrame()) \ INSTALL_MANAGED_EXCEPTION_DISPATCHER \ INSTALL_UNWIND_AND_CONTINUE_HANDLER #define END_QCALL \ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER \ - UNINSTALL_MANAGED_EXCEPTION_DISPATCHER + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER \ + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME #define QCALL_CHECK \ THROWS; \ diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S index 771dab4df4402e..e2afa60cee0e6d 100644 --- a/src/coreclr/vm/riscv64/asmhelpers.S +++ b/src/coreclr/vm/riscv64/asmhelpers.S @@ -335,7 +335,7 @@ C_FUNC(ThePreStubPatchLabel): ret LEAF_END ThePreStubPatch, _TEXT -NESTED_ENTRY TheUMEntryPrestub, _TEXT, UnhandledExceptionHandlerUnix +NESTED_ENTRY TheUMEntryPrestub, _TEXT, NoHandler // Save arguments and return address PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, 0xa0 //PROLOG_SAVE_REG gp, 16 diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 81320ed6c38a1f..9d409b7088c7ed 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1613,11 +1613,13 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, if (pObj == NULL) { pSDFrame->SetForNullReferenceException(); pSDFrame->Push(CURRENT_THREAD); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pSDFrame); INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; COMPlusThrow(kNullReferenceException); UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; _ASSERTE(!"Throw returned"); } @@ -1653,6 +1655,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, pSDFrame->SetRepresentativeSlot(pRepresentativeMT, representativeToken.GetSlotNumber()); pSDFrame->Push(CURRENT_THREAD); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pSDFrame); INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; @@ -1692,6 +1695,8 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; + pSDFrame->Pop(CURRENT_THREAD); return target; @@ -1739,6 +1744,7 @@ PCODE VSD_ResolveWorkerForInterfaceLookupSlot(TransitionBlock * pTransitionBlock pSDFrame->Push(CURRENT_THREAD); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pSDFrame); INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; @@ -1777,6 +1783,8 @@ PCODE VSD_ResolveWorkerForInterfaceLookupSlot(TransitionBlock * pTransitionBlock UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME; + pSDFrame->Pop(CURRENT_THREAD); return target; From 258c8641d050281ecf1c38e41319c7cfa6b71882 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 29 May 2026 14:14:08 +0200 Subject: [PATCH 02/11] Fix Linux x64 --- src/coreclr/vm/amd64/cgenamd64.cpp | 2 +- src/coreclr/vm/exceptionhandling.cpp | 2 +- src/coreclr/vm/wasm/helpers.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 8db2219309785d..748da07db2f898 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -210,7 +210,7 @@ void InlinedCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateF SyncRegDisplayToCurrentContext(pRD); #ifdef FEATURE_INTERPRETER - if ((m_Next != FRAME_TOP) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) + if ((m_Next != FRAME_TOP) && (m_Next != NULL) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) { // If the next frame is an interpreter frame, we also need to set the first argument register to point to the interpreter frame. SetFirstArgReg(pRD->pCurrentContext, dac_cast(m_Next)); diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 73a3fbe998bd5e..4cf6bdbff68dc6 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1502,7 +1502,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) exInfo.TakeExceptionPointersOwnership(ex); } - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exInfo.m_ptrs.ContextRecord); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext()); // m_exception is GC-reported via ExInfo chain scanning in ScanStackRoots. // Do NOT also GCPROTECT it - reporting the same location twice corrupts // the GC's relocation logic (see clr-code-guide.md §2.1.5). diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index f82a4826b37dec..182a52def23016 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -463,7 +463,7 @@ void InlinedCallFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateF SyncRegDisplayToCurrentContext(pRD); #ifdef FEATURE_INTERPRETER - if ((m_Next != FRAME_TOP) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) + if ((m_Next != FRAME_TOP) && (m_Next != NULL) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) { // If the next frame is an interpreter frame, we also need to set the first argument register to point to the interpreter frame. SetFirstArgReg(pRD->pCurrentContext, dac_cast(m_Next)); From 70c9a7143c1597ee516acfa3bcf5703df70f3126 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 3 Jun 2026 00:03:15 +0200 Subject: [PATCH 03/11] Windows with CET shadow stack working now --- src/coreclr/vm/excep.cpp | 14 ++- src/coreclr/vm/exceptionhandling.cpp | 138 ++++++++++++++++----------- src/coreclr/vm/exceptionhandling.h | 2 +- src/coreclr/vm/exceptmacros.h | 55 +++++++---- src/coreclr/vm/interpexec.cpp | 17 +++- src/coreclr/vm/jithelpers.cpp | 7 +- 6 files changed, 149 insertions(+), 84 deletions(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 511d1a790d78f5..60cce47336a336 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -2109,11 +2109,13 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable) GCPROTECT_END(); } +#ifdef TARGET_WASM EXCEPTION_DISPOSITION SetTargetFrame(PAL_SEHException& ex, UINT_PTR targetSP) { ex.TargetFrameSp = targetSP; return EXCEPTION_CONTINUE_SEARCH; } +#endif // TARGET_WASM VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object *exceptionObj, UINT_PTR targetSP) { @@ -2125,6 +2127,7 @@ VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object * } CONTRACTL_END; +#ifdef TARGET_WASM // On WASM, the exception needs to keep propagating until it reaches the target frame. On other platforms, // this is ensured by unwinding stack up to the target frame before propagating the exception. This // difference is due to the fact that on WASM we don't have native stack unwinding support. @@ -2137,12 +2140,17 @@ VOID DECLSPEC_NORETURN __fastcall PropagateExceptionThroughNativeFrames(Object * PAL_TRY(Param *, pParam, ¶m) { OBJECTREF throwable = ObjectToOBJECTREF(pParam->exceptionObj); +#else + OBJECTREF throwable = ObjectToOBJECTREF(exceptionObj); +#endif // TARGET_WASM RealCOMPlusThrowWorker(throwable); +#ifdef TARGET_WASM } PAL_EXCEPT(SetTargetFrame(ex, __param->targetSP)) { } PAL_ENDTRY +#endif // TARGET_WASM UNREACHABLE(); } @@ -5613,6 +5621,7 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) } } + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext(), GetSSP(pContext)); // m_exception is GC-reported via ExInfo chain scanning in ScanStackRoots. // Do NOT also GCPROTECT it - reporting the same location twice corrupts // the GC's relocation logic (see clr-code-guide.md §2.1.5). @@ -5624,6 +5633,7 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) throwHwEx.InvokeDirect(exceptionCode, &exInfo); DispatchExSecondPass(&exInfo); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; UNREACHABLE(); } @@ -6690,10 +6700,6 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra } } -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) -size_t GetSSPForFrameOnCurrentStack(TADDR ip); -#endif // HOST_AMD64 && HOST_WINDOWS - #ifdef FEATURE_INTERPRETER void ThrowResumeAfterCatchException(TADDR resumeSP, TADDR resumeIP) { diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 4cf6bdbff68dc6..f59bce273fcdc3 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -653,8 +653,14 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, #endif else { + void *sp = (void*)GetSP(pContextRecord); + PopExplicitFrames(pThread, sp, NULL /* targetCallerSp */, false /* popGCFrames */); + ExInfo::PopExInfos(pThread, sp); + OBJECTREF oref = ExInfo::CreateThrowable(pExceptionRecord, FALSE); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContextRecord, GetSSP(pContextRecord)); DispatchManagedException(oref, pContextRecord, pExceptionRecord); + UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; } #endif // !HOST_UNIX @@ -950,61 +956,6 @@ static VOID UpdateContextForPropagationCallback( UpdateContextForPropagationCallback(ex.ManagedToNativeExceptionCallback, ex.ManagedToNativeExceptionCallbackContext, startContext); } -#ifndef HOST_WASM - -VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext) -{ -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) - TADDR targetSSP = GetSSPForFrameOnCurrentStack(GetIP(pContext)); -#else - TADDR targetSSP = 0; -#endif - - while (ExecutionManager::IsManagedCode(GetIP(pContext))) - { - Thread::VirtualUnwindCallFrame(pContext); -#if defined(HOST_AMD64) && defined(HOST_WINDOWS) - targetSSP += 8; -#endif - } - - TADDR resumeSP; - TADDR resumeIP; - ex.GetResumeContext(&resumeSP, &resumeIP); - _ASSERTE(resumeSP != 0 && resumeIP != 0); - - ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, targetSSP, resumeSP, resumeIP); -} - -VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame) -{ - // The frame should have been popped already - _ASSERTE(pFrame->PtrNextFrame() == NULL); - - REGDISPLAY rd = {}; - T_CONTEXT context = {}; - context.ContextFlags = CONTEXT_FULL; - FillRegDisplay(&rd, &context); - pFrame->UpdateRegDisplay(&rd, FALSE /* updateFloats */); - - if (!ExecutionManager::IsManagedCode(GetIP(rd.pCurrentContext))) - { - // Native caller (interpreter stubs) - throw; - } - - EECodeInfo codeInfo(GetIP(rd.pCurrentContext)); - if (codeInfo.IsInterpretedCode()) - { - // PInvoke called from the interpreted code - throw; - } - - RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, rd.pCurrentContext); -} - -#endif // HOST_WASM - extern void* g_hostingApiReturnAddress; VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException) @@ -1502,7 +1453,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) exInfo.TakeExceptionPointersOwnership(ex); } - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext()); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext(), 0 /* SSP - no SSP support on Unix */); // m_exception is GC-reported via ExInfo chain scanning in ScanStackRoots. // Do NOT also GCPROTECT it - reporting the same location twice corrupts // the GC's relocation logic (see clr-code-guide.md §2.1.5). @@ -1564,6 +1515,80 @@ BOOL HandleHardwareException(PAL_SEHException* ex) #endif // TARGET_UNIX +#if defined(FEATURE_INTERPRETER) && !defined(HOST_WASM) + +// The ssp argument needs to match the pContext (SSP register value at that context) +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext, TADDR ssp) +{ +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + // Find precise SSP value. We cannot use the instruction pointer from the context here because for PInvoke frames it points to the return address + // of the JIT_PInvokeBegin call that is called before the actual target function. + if (ssp != 0) + { + while (!ExecutionManager::IsManagedCode(*(PCODE*)(ssp - 8))) + { + ssp += 8; + } + } +#endif + + while (ExecutionManager::IsManagedCode(GetIP(pContext))) + { + Thread::VirtualUnwindCallFrame(pContext); +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) + if (ssp != 0) + { + ssp += 8; + } +#endif + } + + TADDR resumeSP; + TADDR resumeIP; + ex.GetResumeContext(&resumeSP, &resumeIP); + _ASSERTE(resumeSP != 0 && resumeIP != 0); + + ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, ssp, resumeSP, resumeIP); +} + +// The ssp argument is approximate (it can be the SSP value of several frames below the context extracted from the pFrame) +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame, TADDR ssp) +{ + // The frame should have been popped already + _ASSERTE(pFrame->PtrNextFrame() == NULL); + + EECodeInfo codeInfo(pFrame->GetReturnAddress()); + + if (!codeInfo.IsValid() || codeInfo.IsInterpretedCode()) + { + // Native caller - interpreter stubs or PInvoke called from the interpreted code + throw ex; + } + + REGDISPLAY rd = {}; + T_CONTEXT context = {}; +#if (defined(HOST_WINDOWS) && defined(HOST_AMD64)) || defined(TARGET_ARM64) + context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; +#else + context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; +#endif + + FillRegDisplay(&rd, &context); + pFrame->UpdateRegDisplay(&rd, FALSE /* updateFloats */); + +#if defined(HOST_WINDOWS) && defined(HOST_AMD64) + // Initialize FP control/status so that the context can be used for resuming execution + rd.pCurrentContext->FltSave.ControlWord = 0x27F; // Default x87 control word + rd.pCurrentContext->FltSave.MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) + rd.pCurrentContext->FltSave.MxCsr_Mask = 0x1FFF; // MXCSR mask + rd.pCurrentContext->MxCsr = 0x1F80; // Default MXCSR value (all exceptions masked) +#endif // HOST_WINDOWS && HOST_AMD64 + + RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, rd.pCurrentContext, ssp); +} + +#endif // FEATURE_INTERPRETER && !HOST_WASM + void FirstChanceExceptionNotification() { #ifdef TARGET_WINDOWS @@ -3057,6 +3082,7 @@ void ExecuteFunctionBelowContext(PCODE functionPtr, CONTEXT *pContext, size_t ta if (targetSSP != 0) { targetSSP -= sizeof(size_t); + _ASSERTE(*(ULONG64*)targetSSP == pContext->Rip); } #endif // HOST_WINDOWS SetSP(pContext, targetSp - 8); diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index ea281decf812b0..1c8d72f03785ce 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -89,7 +89,7 @@ class ResumeAfterCatchException }; #ifndef HOST_WASM -VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext); +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchExceptionSkipManagedFrames(const ResumeAfterCatchException& ex, CONTEXT *pContext, TADDR ssp); #endif // HOST_WASM #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index aa81cd5e97701a..2400367d5b8d5c 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -210,37 +210,58 @@ class ResumeAfterCatchException; #endif #if defined(FEATURE_INTERPRETER) && !defined(HOST_WASM) -VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame); +VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchException& ex, Frame *pFrame, TADDR ssp); + +#if defined(HOST_AMD64) && defined(HOST_WINDOWS) +#define READ_SSP() _rdsspq() +#else +#define READ_SSP() 0 +#endif // Install / uninstall handler at a native to managed code boundary. -#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext) \ - CONTEXT *__pResumeAfterCatchContext = pContext; \ - try \ +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext, ssp) \ + CONTEXT *__pResumeAfterCatchContext = pContext; \ + TADDR __pResumeAfterCatchSSP = ssp; \ + TADDR __resumeSP = 0, __resumeIP = 0; \ + try \ { #define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pFrame) \ - Frame *__pResumeAfterCatchFrame = pFrame; \ - try \ + Frame *__pResumeAfterCatchFrame = pFrame; \ + TADDR __pResumeAfterCatchSSP = READ_SSP(); \ + TADDR __resumeSP = 0, __resumeIP = 0; \ + try \ { -#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT \ - } \ - catch (const ResumeAfterCatchException& ex) \ - { \ - RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, __pResumeAfterCatchContext); \ +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT \ + } \ + catch (const ResumeAfterCatchException& ex) \ + { \ + ex.GetResumeContext(&__resumeSP, &__resumeIP); \ + } \ + if (__resumeSP != 0) \ + { \ + ResumeAfterCatchException ex(__resumeSP, __resumeIP); \ + RethrowResumeAfterCatchExceptionSkipManagedFrames(ex, __pResumeAfterCatchContext, __pResumeAfterCatchSSP); \ } -#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME \ - } \ - catch (const ResumeAfterCatchException& ex) \ - { \ - RethrowResumeAfterCatchException(ex, __pResumeAfterCatchFrame); \ + +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME \ + } \ + catch (const ResumeAfterCatchException& ex) \ + { \ + ex.GetResumeContext(&__resumeSP, &__resumeIP); \ + } \ + if (__resumeSP != 0) \ + { \ + ResumeAfterCatchException ex(__resumeSP, __resumeIP); \ + RethrowResumeAfterCatchException(ex, __pResumeAfterCatchFrame, __pResumeAfterCatchSSP); \ } #else // FEATURE_INTERPRETER && !HOST_WASM #define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME(pFrame) -#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext) +#define INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContext, ssp) #define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME #define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT #endif // FEATURE_INTERPRETER && !HOST_WASM diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 37e9d6e841c899..43d8da9f7a8b2c 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -1325,6 +1325,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } CONTRACTL_END; + TADDR resumeSP; + TADDR resumeIP; + #if defined(HOST_AMD64) && defined(HOST_WINDOWS) pInterpreterFrame->SetInterpExecMethodSSP((TADDR)_rdsspq()); #endif // HOST_AMD64 && HOST_WINDOWS @@ -4625,8 +4628,6 @@ do \ catch (const ResumeAfterCatchException& ex) { GCX_COOP_NO_DTOR(); - TADDR resumeSP; - TADDR resumeIP; ex.GetResumeContext(&resumeSP, &resumeIP); _ASSERTE(resumeSP != 0 && resumeIP != 0); @@ -4640,7 +4641,12 @@ do \ // sequences of interpreted frames without any AOTed/JITted frames in between. In such case, the topmost native frame // the ResumeAfterCatchException is thrown from may not be the one that corresponds to the target interpreted frame. // Thus, we need to rethrow it to let it propagate further. +#ifdef HOST_WINDOWS + // On Windows, rethrow the exception out of the catch block to work around a Windows bug in shadow stack unwinding + goto RETHROW_RESUME_AFTER_CATCH; +#else // HOST_WINDOWS throw; +#endif // HOST_WINDOWS } pThreadContext->frameDataAllocator.PopInfo(pFrame); pFrame->ip = 0; @@ -4692,6 +4698,13 @@ do \ } pThreadContext->pStackPointer = pFrame->pStack; + return; + +#ifdef HOST_WINDOWS +RETHROW_RESUME_AFTER_CATCH: + // Rethrow the exception to let it propagate to the correct resume frame + ThrowResumeAfterCatchException(resumeSP, resumeIP); +#endif // HOST_WINDOWS } #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 48b00c0b39bcaa..95bcbefb4dbfea 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -802,8 +802,7 @@ EXTERN_C HCIMPL2(void, IL_Throw_Impl, Object* obj, TransitionBlock* transitionB DispatchManagedException(kNullReferenceException); NormalizeThrownObject(&oref); - - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext(), READ_SSP()); DispatchManagedException(oref, exceptionFrame.GetContext()); UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; @@ -833,7 +832,7 @@ EXTERN_C HCIMPL1(void, IL_Rethrow_Impl, TransitionBlock* transitionBlock) FC_CAN_TRIGGER_GC(); - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext(), READ_SSP()); DispatchRethrownManagedException(exceptionFrame.GetContext()); UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; @@ -867,7 +866,7 @@ EXTERN_C HCIMPL2(void, IL_ThrowExact_Impl, Object* obj, TransitionBlock* transi FC_CAN_TRIGGER_GC(); - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext()); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(exceptionFrame.GetContext(), READ_SSP()); DispatchManagedException(oref, exceptionFrame.GetContext(), NULL, ExKind::RethrowFlag); UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; From e882465d29a984c0b0f98252096c0b9d5057ba7c Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 3 Jun 2026 18:46:20 +0200 Subject: [PATCH 04/11] Make Windows and Unix rethrowing in InterpExecMethod the same It is not worth differentiating them --- src/coreclr/vm/interpexec.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 43d8da9f7a8b2c..36659d2f6b21f8 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -4641,12 +4641,7 @@ do \ // sequences of interpreted frames without any AOTed/JITted frames in between. In such case, the topmost native frame // the ResumeAfterCatchException is thrown from may not be the one that corresponds to the target interpreted frame. // Thus, we need to rethrow it to let it propagate further. -#ifdef HOST_WINDOWS - // On Windows, rethrow the exception out of the catch block to work around a Windows bug in shadow stack unwinding goto RETHROW_RESUME_AFTER_CATCH; -#else // HOST_WINDOWS - throw; -#endif // HOST_WINDOWS } pThreadContext->frameDataAllocator.PopInfo(pFrame); pFrame->ip = 0; @@ -4700,11 +4695,9 @@ do \ pThreadContext->pStackPointer = pFrame->pStack; return; -#ifdef HOST_WINDOWS RETHROW_RESUME_AFTER_CATCH: // Rethrow the exception to let it propagate to the correct resume frame ThrowResumeAfterCatchException(resumeSP, resumeIP); -#endif // HOST_WINDOWS } #endif // FEATURE_INTERPRETER From d19ae6fb524effe6891c96eafc4aab95ea9ed249 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 3 Jun 2026 19:19:41 +0200 Subject: [PATCH 05/11] Fix GetSSP call on non-win-amd64 --- src/coreclr/vm/exceptionhandling.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index f59bce273fcdc3..8b40418287d42b 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -657,8 +657,14 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, PopExplicitFrames(pThread, sp, NULL /* targetCallerSp */, false /* popGCFrames */); ExInfo::PopExInfos(pThread, sp); +#if defined(HOST_WINDOWS) && defined(HOST_AMD64) + TADDR ssp = GetSSP(pContextRecord); +#else + TADDR ssp = 0; +#endif + OBJECTREF oref = ExInfo::CreateThrowable(pExceptionRecord, FALSE); - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContextRecord, GetSSP(pContextRecord)); + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(pContextRecord, ssp); DispatchManagedException(oref, pContextRecord, pExceptionRecord); UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT; } From eae84c3ae63b103ff26286d7935d8b3420abc340 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 3 Jun 2026 19:34:43 +0200 Subject: [PATCH 06/11] Fix another GetSSP call --- src/coreclr/vm/excep.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 60cce47336a336..8c2a69f34c0d3b 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -5621,7 +5621,13 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext) } } - INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext(), GetSSP(pContext)); +#if defined(HOST_WINDOWS) && defined(HOST_AMD64) + TADDR ssp = GetSSP(pContext); +#else + TADDR ssp = 0; +#endif + + INSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_CONTEXT(fef.GetExceptionContext(), ssp); // m_exception is GC-reported via ExInfo chain scanning in ScanStackRoots. // Do NOT also GCPROTECT it - reporting the same location twice corrupts // the GC's relocation logic (see clr-code-guide.md §2.1.5). From 8882e7e86f3c4b276ea7780fecc4e5212ce99980 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 4 Jun 2026 14:05:57 +0200 Subject: [PATCH 07/11] Add comment explaining why we don't rethrow from catch --- src/coreclr/vm/exceptmacros.h | 22 ++++++++++++---------- src/coreclr/vm/interpexec.cpp | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 2400367d5b8d5c..157a05dfd4edb4 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -238,6 +238,7 @@ VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchEx } \ catch (const ResumeAfterCatchException& ex) \ { \ + /* We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating */ \ ex.GetResumeContext(&__resumeSP, &__resumeIP); \ } \ if (__resumeSP != 0) \ @@ -247,16 +248,17 @@ VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchEx } -#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME \ - } \ - catch (const ResumeAfterCatchException& ex) \ - { \ - ex.GetResumeContext(&__resumeSP, &__resumeIP); \ - } \ - if (__resumeSP != 0) \ - { \ - ResumeAfterCatchException ex(__resumeSP, __resumeIP); \ - RethrowResumeAfterCatchException(ex, __pResumeAfterCatchFrame, __pResumeAfterCatchSSP); \ +#define UNINSTALL_RESUME_AFTER_CATCH_HANDLER_WITH_FRAME \ + } \ + catch (const ResumeAfterCatchException& ex) \ + { \ + /* We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating */ \ + ex.GetResumeContext(&__resumeSP, &__resumeIP); \ + } \ + if (__resumeSP != 0) \ + { \ + ResumeAfterCatchException ex(__resumeSP, __resumeIP); \ + RethrowResumeAfterCatchException(ex, __pResumeAfterCatchFrame, __pResumeAfterCatchSSP); \ } #else // FEATURE_INTERPRETER && !HOST_WASM diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 36659d2f6b21f8..dfaf06e01ae0bb 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -4641,6 +4641,7 @@ do \ // sequences of interpreted frames without any AOTed/JITted frames in between. In such case, the topmost native frame // the ResumeAfterCatchException is thrown from may not be the one that corresponds to the target interpreted frame. // Thus, we need to rethrow it to let it propagate further. + // We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating. goto RETHROW_RESUME_AFTER_CATCH; } pThreadContext->frameDataAllocator.PopInfo(pFrame); From cfd1ca44b8f11c5f22f1a40c4c46369443863efb Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 5 Jun 2026 11:17:09 +0200 Subject: [PATCH 08/11] Fix assert when resuming through ThePreStubWorker --- src/coreclr/vm/prestub.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a2ca3838c8220e..8b32b2e3d14b07 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1936,8 +1936,11 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method EX_CATCH { OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle(); - _ASSERTE(ohThrowable); - StackTraceInfo::AppendElement(ObjectFromHandle(ohThrowable), 0, (UINT_PTR)pTransitionBlock, pMD, NULL); + // ohThrowable can be NULL when we've caught the ResumeAfterCatchException + if (ohThrowable != NULL) + { + StackTraceInfo::AppendElement(ObjectFromHandle(ohThrowable), 0, (UINT_PTR)pTransitionBlock, pMD, NULL); + } EX_RETHROW; } EX_END_CATCH From eac79adeb33d1647540d5ba14bf645eeea16d267 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 5 Jun 2026 17:29:31 +0200 Subject: [PATCH 09/11] Fix floating point updating in resuming after catch --- src/coreclr/vm/exceptionhandling.cpp | 5 ++++- src/coreclr/vm/frames.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 8b40418287d42b..a4bdb626a33596 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1574,13 +1574,16 @@ VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchEx REGDISPLAY rd = {}; T_CONTEXT context = {}; #if (defined(HOST_WINDOWS) && defined(HOST_AMD64)) || defined(TARGET_ARM64) + constexpr BOOL updateFloats = TRUE; context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + RtlCaptureContext(&context); #else + constexpr BOOL updateFloats = FALSE; context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; #endif FillRegDisplay(&rd, &context); - pFrame->UpdateRegDisplay(&rd, FALSE /* updateFloats */); + pFrame->UpdateRegDisplay(&rd, updateFloats); #if defined(HOST_WINDOWS) && defined(HOST_AMD64) // Initialize FP control/status so that the context can be used for resuming execution diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 473e8e7a7df8fe..c69962bdb05509 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -713,7 +713,7 @@ void InlinedCallFrame::UpdateFloatingPointRegisters_Impl(const PREGDISPLAY pRD, BOOL InlinedCallFrame::IsInInterpreter() { PTR_InterpreterFrame pInterpreterFrame = NULL; - if ((m_Next != FRAME_TOP) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) + if ((m_Next != FRAME_TOP) && (m_Next != NULL) && (m_Next->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame)) { PTR_InterpreterFrame pInterpreterFrame = (PTR_InterpreterFrame)m_Next; // The interpreter frame is in the interpreter when its top method context frame matches the m_pCallSiteSP From 7724e9b1e8b6fbe79b193bcf162255016ddbe35b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 8 Jun 2026 21:58:52 +0200 Subject: [PATCH 10/11] Added Windows bug tracking issue link --- src/coreclr/vm/exceptmacros.h | 2 ++ src/coreclr/vm/interpexec.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 157a05dfd4edb4..7fea6c62f1ecc9 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -239,6 +239,7 @@ VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchEx catch (const ResumeAfterCatchException& ex) \ { \ /* We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating */ \ + /* tracked by (internal) OS issue: https://microsoft.visualstudio.com/OS/_workitems/edit/62622295 */ \ ex.GetResumeContext(&__resumeSP, &__resumeIP); \ } \ if (__resumeSP != 0) \ @@ -253,6 +254,7 @@ VOID DECLSPEC_NORETURN RethrowResumeAfterCatchException(const ResumeAfterCatchEx catch (const ResumeAfterCatchException& ex) \ { \ /* We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating */ \ + /* tracked by (internal) OS issue: https://microsoft.visualstudio.com/OS/_workitems/edit/62622295 */ \ ex.GetResumeContext(&__resumeSP, &__resumeIP); \ } \ if (__resumeSP != 0) \ diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index dfaf06e01ae0bb..79afc32da4ff63 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -4641,7 +4641,8 @@ do \ // sequences of interpreted frames without any AOTed/JITted frames in between. In such case, the topmost native frame // the ResumeAfterCatchException is thrown from may not be the one that corresponds to the target interpreted frame. // Thus, we need to rethrow it to let it propagate further. - // We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating. + // We don't rethrow the exception here to work around a Windows bug in shadow stack pointer updating, + // tracked by (internal) OS issue: https://microsoft.visualstudio.com/OS/_workitems/edit/62622295 goto RETHROW_RESUME_AFTER_CATCH; } pThreadContext->frameDataAllocator.PopInfo(pFrame); From e5d2f35aaf743ea895007168bcce673201cd33ad Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 8 Jun 2026 22:16:58 +0200 Subject: [PATCH 11/11] Copilot feedback --- src/coreclr/vm/interpexec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 79afc32da4ff63..accba2e4dcd272 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -1325,8 +1325,8 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } CONTRACTL_END; - TADDR resumeSP; - TADDR resumeIP; + TADDR resumeSP = 0; + TADDR resumeIP = 0; #if defined(HOST_AMD64) && defined(HOST_WINDOWS) pInterpreterFrame->SetInterpExecMethodSSP((TADDR)_rdsspq());