From 305144e2d798a5385e8a52137c3e3f4e2abc2cd5 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 22 Jun 2022 10:55:06 +0200 Subject: [PATCH] Make GC Stress 4/8 work with CET (#71085) * Make GC Stress 4/8 work with CET This change makes the GC stress 4/8 work without redirection. It also fixes a problem with missing unwinding of the shadow stack pointer that was not discovered before. I've found it while testing this change - it has manifested itself as a shadow stack overflow. And there is one more fix. The VSD Resolve stub was problematic to unwind through when null reference was passed as this to it. The stub had a push rdx as the first instruction and the dereference of this happened after that. So in case of the null, the call stack in the vectored exception handler contained a phantom frame caused by a problem unwinding from the stub. That caused incorrect updating of the shadow SP. I've fixed it by moving the dereference before the push. --- src/coreclr/debug/ee/debugger.cpp | 2 +- src/coreclr/debug/ee/debugger.h | 2 +- src/coreclr/vm/amd64/Context.asm | 16 +++-- src/coreclr/vm/amd64/cgencpu.h | 41 +++++++++++ src/coreclr/vm/amd64/excepamd64.cpp | 9 ++- src/coreclr/vm/amd64/virtualcallstubcpu.hpp | 37 +++++----- src/coreclr/vm/ceemain.cpp | 22 ++++++ src/coreclr/vm/common.h | 12 ---- src/coreclr/vm/encee.cpp | 5 ++ src/coreclr/vm/excep.cpp | 7 +- src/coreclr/vm/exceptionhandling.cpp | 8 --- src/coreclr/vm/gccover.cpp | 36 +++++----- src/coreclr/vm/jithelpers.cpp | 76 ++++++++++++++++----- src/coreclr/vm/stackwalk.cpp | 7 ++ src/coreclr/vm/threads.cpp | 17 +++++ src/coreclr/vm/threads.h | 22 +++++- src/coreclr/vm/threadsuspend.cpp | 47 ++++--------- src/coreclr/vm/vars.hpp | 11 +++ 18 files changed, 263 insertions(+), 114 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 032e96457ecc1..954836a3e0f74 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -1010,7 +1010,7 @@ MemoryRange Debugger::s_hijackFunction[kMaxHijackFunctions] = RedirectedHandledJITCaseForDbgThreadControl_StubEnd), GetMemoryRangeForFunction(RedirectedHandledJITCaseForUserSuspend_Stub, RedirectedHandledJITCaseForUserSuspend_StubEnd) -#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) +#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) && defined(USE_REDIRECT_FOR_GCSTRESS) , GetMemoryRangeForFunction(RedirectedHandledJITCaseForGCStress_Stub, RedirectedHandledJITCaseForGCStress_StubEnd) diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index 39b203fa58923..a5c5ecd0fd620 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -2938,7 +2938,7 @@ void RedirectedHandledJITCaseForDbgThreadControl_StubEnd(); void RedirectedHandledJITCaseForUserSuspend_Stub(); void RedirectedHandledJITCaseForUserSuspend_StubEnd(); -#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) +#if defined(HAVE_GCCOVER) && defined(TARGET_AMD64) && defined(USE_REDIRECT_FOR_GCSTRESS) void RedirectedHandledJITCaseForGCStress_Stub(); void RedirectedHandledJITCaseForGCStress_StubEnd(); #endif // HAVE_GCCOVER && TARGET_AMD64 diff --git a/src/coreclr/vm/amd64/Context.asm b/src/coreclr/vm/amd64/Context.asm index 2c6d147bdb340..71bc2566f8acc 100644 --- a/src/coreclr/vm/amd64/Context.asm +++ b/src/coreclr/vm/amd64/Context.asm @@ -15,8 +15,8 @@ CONTEXT_CONTROL equ 1h CONTEXT_INTEGER equ 2h CONTEXT_FLOATING_POINT equ 8h -; Signature: EXTERN_C void STDCALL ClrRestoreNonvolatileContext(PCONTEXT ContextRecord); -NESTED_ENTRY ClrRestoreNonvolatileContext, _TEXT +; Signature: EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); +NESTED_ENTRY ClrRestoreNonvolatileContextWorker, _TEXT push_nonvol_reg rbp set_frame rbp, 0 END_PROLOGUE @@ -40,7 +40,15 @@ NESTED_ENTRY ClrRestoreNonvolatileContext, _TEXT test byte ptr [rcx + OFFSETOF__CONTEXT__ContextFlags], CONTEXT_CONTROL je Done_Restore_CONTEXT_CONTROL - + + test rdx, rdx + je No_Ssp_Update + rdsspq rax + sub rdx, rax + shr rdx, 3 + incsspq rdx + No_Ssp_Update: + ; When user-mode shadow stacks are enabled, and for example the intent is to continue execution in managed code after ; exception handling, iret and ret can't be used because their shadow stack enforcement would not allow that transition, ; and using them would require writing to the shadow stack, which is not preferable. Instead, iret is partially @@ -55,6 +63,6 @@ NESTED_ENTRY ClrRestoreNonvolatileContext, _TEXT ; The function was not asked to restore the control registers so we return back to the caller pop rbp ret -NESTED_END ClrRestoreNonvolatileContext, _TEXT +NESTED_END ClrRestoreNonvolatileContextWorker, _TEXT end diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index 39d9915c711d3..b64d3282e29fc 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -342,6 +342,47 @@ inline void SetSP(CONTEXT *context, TADDR rsp) context->Rsp = rsp; } +#if defined(TARGET_WINDOWS) && !defined(DACCESS_COMPILE) +inline DWORD64 GetSSP(const CONTEXT * context) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(const_cast(context), XSTATE_CET_U, NULL); + if ((pCET != NULL) && (pCET->Ia32CetUMsr != 0)) + { + return pCET->Ia32Pl3SspMsr; + } + + return 0; +} + +inline void SetSSP(CONTEXT *context, DWORD64 ssp) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + + PRECONDITION(CheckPointer(context)); + } + CONTRACTL_END; + + XSAVE_CET_U_FORMAT* pCET = (XSAVE_CET_U_FORMAT*)LocateXStateFeature(context, XSTATE_CET_U, NULL); + if (pCET != NULL) + { + pCET->Ia32Pl3SspMsr = ssp; + pCET->Ia32CetUMsr = 1; + } +} +#endif // TARGET_WINDOWS && !DACCESS_COMPILE + #define SetFP(context, ebp) inline TADDR GetFP(const CONTEXT * context) { diff --git a/src/coreclr/vm/amd64/excepamd64.cpp b/src/coreclr/vm/amd64/excepamd64.cpp index 17509c3ac1dc5..a50ddedc2ff49 100644 --- a/src/coreclr/vm/amd64/excepamd64.cpp +++ b/src/coreclr/vm/amd64/excepamd64.cpp @@ -619,7 +619,6 @@ AdjustContextForVirtualStub( _ASSERTE(!"AV in ResolveStub at unknown instruction"); return FALSE; } - SetSP(pContext, dac_cast(dac_cast(GetSP(pContext)) + sizeof(void*))); // rollback push rdx } else { @@ -634,6 +633,14 @@ AdjustContextForVirtualStub( SetIP(pContext, callsite); SetSP(pContext, dac_cast(dac_cast(GetSP(pContext)) + sizeof(void*))); // Move SP to where it was at the call site +#if defined(TARGET_WINDOWS) + DWORD64 ssp = GetSSP(pContext); + if (ssp != 0) + { + SetSSP(pContext, ssp + sizeof(void*)); + } +#endif // TARGET_WINDOWS + return TRUE; } diff --git a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp index d579633e527c5..e20dc31076440 100644 --- a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp @@ -398,11 +398,12 @@ struct ResolveStub private: friend struct ResolveHolder; - BYTE _resolveEntryPoint[3];// resolveStub: + BYTE _resolveEntryPoint[6];// resolveStub: + // 48 8B XX mov rax, [THIS_REG] // 52 push rdx // 49 BA mov r10, size_t _cacheAddress; // xx xx xx xx xx xx xx xx 64-bit address - BYTE part1 [15]; // 48 8B XX mov rax, [THIS_REG] ; Compute hash = ((MT + MT>>12) ^ prehash) + BYTE part1 [12]; // ; Compute hash = ((MT + MT>>12) ^ prehash) // 48 8B D0 mov rdx, rax ; rdx <- current MethodTable // 48 C1 E8 0C shr rax, 12 // 48 03 C2 add rax, rdx @@ -687,25 +688,25 @@ void ResolveHolder::InitializeStatic() { static_assert_no_msg((sizeof(ResolveHolder) % sizeof(void*)) == 0); - resolveInit._resolveEntryPoint [0] = 0x52; - resolveInit._resolveEntryPoint [1] = 0x49; - resolveInit._resolveEntryPoint [2] = 0xBA; + resolveInit._resolveEntryPoint [0] = X64_INSTR_MOV_RAX_IND_THIS_REG & 0xff; + resolveInit._resolveEntryPoint [1] = (X64_INSTR_MOV_RAX_IND_THIS_REG >> 8) & 0xff; + resolveInit._resolveEntryPoint [2] = (X64_INSTR_MOV_RAX_IND_THIS_REG >> 16) & 0xff; + resolveInit._resolveEntryPoint [3] = 0x52; + resolveInit._resolveEntryPoint [4] = 0x49; + resolveInit._resolveEntryPoint [5] = 0xBA; resolveInit._cacheAddress = 0xcccccccccccccccc; - resolveInit.part1 [ 0] = X64_INSTR_MOV_RAX_IND_THIS_REG & 0xff; - resolveInit.part1 [ 1] = (X64_INSTR_MOV_RAX_IND_THIS_REG >> 8) & 0xff; - resolveInit.part1 [ 2] = (X64_INSTR_MOV_RAX_IND_THIS_REG >> 16) & 0xff; + resolveInit.part1 [ 0] = 0x48; + resolveInit.part1 [ 1] = 0x8B; + resolveInit.part1 [ 2] = 0xD0; resolveInit.part1 [ 3] = 0x48; - resolveInit.part1 [ 4] = 0x8B; - resolveInit.part1 [ 5] = 0xD0; - resolveInit.part1 [ 6] = 0x48; - resolveInit.part1 [ 7] = 0xC1; - resolveInit.part1 [ 8] = 0xE8; - resolveInit.part1 [ 9] = CALL_STUB_CACHE_NUM_BITS; + resolveInit.part1 [ 4] = 0xC1; + resolveInit.part1 [ 5] = 0xE8; + resolveInit.part1 [ 6] = CALL_STUB_CACHE_NUM_BITS; + resolveInit.part1 [ 7] = 0x48; + resolveInit.part1 [ 8] = 0x03; + resolveInit.part1 [ 9] = 0xC2; resolveInit.part1 [10] = 0x48; - resolveInit.part1 [11] = 0x03; - resolveInit.part1 [12] = 0xC2; - resolveInit.part1 [13] = 0x48; - resolveInit.part1 [14] = 0x35; + resolveInit.part1 [11] = 0x35; // Review truncation from unsigned __int64 to UINT32 of a constant value. #if defined(_MSC_VER) #pragma warning(push) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 5d3e2a7f47a7f..aada53cd7e565 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -401,7 +401,26 @@ static BOOL WINAPI DbgCtrlCHandler(DWORD dwCtrlType) // A host can specify that it only wants one version of hosting interface to be used. BOOL g_singleVersionHosting; +#ifdef TARGET_WINDOWS +typedef BOOL(WINAPI* PINITIALIZECONTEXT2)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength, ULONG64 XStateCompactionMask); +PINITIALIZECONTEXT2 g_pfnInitializeContext2 = NULL; +#ifdef TARGET_X86 +typedef VOID(__cdecl* PRTLRESTORECONTEXT)(PCONTEXT ContextRecord, struct _EXCEPTION_RECORD* ExceptionRecord); +PRTLRESTORECONTEXT g_pfnRtlRestoreContext = NULL; +#endif // TARGET_X86 + +void InitializeOptionalWindowsAPIPointers() +{ + HMODULE hm = GetModuleHandleW(_T("kernel32.dll")); + g_pfnInitializeContext2 = (PINITIALIZECONTEXT2)GetProcAddress(hm, "InitializeContext2"); + +#ifdef TARGET_X86 + hm = GetModuleHandleW(_T("ntdll.dll")); + g_pfnRtlRestoreContext = (PRTLRESTORECONTEXT)GetProcAddress(hm, "RtlRestoreContext"); +#endif //TARGET_X86 +} +#endif // TARGET_WINDOWS void InitializeStartupFlags() { @@ -595,6 +614,9 @@ void EEStartupHelper() ::SetConsoleCtrlHandler(DbgCtrlCHandler, TRUE/*add*/); #endif +#ifdef HOST_WINDOWS + InitializeOptionalWindowsAPIPointers(); +#endif // HOST_WINDOWS // SString initialization // This needs to be done before config because config uses SString::Empty() diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index c4bd0ad55d08c..5683ca7d5338c 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -269,18 +269,6 @@ namespace Loader } LoadFlag; } -#if !defined(DACCESS_COMPILE) -#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) -EXTERN_C void STDCALL ClrRestoreNonvolatileContext(PCONTEXT ContextRecord); -#elif !(defined(TARGET_WINDOWS) && defined(TARGET_X86)) // !(TARGET_WINDOWS && TARGET_AMD64) && !(TARGET_WINDOWS && TARGET_X86) -inline void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) -{ - // Falling back to RtlRestoreContext() for now, though it should be possible to have simpler variants for these cases - RtlRestoreContext(ContextRecord, NULL); -} -#endif // TARGET_WINDOWS && TARGET_AMD64 -#endif // !DACCESS_COMPILE - // src/inc #include "utilcode.h" #include "log.h" diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index e59a217dfe442..d51a6431b5ad9 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -752,6 +752,9 @@ NOINLINE void EditAndContinueModule::FixContextAndResume( STATIC_CONTRACT_GC_TRIGGERS; // Sends IPC event STATIC_CONTRACT_THROWS; +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + DWORD64 ssp = GetSSP(pContext); +#endif // Create local copies of all structs passed as arguments to prevent them from being overwritten CONTEXT context; memcpy(&context, pContext, sizeof(CONTEXT)); @@ -832,6 +835,8 @@ NOINLINE void EditAndContinueModule::FixContextAndResume( #if defined(TARGET_X86) ResumeAtJit(pContext, oldSP); +#elif defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + ClrRestoreNonvolatileContextWorker(pContext, ssp); #else ClrRestoreNonvolatileContext(pContext); #endif diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 23d5e3b8f6f71..07dfb58cf344d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -7087,9 +7087,12 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExcepti // NOTE: this is effectively ifdef (TARGET_AMD64 || TARGET_ARM), and does not actually trigger // a GC. This will redirect the exception context to a stub which will // push a frame and cause GC. - if (IsGcMarker(pContext, pExceptionRecord)) + if (Thread::UseRedirectForGcStress()) { - return VEH_CONTINUE_EXECUTION;; + if (IsGcMarker(pContext, pExceptionRecord)) + { + return VEH_CONTINUE_EXECUTION;; + } } #endif // USE_REDIRECT_FOR_GCSTRESS diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 8a4d6ee16c6c5..1510780605ee5 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -950,14 +950,6 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord // begin Early Processing // { -#ifndef USE_REDIRECT_FOR_GCSTRESS - if (IsGcMarker(pContextRecord, pExceptionRecord)) - { - returnDisposition = ExceptionContinueExecution; - goto lExit; - } -#endif // !USE_REDIRECT_FOR_GCSTRESS - EH_LOG((LL_INFO100, "..................................................................................\n")); EH_LOG((LL_INFO100, "ProcessCLRException enter, sp = 0x%p, ControlPc = 0x%p\n", MemoryStackFp, pDispatcherContext->ControlPc)); DebugLogExceptionRecord(pExceptionRecord); diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 3f473c5a34599..71a49b63be540 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1359,19 +1359,21 @@ BOOL OnGcCoverageInterrupt(PCONTEXT regs) #if defined(USE_REDIRECT_FOR_GCSTRESS) && !defined(TARGET_UNIX) // If we're unable to redirect, then we simply won't test GC at this // location. - if (!pThread->CheckForAndDoRedirectForGCStress(regs)) + if (Thread::UseRedirectForGcStress()) { - RemoveGcCoverageInterrupt(instrPtr, savedInstrPtr, gcCover, offset); + if (!pThread->CheckForAndDoRedirectForGCStress(regs)) + { + RemoveGcCoverageInterrupt(instrPtr, savedInstrPtr, gcCover, offset); + } } - -#else // !USE_REDIRECT_FOR_GCSTRESS - + else +#endif // !USE_REDIRECT_FOR_GCSTRESS + { #ifdef _DEBUG - if (!g_pConfig->SkipGCCoverage(pMD->GetModule()->GetSimpleName())) + if (!g_pConfig->SkipGCCoverage(pMD->GetModule()->GetSimpleName())) #endif - DoGcStress(regs, codeInfo.GetNativeCodeVersion()); - -#endif // !USE_REDIRECT_FOR_GCSTRESS + DoGcStress(regs, codeInfo.GetNativeCodeVersion()); + } return TRUE; } @@ -1707,16 +1709,15 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) } #endif // 0 - -#if !defined(USE_REDIRECT_FOR_GCSTRESS) // // If we redirect for gc stress, we don't need this frame on the stack, // the redirection will push a resumable frame. // FrameWithCookie frame(regs); - frame.Push(pThread); -#endif // USE_REDIRECT_FOR_GCSTRESS - + if (!Thread::UseRedirectForGcStress()) + { + frame.Push(pThread); + } DWORD_PTR retValRegs[2] = { 0 }; UINT numberOfRegs = 0; @@ -1801,9 +1802,10 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) } } -#if !defined(USE_REDIRECT_FOR_GCSTRESS) - frame.Pop(pThread); -#endif // USE_REDIRECT_FOR_GCSTRESS + if (!Thread::UseRedirectForGcStress()) + { + frame.Pop(pThread); + } if (enableWhenDone) { diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 44ec15481fa2f..ffec65bfa34bd 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -5085,33 +5085,69 @@ void JIT_Patchpoint(int* counter, int ilOffset) #endif // Find context for the original method + CONTEXT *pFrameContext = NULL; +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + DWORD contextSize = 0; + ULONG64 xStateCompactionMask = 0; + DWORD contextFlags = CONTEXT_FULL; + if (Thread::AreCetShadowStacksEnabled()) + { + xStateCompactionMask = XSTATE_MASK_CET_U; + contextFlags |= CONTEXT_XSTATE; + } + + // The initialize call should fail but return contextSize + BOOL success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) : + InitializeContext(NULL, contextFlags, NULL, &contextSize); + + _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)); + + PVOID pBuffer = _alloca(contextSize); + success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) : + InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize); + _ASSERTE(success); +#else // TARGET_WINDOWS && TARGET_AMD64 CONTEXT frameContext; frameContext.ContextFlags = CONTEXT_FULL; - RtlCaptureContext(&frameContext); + pFrameContext = &frameContext; +#endif // TARGET_WINDOWS && TARGET_AMD64 + + // Find context for the original method + RtlCaptureContext(pFrameContext); + +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + if (Thread::AreCetShadowStacksEnabled()) + { + pFrameContext->ContextFlags |= CONTEXT_XSTATE; + SetXStateFeaturesMask(pFrameContext, xStateCompactionMask); + SetSSP(pFrameContext, _rdsspq()); + } +#endif // TARGET_WINDOWS && TARGET_AMD64 // Walk back to the original method frame - pThread->VirtualUnwindToFirstManagedCallFrame(&frameContext); + pThread->VirtualUnwindToFirstManagedCallFrame(pFrameContext); // Remember original method FP and SP because new method will inherit them. - UINT_PTR currentSP = GetSP(&frameContext); - UINT_PTR currentFP = GetFP(&frameContext); + UINT_PTR currentSP = GetSP(pFrameContext); + UINT_PTR currentFP = GetFP(pFrameContext); // We expect to be back at the right IP - if ((UINT_PTR)ip != GetIP(&frameContext)) + if ((UINT_PTR)ip != GetIP(pFrameContext)) { // Should be fatal STRESS_LOG2(LF_TIEREDCOMPILATION, LL_FATALERROR, "Jit_Patchpoint: patchpoint (0x%p) TRANSITION" - " unexpected context IP 0x%p\n", ip, GetIP(&frameContext)); + " unexpected context IP 0x%p\n", ip, GetIP(pFrameContext)); EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } // Now unwind back to the original method caller frame. - EECodeInfo callerCodeInfo(GetIP(&frameContext)); - frameContext.ContextFlags = CONTEXT_FULL; + EECodeInfo callerCodeInfo(GetIP(pFrameContext)); ULONG_PTR establisherFrame = 0; PVOID handlerData = NULL; - RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(&frameContext), callerCodeInfo.GetFunctionEntry(), - &frameContext, &handlerData, &establisherFrame, NULL); + RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(), + pFrameContext, &handlerData, &establisherFrame, NULL); // Now, set FP and SP back to the values they had just before this helper was called, // since the new method must have access to the original method frame. @@ -5124,13 +5160,19 @@ void JIT_Patchpoint(int* counter, int ilOffset) // method sees the "expected" SP misalgnment on entry. _ASSERTE(currentSP % 16 == 0); currentSP -= 8; -#endif - SetSP(&frameContext, currentSP); +#if defined(TARGET_WINDOWS) + DWORD64 ssp = GetSSP(pFrameContext); + if (ssp != 0) + { + SetSSP(pFrameContext, ssp - 8); + } +#endif // TARGET_WINDOWS -#if defined(TARGET_AMD64) - frameContext.Rbp = currentFP; -#endif + pFrameContext->Rbp = currentFP; +#endif // TARGET_AMD64 + + SetSP(pFrameContext, currentSP); // Note we can get here w/o triggering, if there is an existing OSR method and // we hit the patchpoint. @@ -5138,10 +5180,10 @@ void JIT_Patchpoint(int* counter, int ilOffset) LOG((LF_TIEREDCOMPILATION, transitionLogLevel, "Jit_Patchpoint: patchpoint [%d] (0x%p) TRANSITION to ip 0x%p\n", ppId, ip, osrMethodCode)); // Install new entry point as IP - SetIP(&frameContext, osrMethodCode); + SetIP(pFrameContext, osrMethodCode); // Transition! - ClrRestoreNonvolatileContext(&frameContext); + ClrRestoreNonvolatileContext(pFrameContext); } // Jit helper invoked at a partial compilation patchpoint. diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 47d653c51bf00..6603ce89e1ec9 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -635,6 +635,13 @@ PCODE Thread::VirtualUnwindLeafCallFrame(T_CONTEXT* pContext) uControlPc = *(ULONGLONG*)pContext->Rsp; pContext->Rsp += sizeof(ULONGLONG); +#ifdef TARGET_WINDOWS + DWORD64 ssp = GetSSP(pContext); + if (ssp != 0) + { + SetSSP(pContext, ssp + sizeof(ULONGLONG)); + } +#endif // TARGET_WINDOWS #elif defined(TARGET_ARM) || defined(TARGET_ARM64) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index e7ed60c75a487..f7cdf70d02d19 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -8352,6 +8352,23 @@ void Thread::InitializeSpecialUserModeApc() } #endif // FEATURE_SPECIAL_USER_MODE_APC + +#if !(defined(TARGET_WINDOWS) && defined(TARGET_X86)) +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); +#endif + +void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord) +{ +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + DWORD64 ssp = GetSSP(ContextRecord); + ClrRestoreNonvolatileContextWorker(ContextRecord, ssp); +#else + // Falling back to RtlRestoreContext() for now, though it should be possible to have simpler variants for these cases + RtlRestoreContext(ContextRecord, NULL); +#endif +} +#endif // !(TARGET_WINDOWS && TARGET_X86) #endif // #ifndef DACCESS_COMPILE #ifdef DACCESS_COMPILE diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index aab863910738c..99b547bf639d5 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -3826,7 +3826,7 @@ class Thread bool m_RedirectContextInUse; #endif - BOOL IsContextSafeToRedirect(T_CONTEXT* pContext); + BOOL IsContextSafeToRedirect(const T_CONTEXT* pContext); public: PT_CONTEXT GetSavedRedirectContext() @@ -4509,6 +4509,17 @@ class Thread #endif } + static bool UseRedirectForGcStress() + { + LIMITED_METHOD_CONTRACT; + +#ifdef USE_REDIRECT_FOR_GCSTRESS + return UseContextBasedThreadRedirection(); +#else + return false; +#endif + } + #ifdef FEATURE_SPECIAL_USER_MODE_APC private: static QueueUserAPC2Proc s_pfnQueueUserAPC2Proc; @@ -6238,4 +6249,13 @@ class StackWalkerWalkingThreadHolder Thread* m_PreviousValue; }; +#ifndef DACCESS_COMPILE +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); +#endif +#if !(defined(TARGET_WINDOWS) && defined(TARGET_X86)) +void ClrRestoreNonvolatileContext(PCONTEXT ContextRecord); +#endif +#endif // DACCESS_COMPILE + #endif //__threads_h__ diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index f4dca3d984c1e..89e5598a10e13 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -1104,7 +1104,7 @@ BOOL Thread::IsRudeAbort() // we can determine that the OS is not in user mode. Otherwise, we // return TRUE. // -BOOL Thread::IsContextSafeToRedirect(CONTEXT* pContext) +BOOL Thread::IsContextSafeToRedirect(const CONTEXT* pContext) { CONTRACTL { @@ -1933,13 +1933,7 @@ void ThreadSuspend::UnlockThreadStore(BOOL bThreadDestroyed, ThreadSuspend::SUSP #endif } -typedef BOOL(WINAPI* PINITIALIZECONTEXT2)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength, ULONG64 XStateCompactionMask); -PINITIALIZECONTEXT2 pfnInitializeContext2 = NULL; - #ifdef TARGET_X86 -typedef VOID(__cdecl* PRTLRESTORECONTEXT)(PCONTEXT ContextRecord, struct _EXCEPTION_RECORD* ExceptionRecord); -PRTLRESTORECONTEXT pfnRtlRestoreContext = NULL; - #define CONTEXT_COMPLETE (CONTEXT_FULL | CONTEXT_FLOATING_POINT | \ CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS) #else @@ -1953,20 +1947,6 @@ CONTEXT* AllocateOSContextHelper(BYTE** contextBuffer) #if !defined(TARGET_UNIX) && (defined(TARGET_X86) || defined(TARGET_AMD64)) DWORD context = CONTEXT_COMPLETE; - if (pfnInitializeContext2 == NULL) - { - HMODULE hm = GetModuleHandleW(_T("kernel32.dll")); - pfnInitializeContext2 = (PINITIALIZECONTEXT2)GetProcAddress(hm, "InitializeContext2"); - } - -#ifdef TARGET_X86 - if (pfnRtlRestoreContext == NULL) - { - HMODULE hm = GetModuleHandleW(_T("ntdll.dll")); - pfnRtlRestoreContext = (PRTLRESTORECONTEXT)GetProcAddress(hm, "RtlRestoreContext"); - } -#endif //TARGET_X86 - // Determine if the processor supports AVX so we could // retrieve extended registers DWORD64 FeatureMask = GetEnabledXStateFeatures(); @@ -1979,8 +1959,8 @@ CONTEXT* AllocateOSContextHelper(BYTE** contextBuffer) DWORD contextSize = 0; ULONG64 xStateCompactionMask = XSTATE_MASK_LEGACY | XSTATE_MASK_AVX; // The initialize call should fail but return contextSize - BOOL success = pfnInitializeContext2 ? - pfnInitializeContext2(NULL, context, NULL, &contextSize, xStateCompactionMask) : + BOOL success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(NULL, context, NULL, &contextSize, xStateCompactionMask) : InitializeContext(NULL, context, NULL, &contextSize); // Spec mentions that we may get a different error (it was observed on Windows7). @@ -1996,8 +1976,8 @@ CONTEXT* AllocateOSContextHelper(BYTE** contextBuffer) BYTE* buffer = new (nothrow)BYTE[contextSize]; if (buffer != NULL) { - success = pfnInitializeContext2 ? - pfnInitializeContext2(buffer, context, &pOSContext, &contextSize, xStateCompactionMask): + success = g_pfnInitializeContext2 ? + g_pfnInitializeContext2(buffer, context, &pOSContext, &contextSize, xStateCompactionMask): InitializeContext(buffer, context, &pOSContext, &contextSize); if (!success) @@ -2677,7 +2657,7 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) frame.Push(); #if defined(HAVE_GCCOVER) && defined(USE_REDIRECT_FOR_GCSTRESS) // GCCOVER - if (reason == RedirectReason_GCStress) + if (Thread::UseRedirectForGcStress() && (reason == RedirectReason_GCStress)) { _ASSERTE(pThread->PreemptiveGCDisabledOther()); DoGcStress(frame.GetContext(), NULL); @@ -2700,7 +2680,7 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) // and continue normal execution. #ifdef TARGET_X86 - if (!pfnRtlRestoreContext) + if (!g_pfnRtlRestoreContext) { RestoreContextSimulated(pThread, pCtx, &frame, dwLastError); @@ -2715,7 +2695,7 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) // cooperative - but we will resume to preemptive below. We should not trigger an abort in that case, as it will fail // due to the GC mode. // - if (!pThread->m_fPreemptiveGCDisabledForGCStress) + if (!Thread::UseRedirectForGcStress() || !pThread->m_fPreemptiveGCDisabledForGCStress) #endif { @@ -2752,7 +2732,7 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) pThread->UnmarkRedirectContextInUse(pCtx); #if defined(HAVE_GCCOVER) && defined(USE_REDIRECT_FOR_GCSTRESS) // GCCOVER - if (pThread->m_fPreemptiveGCDisabledForGCStress) + if (Thread::UseRedirectForGcStress() && pThread->m_fPreemptiveGCDisabledForGCStress) { pThread->EnablePreemptiveGC(); pThread->m_fPreemptiveGCDisabledForGCStress = false; @@ -2763,7 +2743,7 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) SetLastError(dwLastError); // END_PRESERVE_LAST_ERROR #ifdef TARGET_X86 - pfnRtlRestoreContext(pCtx, NULL); + g_pfnRtlRestoreContext(pCtx, NULL); #else RtlRestoreContext(pCtx, NULL); #endif @@ -2830,6 +2810,7 @@ void __stdcall Thread::RedirectedHandledJITCaseForUserSuspend() void __stdcall Thread::RedirectedHandledJITCaseForGCStress() { WRAPPER_NO_CONTRACT; + _ASSERTE(Thread::UseRedirectForGcStress()); RedirectedHandledJITCase(RedirectReason_GCStress); } @@ -2925,7 +2906,7 @@ BOOL Thread::RedirectThreadAtHandledJITCase(PFN_REDIRECTTARGET pTgt) SetIP(pCtx, (PCODE)pTgt); - STRESS_LOG4(LF_SYNC, LL_INFO10000, "Redirecting thread %p(tid=%x) from address 0x%08x to address 0x%p\n", + STRESS_LOG4(LF_SYNC, LL_INFO10000, "Redirecting thread %p(tid=%x) from address 0x%p to address 0x%p\n", this, this->GetThreadId(), dwOrigEip, pTgt); bRes = EESetThreadContext(this, pCtx); @@ -3072,7 +3053,7 @@ BOOL Thread::IsAddrOfRedirectFunc(void * pFuncAddr) WRAPPER_NO_CONTRACT; #if defined(HAVE_GCCOVER) && defined(USE_REDIRECT_FOR_GCSTRESS) // GCCOVER - if (pFuncAddr == GetRedirectHandlerForGCStress()) + if (Thread::UseRedirectForGcStress() && (pFuncAddr == GetRedirectHandlerForGCStress())) return TRUE; #endif // HAVE_GCCOVER && USE_REDIRECT_FOR_GCSTRESS @@ -3148,6 +3129,8 @@ BOOL Thread::CheckForAndDoRedirectForGCStress (CONTEXT *pCurrentThreadCtx) { WRAPPER_NO_CONTRACT; + _ASSERTE(Thread::UseRedirectForGcStress()); + LOG((LF_CORDB, LL_INFO1000, "Redirecting thread %08x for GCStress", GetThreadId())); m_fPreemptiveGCDisabledForGCStress = !PreemptiveGCDisabled(); diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 032e062c59109..c1de60729ea91 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -717,4 +717,15 @@ enum HostCallPreference NoHostCalls, }; +#ifdef TARGET_WINDOWS +typedef BOOL(WINAPI* PINITIALIZECONTEXT2)(PVOID Buffer, DWORD ContextFlags, PCONTEXT* Context, PDWORD ContextLength, ULONG64 XStateCompactionMask); +extern PINITIALIZECONTEXT2 g_pfnInitializeContext2; + +#ifdef TARGET_X86 +typedef VOID(__cdecl* PRTLRESTORECONTEXT)(PCONTEXT ContextRecord, struct _EXCEPTION_RECORD* ExceptionRecord); +extern PRTLRESTORECONTEXT g_pfnRtlRestoreContext; +#endif // TARGET_X86 + +#endif // TARGET_WINDOWS + #endif /* _VARS_HPP */