Skip to content

Commit fd73e87

Browse files
[release/9.0-staging] Fix return address hijacking with CET (#109548)
* 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. * Add equivalent change to nativeaot * Add missing ifdef --------- Co-authored-by: Jan Vorlicek (from Dev Box) <[email protected]>
1 parent 329fdab commit fd73e87

File tree

3 files changed

+29
-16
lines changed

3 files changed

+29
-16
lines changed

src/coreclr/nativeaot/Runtime/EHHelpers.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ int32_t __stdcall RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t fau
485485

486486
#else // TARGET_UNIX
487487

488+
uintptr_t GetSSP(CONTEXT *pContext);
489+
void SetSSP(CONTEXT *pContext, uintptr_t ssp);
490+
488491
static bool g_ContinueOnFatalErrors = false;
489492

490493
// 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)
539542
// When the CET is enabled, the interruption happens on the ret instruction in the calee.
540543
// We need to "pop" rsp to the caller, as if the ret has consumed it.
541544
interruptedContext->SetSp(interruptedContext->GetSp() + 8);
545+
uintptr_t ssp = GetSSP(interruptedContext);
546+
SetSSP(interruptedContext, ssp + 8);
542547
}
543548

544549
// Change the IP to be at the original return site, as if we have returned to the caller.
545550
// That IP is an interruptible safe point, so we can suspend right there.
546-
uintptr_t origIp = interruptedContext->GetIp();
547551
interruptedContext->SetIp((uintptr_t)pThread->GetHijackedReturnAddress());
548552

549553
pThread->InlineSuspend(interruptedContext);
550554

551-
if (areShadowStacksEnabled)
552-
{
553-
// Undo the "pop", so that the ret could now succeed.
554-
interruptedContext->SetSp(interruptedContext->GetSp() - 8);
555-
interruptedContext->SetIp(origIp);
556-
}
557-
558555
ASSERT(!pThread->IsHijacked());
559556
return EXCEPTION_CONTINUE_EXECUTION;
560557
}

src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,3 +1006,25 @@ REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size
10061006
FlushInstructionCache(GetCurrentProcess(), pAddress, size);
10071007
}
10081008

1009+
#ifdef TARGET_AMD64
1010+
uintptr_t GetSSP(CONTEXT *pContext)
1011+
{
1012+
XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(pContext, XSTATE_CET_U, NULL);
1013+
if ((pCET != NULL) && (pCET->Ia32CetUMsr != 0))
1014+
{
1015+
return pCET->Ia32Pl3SspMsr;
1016+
}
1017+
1018+
return 0;
1019+
}
1020+
1021+
void SetSSP(CONTEXT *pContext, uintptr_t ssp)
1022+
{
1023+
XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(pContext, XSTATE_CET_U, NULL);
1024+
if (pCET != NULL)
1025+
{
1026+
pCET->Ia32Pl3SspMsr = ssp;
1027+
pCET->Ia32CetUMsr = 1;
1028+
}
1029+
}
1030+
#endif // TARGET_AMD64

src/coreclr/vm/excep.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6533,25 +6533,19 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo
65336533
// When the CET is enabled, the interruption happens on the ret instruction in the calee.
65346534
// We need to "pop" rsp to the caller, as if the ret has consumed it.
65356535
interruptedContext->Rsp += 8;
6536+
DWORD64 ssp = GetSSP(interruptedContext);
6537+
SetSSP(interruptedContext, ssp + 8);
65366538
}
65376539

65386540
// Change the IP to be at the original return site, as if we have returned to the caller.
65396541
// That IP is an interruptible safe point, so we can suspend right there.
6540-
uintptr_t origIp = interruptedContext->Rip;
65416542
interruptedContext->Rip = (uintptr_t)pThread->GetHijackedReturnAddress();
65426543

65436544
FrameWithCookie<ResumableFrame> frame(pExceptionInfo->ContextRecord);
65446545
frame.Push(pThread);
65456546
CommonTripThread();
65466547
frame.Pop(pThread);
65476548

6548-
if (areShadowStacksEnabled)
6549-
{
6550-
// Undo the "pop", so that the ret could now succeed.
6551-
interruptedContext->Rsp = interruptedContext->Rsp - 8;
6552-
interruptedContext->Rip = origIp;
6553-
}
6554-
65556549
return VEH_CONTINUE_EXECUTION;
65566550
}
65576551
#endif

0 commit comments

Comments
 (0)