diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 1970b6c33c7544..5dc10ae36570fb 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -223,7 +223,7 @@ if (NOT CLR_CMAKE_TARGET_ARCH_I386 OR NOT CLR_CMAKE_TARGET_WIN32) add_compile_definitions($<$>>: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() diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 3723ab2c90354e..d4a9cde7de04a9 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -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 diff --git a/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm b/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm index 61146cc6fbe59e..4aeb705922ab3c 100644 --- a/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm +++ b/src/coreclr/vm/amd64/RedirectedHandledJITCase.asm @@ -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 diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index 262fa6860df73f..c002916963f421 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -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 diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index bc88d15ee330fb..ec28879607187b 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -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 diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index dcc5442c40a8f7..a12a4c0ce3853e 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -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. @@ -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 diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 05d2ad18b5bd94..6adaec8643f903 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -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; @@ -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); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index e9e2409dde0c7b..31d73aa6f0f607 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -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 } diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 67c4b6b83c975b..982fdf956d2867 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -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 diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index f79193888480d8..bf449aad984fb2 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -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 @@ -4747,13 +4747,13 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) VOID *pvHijackAddr = reinterpret_cast(OnHijackTripThread); -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +#if defined(TARGET_WINDOWS) void* returnAddressHijackTarget = ThreadSuspend::GetReturnAddressHijackTarget(); if (returnAddressHijackTarget != NULL) { pvHijackAddr = returnAddressHijackTarget; } -#endif // TARGET_WINDOWS && TARGET_AMD64 +#endif // TARGET_WINDOWS #ifdef TARGET_X86 if (returnKind == RT_Float) @@ -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) + 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 } diff --git a/src/coreclr/vm/threadsuspend.h b/src/coreclr/vm/threadsuspend.h index 6d034b91a1997a..e1114badf6d065 100644 --- a/src/coreclr/vm/threadsuspend.h +++ b/src/coreclr/vm/threadsuspend.h @@ -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 @@ -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;