Skip to content
2 changes: 1 addition & 1 deletion src/coreclr/clrdefinitions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32)
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_ARCH>>>:FEATURE_EH_FUNCLETS>)
endif (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32)

if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)
if (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64))
add_definitions(-DFEATURE_SPECIAL_USER_MODE_APC)
endif()

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/ee/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ Debugger::Debugger()
m_mdDataStructureVersion = 1;
m_fOutOfProcessSetContextEnabled =
#if defined(OUT_OF_PROCESS_SETTHREADCONTEXT) && !defined(DACCESS_COMPILE)
Thread::AreCetShadowStacksEnabled() || CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_OutOfProcessSetContext) != 0;
Thread::AreShadowStacksEnabled() || CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_OutOfProcessSetContext) != 0;
#else
FALSE;
#endif
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/amd64/RedirectedHandledJITCase.asm
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ NESTED_ENTRY ApcActivationCallbackStub, _TEXT, FixRedirectContextHandler
.errnz REDIRECTSTUB_ESTABLISHER_OFFSET_RBP, REDIRECTSTUB_ESTABLISHER_OFFSET_RBP has changed - update asm stubs
END_PROLOGUE

; Save the pointer to the interrupted context on the stack for the stack walker
; Save a copy of the redirect CONTEXT*.
; This is needed for the debugger to unwind the stack.
mov rax, [rcx + OFFSETOF__APC_CALLBACK_DATA__ContextRecord]
mov [rbp + 20h], rax
.errnz REDIRECTSTUB_RBP_OFFSET_CONTEXT - 20h, REDIRECTSTUB_RBP_OFFSET_CONTEXT has changed - update asm stubs
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/arm64/asmconstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ ASMCONSTANTS_C_ASSERT(UnmanagedToManagedFrame__m_pvDatum == offsetof(UnmanagedTo

#endif // FEATURE_COMINTEROP

#ifdef FEATURE_SPECIAL_USER_MODE_APC
#define OFFSETOF__APC_CALLBACK_DATA__ContextRecord 0x8
#endif

#define REDIRECTSTUB_SP_OFFSET_CONTEXT 0

Expand Down
34 changes: 34 additions & 0 deletions src/coreclr/vm/arm64/asmhelpers.asm
Original file line number Diff line number Diff line change
Expand Up @@ -1174,5 +1174,39 @@ __HelperNakedFuncName SETS "$helper":CC:"Naked"
br x9
LEAF_END

#ifdef FEATURE_SPECIAL_USER_MODE_APC

IMPORT |?ApcActivationCallback@Thread@@CAX_K@Z|

; extern "C" void NTAPI ApcActivationCallbackStub(ULONG_PTR Parameter);
NESTED_ENTRY ApcActivationCallbackStub

PROLOG_SAVE_REG_PAIR fp, lr, #-16!
PROLOG_STACK_ALLOC 16 ; stack slot for CONTEXT* and padding

;REDIRECTSTUB_SP_OFFSET_CONTEXT is defined in asmconstants.h and is used in GetCONTEXTFromRedirectedStubStackFrame
;If CONTEXT is not saved at 0 offset from SP it must be changed as well.
ASSERT REDIRECTSTUB_SP_OFFSET_CONTEXT == 0

; Save a copy of the redirect CONTEXT*.
; This is needed for the debugger to unwind the stack.
ldr x17, [x0, OFFSETOF__APC_CALLBACK_DATA__ContextRecord]
str x17, [sp]

bl |?ApcActivationCallback@Thread@@CAX_K@Z|

EPILOG_STACK_FREE 16 ; undo stack slot for CONTEXT* and padding
EPILOG_RESTORE_REG_PAIR fp, lr, #16!
EPILOG_RETURN

; Put a label here to tell the debugger where the end of this function is.
PATCH_LABEL ApcActivationCallbackStubEnd
EXPORT ApcActivationCallbackStubEnd

NESTED_END

#endif ; FEATURE_SPECIAL_USER_MODE_APC


; Must be at very end of file
END
13 changes: 10 additions & 3 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6767,12 +6767,19 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo

if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT)
{
if (pThread == NULL || !pThread->PreemptiveGCDisabled())
{
// We are not running managed code, so this cannot be our hijack
// Perhaps some other runtime is responsible.
return VEH_CONTINUE_SEARCH;
}

HijackArgs hijackArgs;
hijackArgs.Rax = pExceptionInfo->ContextRecord->Rax;
hijackArgs.Rsp = pExceptionInfo->ContextRecord->Rsp;

bool areCetShadowStacksEnabled = Thread::AreCetShadowStacksEnabled();
if (areCetShadowStacksEnabled)
bool areShadowStacksEnabled = Thread::AreShadowStacksEnabled();
if (areShadowStacksEnabled)
{
// When the CET is enabled, the return address is still on stack, so we need to set the Rsp as
// if it was popped.
Expand All @@ -6790,7 +6797,7 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo
#undef CALLEE_SAVED_REGISTER
pExceptionInfo->ContextRecord->Rax = hijackArgs.Rax;

if (areCetShadowStacksEnabled)
if (areShadowStacksEnabled)
{
// The context refers to the return instruction
// Set the return address on the stack to the original one
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5282,7 +5282,7 @@ void JIT_Patchpoint(int* counter, int ilOffset)
DWORD contextSize = 0;
ULONG64 xStateCompactionMask = 0;
DWORD contextFlags = CONTEXT_FULL;
if (Thread::AreCetShadowStacksEnabled())
if (Thread::AreShadowStacksEnabled())
{
xStateCompactionMask = XSTATE_MASK_CET_U;
contextFlags |= CONTEXT_XSTATE;
Expand Down Expand Up @@ -5310,7 +5310,7 @@ void JIT_Patchpoint(int* counter, int ilOffset)
RtlCaptureContext(pFrameContext);

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
if (Thread::AreCetShadowStacksEnabled())
if (Thread::AreShadowStacksEnabled())
{
pFrameContext->ContextFlags |= CONTEXT_XSTATE;
SetXStateFeaturesMask(pFrameContext, xStateCompactionMask);
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8198,8 +8198,8 @@ void Thread::StaticInitialize()
#ifdef FEATURE_SPECIAL_USER_MODE_APC
InitializeSpecialUserModeApc();

// When CET shadow stacks are enabled, support for special user-mode APCs with the necessary functionality is required
_ASSERTE_ALL_BUILDS(!AreCetShadowStacksEnabled() || UseSpecialUserModeApc());
// When shadow stacks are enabled, support for special user-mode APCs with the necessary functionality is required
_ASSERTE_ALL_BUILDS(!AreShadowStacksEnabled() || UseSpecialUserModeApc());
#endif
}

Expand Down
9 changes: 7 additions & 2 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -4017,15 +4017,20 @@ class Thread
public:
static void StaticInitialize();

#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
static bool AreCetShadowStacksEnabled()
#if defined(TARGET_WINDOWS)
static bool AreShadowStacksEnabled()
{
LIMITED_METHOD_CONTRACT;

#if defined(TARGET_AMD64)
// The SSP is null when CET shadow stacks are not enabled. On processors that don't support shadow stacks, this is a
// no-op and the intrinsic returns 0. CET shadow stacks are enabled or disabled for all threads, so the result is the
// same from any thread.
return _rdsspq() != 0;
#else
// When implementing AreShadowStacksEnabled() on other architectures, review all the places where this is used.
return false;
#endif
}
#endif

Expand Down
24 changes: 11 additions & 13 deletions src/coreclr/vm/threadsuspend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ CLREvent* ThreadSuspend::g_pGCSuspendEvent = NULL;

ThreadSuspend::SUSPEND_REASON ThreadSuspend::m_suspendReason;

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
#if defined(TARGET_WINDOWS)
void* ThreadSuspend::g_returnAddressHijackTarget = NULL;
#endif

Expand Down Expand Up @@ -4747,13 +4747,13 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb)

VOID *pvHijackAddr = reinterpret_cast<VOID *>(OnHijackTripThread);

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
#if defined(TARGET_WINDOWS)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Update comment on #endif

void* returnAddressHijackTarget = ThreadSuspend::GetReturnAddressHijackTarget();
if (returnAddressHijackTarget != NULL)
{
pvHijackAddr = returnAddressHijackTarget;
}
#endif // TARGET_WINDOWS && TARGET_AMD64
#endif // TARGET_WINDOWS

#ifdef TARGET_X86
if (returnKind == RT_Float)
Expand Down Expand Up @@ -6062,22 +6062,20 @@ void ThreadSuspend::Initialize()
#ifdef FEATURE_HIJACK
#if defined(TARGET_UNIX)
::PAL_SetActivationFunction(HandleSuspensionForInterruptedThread, CheckActivationSafePoint);
#elif defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
// Only versions of Windows that have the special user mode APC have a correct implementation of the return address hijack handling
if (Thread::UseSpecialUserModeApc())
#elif defined(TARGET_WINDOWS)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtto

if (Thread::AreShadowStacksEnabled())
{
HMODULE hModNtdll = WszLoadLibrary(W("ntdll.dll"));
if (hModNtdll != NULL)
{
typedef ULONG_PTR (NTAPI *PFN_RtlGetReturnAddressHijackTarget)();
PFN_RtlGetReturnAddressHijackTarget pfnRtlGetReturnAddressHijackTarget = (PFN_RtlGetReturnAddressHijackTarget)GetProcAddress(hModNtdll, "RtlGetReturnAddressHijackTarget");
if (pfnRtlGetReturnAddressHijackTarget != NULL)
{
g_returnAddressHijackTarget = (void*)pfnRtlGetReturnAddressHijackTarget();
}
g_returnAddressHijackTarget = (void*)GetProcAddress(hModNtdll, "RtlGetReturnAddressHijackTarget");
}
if (g_returnAddressHijackTarget == NULL)
{
_ASSERTE_ALL_BUILDS(!"RtlGetReturnAddressHijackTarget must provide a target when shadow stacks are enabled");
}
}
#endif // TARGET_WINDOWS && TARGET_AMD64
#endif // TARGET_WINDOWS
#endif // FEATURE_HIJACK
}

Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/vm/threadsuspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ class ThreadSuspend
private:
static CLREvent * g_pGCSuspendEvent;

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
#if defined(TARGET_WINDOWS)
static void* g_returnAddressHijackTarget;
#endif // TARGET_WINDOWS && TARGET_AMD64
#endif // TARGET_WINDOWS

// This is true iff we're currently in the process of suspending threads. Once the
// threads have been suspended, this is false. This is set via an instance of
Expand Down Expand Up @@ -251,12 +251,12 @@ class ThreadSuspend
return g_pSuspensionThread;
}

#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
#if defined(TARGET_WINDOWS)
static void* GetReturnAddressHijackTarget()
{
return g_returnAddressHijackTarget;
}
#endif // TARGET_WINDOWS && TARGET_AMD64
#endif // TARGET_WINDOWS

private:
static LONG m_DebugWillSyncCount;
Expand Down