From 27e3586a3f16bbae399652841da390b6646f6814 Mon Sep 17 00:00:00 2001 From: "Jan Vorlicek (from Dev Box)" Date: Mon, 21 Oct 2024 15:12:41 +0200 Subject: [PATCH 1/3] Fix return address hijacking with CET There is a problematic case when return address is hijacked while in a managed method that tail calls a GC write barrier and when CET is enabled. The write barrier code can change while the handler for the hijacked address is executed from the vectored exception handler. When the vectored exception handler then returns to the write barrier to re-execute the `ret` instruction that has triggered the vectored exception handler due to the main stack containing a different address than the shadow stack (now with the main stack fixed), the instruction may no longer be `ret` due to the change of the write barrier change. This change fixes it by setting the context to return to from the vectored exception handler to point to the caller and setting the Rsp and SSP to match that. That way, the write barrier code no longer matters. --- src/coreclr/vm/excep.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 8418202b933893..9328ac24876456 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6533,11 +6533,12 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo // When the CET is enabled, the interruption happens on the ret instruction in the calee. // We need to "pop" rsp to the caller, as if the ret has consumed it. interruptedContext->Rsp += 8; + DWORD64 ssp = GetSSP(interruptedContext); + SetSSP(interruptedContext, ssp + 8); } // Change the IP to be at the original return site, as if we have returned to the caller. // That IP is an interruptible safe point, so we can suspend right there. - uintptr_t origIp = interruptedContext->Rip; interruptedContext->Rip = (uintptr_t)pThread->GetHijackedReturnAddress(); FrameWithCookie frame(pExceptionInfo->ContextRecord); @@ -6545,13 +6546,6 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo CommonTripThread(); frame.Pop(pThread); - if (areShadowStacksEnabled) - { - // Undo the "pop", so that the ret could now succeed. - interruptedContext->Rsp = interruptedContext->Rsp - 8; - interruptedContext->Rip = origIp; - } - return VEH_CONTINUE_EXECUTION; } #endif From 29dda473b18a5d9b789efc68a74ba6160a0f21b0 Mon Sep 17 00:00:00 2001 From: "Jan Vorlicek (from Dev Box)" Date: Tue, 22 Oct 2024 13:32:19 +0200 Subject: [PATCH 2/3] Add equivalent change to nativeaot --- src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 13 +++++------- .../Runtime/windows/PalRedhawkMinWin.cpp | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index 060e19f3c525e2..569cf36e84fa50 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -485,6 +485,9 @@ int32_t __stdcall RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t fau #else // TARGET_UNIX +uintptr_t GetSSP(CONTEXT *pContext); +void SetSSP(CONTEXT *pContext, uintptr_t ssp); + static bool g_ContinueOnFatalErrors = false; // Set the runtime to continue search when encountering an unhandled runtime exception. Once done it is forever. @@ -539,22 +542,16 @@ int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs) // When the CET is enabled, the interruption happens on the ret instruction in the calee. // We need to "pop" rsp to the caller, as if the ret has consumed it. interruptedContext->SetSp(interruptedContext->GetSp() + 8); + uintptr_t ssp = GetSSP(interruptedContext); + SetSSP(interruptedContext, ssp + 8); } // Change the IP to be at the original return site, as if we have returned to the caller. // That IP is an interruptible safe point, so we can suspend right there. - uintptr_t origIp = interruptedContext->GetIp(); interruptedContext->SetIp((uintptr_t)pThread->GetHijackedReturnAddress()); pThread->InlineSuspend(interruptedContext); - if (areShadowStacksEnabled) - { - // Undo the "pop", so that the ret could now succeed. - interruptedContext->SetSp(interruptedContext->GetSp() - 8); - interruptedContext->SetIp(origIp); - } - ASSERT(!pThread->IsHijacked()); return EXCEPTION_CONTINUE_EXECUTION; } diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 86013f7a964d28..428fe630daeeb3 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -1006,3 +1006,23 @@ REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size FlushInstructionCache(GetCurrentProcess(), pAddress, size); } +uintptr_t GetSSP(CONTEXT *pContext) +{ + XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(pContext, XSTATE_CET_U, NULL); + if ((pCET != NULL) && (pCET->Ia32CetUMsr != 0)) + { + return pCET->Ia32Pl3SspMsr; + } + + return 0; +} + +void SetSSP(CONTEXT *pContext, uintptr_t ssp) +{ + XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(pContext, XSTATE_CET_U, NULL); + if (pCET != NULL) + { + pCET->Ia32Pl3SspMsr = ssp; + pCET->Ia32CetUMsr = 1; + } +} From 9fdbd28deec3b99fc1bd0c42b972a36adc2d31ac Mon Sep 17 00:00:00 2001 From: "Jan Vorlicek (from Dev Box)" Date: Tue, 22 Oct 2024 18:38:02 +0200 Subject: [PATCH 3/3] Add missing ifdef --- src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 428fe630daeeb3..f7bfcd68bdad75 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -1006,6 +1006,7 @@ REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size FlushInstructionCache(GetCurrentProcess(), pAddress, size); } +#ifdef TARGET_AMD64 uintptr_t GetSSP(CONTEXT *pContext) { XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(pContext, XSTATE_CET_U, NULL); @@ -1026,3 +1027,4 @@ void SetSSP(CONTEXT *pContext, uintptr_t ssp) pCET->Ia32CetUMsr = 1; } } +#endif // TARGET_AMD64