diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 52a3fbe68d7024..8f86df4227d464 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -19,6 +19,10 @@ #include "../../vm/methoditer.h" #include "../../vm/tailcallhelp.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 + const char *GetTType( TraceType tt); #define IsSingleStep(exception) ((exception) == EXCEPTION_SINGLE_STEP) diff --git a/src/coreclr/unwinder/arm64/unwinder.cpp b/src/coreclr/unwinder/arm64/unwinder.cpp index f76a97c67f88ec..f3c398c123af66 100644 --- a/src/coreclr/unwinder/arm64/unwinder.cpp +++ b/src/coreclr/unwinder/arm64/unwinder.cpp @@ -31,6 +31,10 @@ #define FIELD_OFFSET(type, field) ((LONG)__builtin_offsetof(type, field)) #endif +#if !defined(DACCESS_COMPILE) +extern "C" void* PacAuthPtr(void* ptr, void* sp); +#endif // !defined(DACCESS_COMPILE) + #ifdef HOST_UNIX #define RtlZeroMemory ZeroMemory @@ -251,16 +255,75 @@ do { #endif // !defined(DEBUGGER_UNWIND) -// // Macros for stripping pointer authentication (PAC) bits. -// +#if !defined(DACCESS_COMPILE) -#if !defined(DEBUGGER_STRIP_PAC) +#define HANDLE_PAC(pointer, sp) RtlHandlePacOnline(pointer, sp) -// NOTE: Pointer authentication is not used by .NET, so the implementation does nothing -#define STRIP_PAC(Params, pointer) +FORCEINLINE +VOID RtlHandlePacOnline(_Inout_ PULONG64 Pointer, _In_ ULONG64 Sp) -#endif +/*++ + +Routine Description: + + This routine authenticates an ARM64 pointer authenticated with PACIASP + using the supplied stack pointer as the modifier. Hence this should only + be called when authenticating a pointer at runtime (not debugger). + +Arguments: + + Pointer - Supplies a pointer to the pointer whose PAC will be authenticated. + + Sp - Supplies the stack pointer value that was used as the PAC modifier. + +Return Value: + + None. + +--*/ + +{ + *Pointer = (ULONG64)PacAuthPtr((void *)(*Pointer), (void *)Sp); +} +#else + +#define HANDLE_PAC(pointer, sp) RtlStripPacManual(pointer, sp) + +FORCEINLINE +VOID +RtlStripPacManual( + _Inout_ PULONG64 Pointer, + _In_ ULONG64 Sp + ) +/*++ + +Routine Description: + + This routine manually strips the ARM64 Pointer Authentication Code (PAC) + from a pointer. This is functionally similar to the XPAC family of + instructions. + + N.B. Even though PAC is only supported on ARM64, this routine is available + on all architectures to conveniently enable scenarios such as the + Debugger. + +Arguments: + + Pointer - Supplies a pointer to the pointer whose PAC will be stripped. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER(Sp); + *Pointer &= 0x0000FFFFFFFFFFFF; + return; +} + +#endif // !defined(DACCESS_COMPILE) // // Macros to clarify opcode parsing @@ -2343,7 +2406,7 @@ Return Value: return STATUS_UNWIND_INVALID_SEQUENCE; } - STRIP_PAC(UnwindParams, &ContextRecord->Lr); + HANDLE_PAC(&ContextRecord->Lr, ContextRecord->Sp); // // TODO: Implement support for UnwindFlags RTL_VIRTUAL_UNWIND2_VALIDATE_PAC. diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index b60f2a118be155..30b624a1f7309e 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -147,11 +147,47 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler EPILOG_RESTORE_REG_PAIR x25, x26, 64 EPILOG_RESTORE_REG_PAIR x27, x28, 80 EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 192 + xpaclri EPILOG_RETURN NESTED_END OnHijackTripThread, _TEXT #endif // FEATURE_HIJACK +// void* PacStripPtr(void *); +// This function strips the pointer of PAC info that is passed as an argument. +// We prefer to strip a pointer where it's not going to be used to branch execution to. +.arch_extension pauth + LEAF_ENTRY PacStripPtr, _TEXT + xpaci x0 + ret + LEAF_END PacStripPtr, _TEXT + +// void* PacSignPtr(void *, void *); +// This function signs the input pointer using x1 as salt. +// To avoid failing on non-PAC enabled machines, we use pacia1716 which signs lr explicitly. +// Thus we need to move input in lr, sign it and then copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacSignPtr, _TEXT + mov x17, x0 + mov x16, x1 + pacia1716 + mov x0, x17 + ret + LEAF_END PacSignPtr, _TEXT + +// void* PacAuthPtr(void *, void *); +// This function authenticates the input signed-pointer using x1 as salt. +// To avoid failing on non-PAC enabled machines, we use pacia1716 which authenticates lr explicitly. +// Thus we need to move input in lr, authenticate it and then copy it back to the result register. +.arch_extension pauth + LEAF_ENTRY PacAuthPtr, _TEXT + mov x17, x0 + mov x16, x1 + autia1716 + mov x0, x17 + ret + LEAF_END PacAuthPtr, _TEXT + // ------------------------------------------------------------------ // Redirection Stub for GC in fully interruptible method //GenerateRedirectedHandledJITCaseStub GCThreadControl diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index e7f6f8083b6d58..8d3a816caa9fe1 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -311,11 +311,45 @@ NoFloatingPointRetVal EPILOG_RESTORE_REG_PAIR x25, x26, #64 EPILOG_RESTORE_REG_PAIR x27, x28, #80 EPILOG_RESTORE_REG_PAIR fp, lr, #192! + + DCD 0xD50320FF ; xpaclri instruction in binary to avoid error while compiling with non-PAC enabled compilers EPILOG_RETURN NESTED_END #endif ; FEATURE_HIJACK +; void* PacStripPtr(void *); +; This function strips the pointer of PAC info that is passed as an argument. +; We prefer to strip a pointer where it's not going to be used to branch execution to. + LEAF_ENTRY PacStripPtr + DCD 0xDAC143E0 ; xpaci x0 instruction in binary to avoid requiring PAC-enabled assemblers + ret + LEAF_END PacStripPtr + +; void* PacSignPtr(void *, void *); +; This function signs the input pointer using x1 as salt. +; To avoid failing on non-PAC enabled machines, we use pacib1716 which signs lr explicitly. +; Thus we need to move input in lr, sign it and then copy it back to the result register. + LEAF_ENTRY PacSignPtr + mov x17, x0 + mov x16, x1 + DCD 0xD503215F ; pacib1716 instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, x17 + ret + LEAF_END PacSignPtr + +; void* PacAuthPtr(void *, void *); +; This function authenticates the input signed-pointer using x1 as salt. +; To avoid failing on non-PAC enabled machines, we use autib1716 which authenticates lr explicitly. +; Thus we need to move input in lr, authenticate it and then copy it back to the result register. + LEAF_ENTRY PacAuthPtr + mov x17, x0 + mov x16, x1 + DCD 0xD50321DF ; autib1716 instruction in binary to avoid error while compiling with non-PAC enabled compilers + mov x0, x17 + ret + LEAF_END PacAuthPtr + ;; ------------------------------------------------------------------ ;; Redirection Stub for GC in fully interruptible method GenerateRedirectedHandledJITCaseStub GCThreadControl diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 55e327dff3beda..802fcce7af29a7 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -45,6 +45,10 @@ class ComCallMethodDesc; extern PCODE GetPreStubEntryPoint(); +#ifndef DACCESS_COMPILE +extern "C" void* PacAuthPtr(void* ptr, void* sp); +#endif + #define STACK_ALIGN_SIZE 16 #define JUMP_ALLOCATE_SIZE 16 // # bytes to allocate for a jump instruction @@ -208,7 +212,7 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA inline PCODE GetIP(const T_CONTEXT * context) { LIMITED_METHOD_DAC_CONTRACT; - return context->Pc; + return (PCODE) context->Pc; } inline void SetIP(T_CONTEXT *context, PCODE eip) { diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 89e9c3f8b45727..60298085c77d2a 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -474,7 +474,7 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; - pRD->pCurrentContext->Pc = m_ReturnAddress; + pRD->pCurrentContext->Pc = GetReturnAddress(); size_t s = sizeof(struct HijackArgs); _ASSERTE(s%8 == 0); // HijackArgs contains register values and hence will be a multiple of 8 // stack must be multiple of 16. So if s is not multiple of 16 then there must be padding of 8 bytes diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 5e87f606da0be9..80d6419535d476 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -6312,6 +6312,244 @@ bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSaf return fIsInEpilog; } +#if defined(TARGET_ARM64) +// Read the PAC state for a managed ARM64 frame and, when PAC is enabled, recover the +// SP value that was live when PACIASP signed the return address in LR. +bool GetPacSignInfo(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, TADDR retAddrLocation, TADDR *pSpForPacSign) +{ + _ASSERTE(pContextToCheck != nullptr); + _ASSERTE(pCodeInfo->IsValid()); + _ASSERTE(pSpForPacSign != nullptr); + + *pSpForPacSign = 0; + + // In prolog or epilog while the current frame is still being established or torn down + // retrieving correct SP is complex. We conservatively bail-out in this case. + // TODO-PAC: Explore opportunities to retrieve SP while in prolog/epilog. + if (IsIPInProlog(pCodeInfo)) + { + return false; + } + + BOOL unused = TRUE; + if (IsIPInEpilog(pContextToCheck, pCodeInfo, &unused)) + { + return false; + } + + // Lookup the function entry for the IP + PTR_RUNTIME_FUNCTION FunctionEntry = pCodeInfo->GetFunctionEntry(); + + // We should always get a function entry for a managed method + _ASSERTE(FunctionEntry != NULL); + DWORD_PTR ImageBase = pCodeInfo->GetModuleBase(); + + _ASSERTE((FunctionEntry->UnwindData & 3) == 0); // Packed unwind data are not used with managed code + ULONG_PTR UnwindDataPtr = (ULONG_PTR)(ImageBase + FunctionEntry->UnwindData); + + // For unwind info layout details refer https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#arm64-exception-handling-information + // Read the header word. + DWORD HeaderWord = *(DWORD*)UnwindDataPtr; + UnwindDataPtr += sizeof(DWORD); + + _ASSERTE(((HeaderWord >> 18) & 3) == 0); // Version 0 is the only supported version. + + ULONG UnwindWords = (HeaderWord >> 27) & 31; + ULONG EpilogScopeCount = (HeaderWord >> 22) & 31; + if (EpilogScopeCount == 0 && UnwindWords == 0) + { + DWORD extendedCounts = *(DWORD*)UnwindDataPtr; + UnwindDataPtr += sizeof(DWORD); + UnwindWords = (extendedCounts >> 16) & 0xFF; + EpilogScopeCount = extendedCounts & 0xFFFF; + } + + if ((HeaderWord & (1 << 21)) != 0) + { + EpilogScopeCount = 0; + } + + ULONG_PTR UnwindCodePtr = UnwindDataPtr + sizeof(DWORD) * EpilogScopeCount; + ULONG_PTR UnwindCodesEndPtr = UnwindCodePtr + sizeof(DWORD) * UnwindWords; + + auto GetUnwindOpSize = [](BYTE unwindCode) -> SIZE_T + { + if (unwindCode < 0xC0) + { + return 1; + } + else if (unwindCode < 0xE0) + { + return 2; + } + else + { + static const BYTE UnwindCodeSizeTable[32] = + { + 4,1,2,1,1,1,1,3, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 2,3,4,5,1,1,1,1 + }; + + return UnwindCodeSizeTable[unwindCode - 0xE0]; + } + }; + + ULONG_PTR* unwindOpStarts = (ULONG_PTR*)_alloca((UnwindCodesEndPtr - UnwindCodePtr) * sizeof(ULONG_PTR)); + ULONG unwindOpIndex = 0; + for (ULONG_PTR unwindOpPtr = UnwindCodePtr; unwindOpPtr < UnwindCodesEndPtr;) + { + BYTE curCode = *(BYTE*)unwindOpPtr; + if (curCode == 0xE4) // end + { + break; + } + + SIZE_T unwindOpSize = GetUnwindOpSize(curCode); + if ((unwindOpPtr + unwindOpSize) > UnwindCodesEndPtr) + { + return false; + } + + unwindOpStarts[unwindOpIndex++] = unwindOpPtr; + unwindOpPtr += unwindOpSize; + } + + SSIZE_T currentSpOffset = 0; + SSIZE_T lrSlotOffset = SSIZE_T_MIN; + SSIZE_T pacSpOffset = 0; + BOOL hasPacSignLR = false; + constexpr SSIZE_T PtrSize = 8; + + // ARM64 prolog unwind codes are stored in reverse prolog order. Replay them in prolog order so + // PACIASP/PACIBSP captures the SP that was live when LR was originally signed. + while (unwindOpIndex != 0) + { + UnwindCodePtr = unwindOpStarts[--unwindOpIndex]; + ULONG CurCode = *(BYTE*)UnwindCodePtr; + + if (((CurCode & 0xFC) == 0xC8) || // save_regp + ((CurCode & 0xFE) == 0xD8) || // save_fregp + ((CurCode & 0xFE) == 0xDC) || // save_freg + CurCode == 0xE1 || // set_fp + CurCode == 0xE2 || // add_fp + CurCode == 0xE3 || // nop + CurCode == 0xE5 || // end_c + CurCode == 0xE6) // save_next + { + continue; + } + + if ((CurCode & 0xE0) == 0x00) // alloc_s + { + currentSpOffset -= (CurCode & 0x1F) * 16; + continue; + } + + if ((CurCode & 0xE0) == 0x20) // save_r19r20_x + { + currentSpOffset -= (CurCode & 0x1F) * 8; + continue; + } + + if ((CurCode & 0xC0) == 0x40) // save_fplr + { + lrSlotOffset = currentSpOffset + ((CurCode & 0x3F) * 8) + PtrSize; + continue; + } + + if ((CurCode & 0xC0) == 0x80) // save_fplr_x + { + currentSpOffset -= ((CurCode & 0x3F) + 1) * 8; + lrSlotOffset = currentSpOffset + PtrSize; + continue; + } + + if ((CurCode & 0xF8) == 0xC0) // alloc_m + { + ULONG x = ((CurCode & 0x7) << 8) | *(BYTE*)(UnwindCodePtr + 1); + currentSpOffset -= x * 16; + continue; + } + + if (((CurCode & 0xFC) == 0xCC) || // save_regp_x + ((CurCode & 0xFE) == 0xDA)) // save_fregp_x + { + ULONG z = *(BYTE*)(UnwindCodePtr + 1) & 0x3F; + currentSpOffset -= (z + 1) * 8; + continue; + } + + if ((CurCode & 0xFC) == 0xD0) // save_reg + { + BYTE nextCode = *(BYTE*)(UnwindCodePtr + 1); + ULONG x = ((CurCode & 0x3) << 2) | (nextCode >> 6); + ULONG z = nextCode & 0x3F; + if (x == 11) // R30 / LR is the 12th GP register in the save_reg encodings + { + lrSlotOffset = currentSpOffset + z * 8; + } + + continue; + } + + if ((CurCode & 0xFE) == 0xD4) // save_reg_x + { + BYTE nextCode = *(BYTE*)(UnwindCodePtr + 1); + ULONG x = ((CurCode & 0x1) << 3) | (nextCode >> 5); + currentSpOffset -= ((nextCode & 0x1F) + 1) * 8; + if (x == 11) // R30 / LR is the 12th GP register in the save_reg encodings + { + lrSlotOffset = currentSpOffset; + } + + continue; + } + + if ((CurCode & 0xFE) == 0xD6) // save_lrpair + { + ULONG z = *(BYTE*)(UnwindCodePtr + 1) & 0x3F; + lrSlotOffset = currentSpOffset + z * 8 + PtrSize; + continue; + } + + if (CurCode == 0xDE) // save_freg_x + { + ULONG z = *(BYTE*)(UnwindCodePtr + 1) & 0x1F; + currentSpOffset -= (z + 1) * 8; + continue; + } + + if (CurCode == 0xE0) // alloc_l + { + ULONG x = (*(BYTE*)(UnwindCodePtr + 1) << 16) | (*(BYTE*)(UnwindCodePtr + 2) << 8) | *(BYTE*)(UnwindCodePtr + 3); + currentSpOffset -= x * 16; + continue; + } + + if (CurCode == 0xFC) // pac_sign_lr + { + pacSpOffset = currentSpOffset; + hasPacSignLR = true; + continue; + } + + return false; + } + + if (!hasPacSignLR) + { + return true; + } + + if (lrSlotOffset == SSIZE_T_MIN) + { + return false; + } + + *pSpForPacSign = (TADDR)((SSIZE_T)retAddrLocation - (lrSlotOffset - pacSpOffset)); + return true; +} +#endif // TARGET_ARM64 + #endif // FEATURE_HIJACK && (!TARGET_X86 || TARGET_UNIX) #define EXCEPTION_VISUALCPP_DEBUGGER ((DWORD) (1<<30 | 0x6D<<16 | 5000)) diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index a37f1a490d3cfd..97157b3566edee 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -29,7 +29,9 @@ BOOL AdjustContextForJITHelpers(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCo // General purpose functions for use on an IP in jitted code. bool IsIPInProlog(EECodeInfo *pCodeInfo); bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSafeToInjectThreadAbort); - +#if defined(TARGET_ARM64) +bool GetPacSignInfo(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, TADDR retAddrLocation, TADDR *pSpForPacSign); +#endif // TARGET_ARM64 #endif // FEATURE_HIJACK && (!TARGET_X86 || TARGET_UNIX) // Enums diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 00f4d86f2578c3..e0b263fe82b04c 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -1299,6 +1299,20 @@ class HijackFrame : public Frame m_ReturnAddress); } + PCODE GetReturnAddress_Impl() + { + LIMITED_METHOD_DAC_CONTRACT; + +#if defined(TARGET_ARM64) && !defined(DACCESS_COMPILE) + if (m_SpForPacSign != 0) + { + return (PCODE)PacAuthPtr((void*)m_ReturnAddress, (void*)m_SpForPacSign); + } +#endif + + return (PCODE)m_ReturnAddress; + } + BOOL NeedsUpdateRegDisplay_Impl() { LIMITED_METHOD_CONTRACT; @@ -1326,11 +1340,18 @@ class HijackFrame : public Frame // HijackFrames are created by trip functions. See OnHijackTripThread() // They are real C++ objects on the stack. // So, it's a public function -- but that doesn't mean you should make some. - HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args); + HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args +#if defined(TARGET_ARM64) + , LPVOID spForPacSign +#endif + ); protected: TADDR m_ReturnAddress; +#if defined(TARGET_ARM64) + TADDR m_SpForPacSign; +#endif PTR_Thread m_Thread; DPTR(HijackArgs) m_Args; diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index da2dcca7d1da13..92efd671e2a6d0 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -10,6 +10,9 @@ #include "gcrefmap.h" #include "threads.h" +#if defined(TARGET_ARM64) +extern "C" void* PacStripPtr(void* ptr); +#endif // TARGET_ARM64 FCIMPL0(void*, TailCallHelp::GetTailCallArgBuffer) { @@ -39,7 +42,15 @@ FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr Thread* thread = GetThread(); - *retAddr = thread->GetReturnAddress(retAddrSlot); + void* retAddrFromSlot = thread->GetReturnAddress(retAddrSlot); + +#if defined(TARGET_ARM64) + // We strip the return address here as it's only used for comparison and + // not being used to branch execution to. + retAddrFromSlot = PacStripPtr(retAddrFromSlot); +#endif // TARGET_ARM64 + *retAddr = retAddrFromSlot; + return thread->GetTailCallTls(); } FCIMPLEND diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 5c13552d6ead34..8129a3140053a4 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2518,6 +2518,9 @@ class Thread void HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86_ARG(bool hasAsyncRet)); VOID *m_pvHJRetAddr; // original return address (before hijack) +#ifdef TARGET_ARM64 + VOID *m_pSpForPacSign; // stack pointer value that was used to sign LR with PACIASP +#endif VOID **m_ppvHJRetAddrPtr; // place we bashed a new return address MethodDesc *m_HijackedFunction; // remember what we hijacked diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 61a5190ea1ded6..4c52f7a49e19c5 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -22,6 +22,11 @@ #define HIJACK_NONINTERRUPTIBLE_THREADS +#if defined(TARGET_ARM64) +extern "C" void* PacSignPtr(void* ptr, void* sp); +extern "C" void* PacAuthPtr(void* ptr, void* sp); +#endif // TARGET_ARM64 + bool ThreadSuspend::s_fSuspendRuntimeInProgress = false; bool ThreadSuspend::s_fSuspended = false; @@ -4464,6 +4469,9 @@ struct ExecutionState bool m_IsInterruptible; // is this code interruptible? MethodDesc *m_pFD; // current function/method we're executing VOID **m_ppvRetAddrPtr; // pointer to return address in frame +#if defined(TARGET_ARM64) + VOID *m_pSpForPacSign; // stack pointer value that was used to sign LR with PACIASP +#endif DWORD m_RelOffset; // relative offset at which we're currently executing in this fcn IJitManager *m_pJitManager; METHODTOKEN m_MethodToken; @@ -4471,8 +4479,10 @@ struct ExecutionState ExecutionState() { LIMITED_METHOD_CONTRACT; -#ifdef TARGET_X86 +#if defined(TARGET_X86) m_FirstPass = true; +#elif defined(TARGET_ARM64) + m_pSpForPacSign = nullptr; #endif } }; @@ -4535,6 +4545,10 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 // Remember the place that the return would have gone m_pvHJRetAddr = *esb->m_ppvRetAddrPtr; +#if defined(TARGET_ARM64) + m_pSpForPacSign = esb->m_pSpForPacSign; +#endif + IS_VALID_CODE_PTR((FARPROC) (TADDR)m_pvHJRetAddr); // TODO [DAVBR]: For the full fix for VsWhidbey 450273, the below // may be uncommented once isLegalManagedCodeCaller works properly @@ -4546,6 +4560,13 @@ void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind) X86 m_HijackedFunction = esb->m_pFD; // Bash the stack to return to one of our stubs +#if defined(TARGET_ARM64) + if (m_pSpForPacSign != nullptr) + { + pvHijackAddr = PacSignPtr(pvHijackAddr, m_pSpForPacSign); + } +#endif // TARGET_ARM64 + *esb->m_ppvRetAddrPtr = pvHijackAddr; SetThreadState(TS_Hijacked); } @@ -4625,6 +4646,9 @@ StackWalkAction SWCB_GetExecutionState(CrawlFrame *pCF, VOID *pData) pES->m_pFD = pCF->GetFunction(); pES->m_MethodToken = pCF->GetMethodToken(); pES->m_ppvRetAddrPtr = 0; +#if defined(TARGET_ARM64) + pES->m_pSpForPacSign = nullptr; +#endif pES->m_IsInterruptible = pCF->IsGcSafe(); pES->m_RelOffset = pCF->GetRelOffset(); pES->m_pJitManager = pCF->GetJitManager(); @@ -4769,9 +4793,16 @@ StackWalkAction SWCB_GetExecutionState(CrawlFrame *pCF, VOID *pData) return action; } -HijackFrame::HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args) +HijackFrame::HijackFrame(LPVOID returnAddress, Thread *thread, HijackArgs *args +#if defined(TARGET_ARM64) + , LPVOID spForPacSign +#endif + ) : Frame(FrameIdentifier::HijackFrame), m_ReturnAddress((TADDR)returnAddress), +#if defined(TARGET_ARM64) + m_SpForPacSign((TADDR)spForPacSign), +#endif m_Thread(thread), m_Args(args) { @@ -4802,12 +4833,18 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs) thread->ResetThreadState(Thread::TS_Hijacked); - // Fix up our caller's stack, so it can resume from the hijack correctly + // Keep the actual resume address in the saved LR slot. HijackFrame + // authenticates the return address on demand for stackwalk/GC, but + // OnHijackTripThread will later return via the saved LR in HijackArgs. pArgs->ReturnAddress = (size_t)thread->m_pvHJRetAddr; // Build a frame so that stack crawling can proceed from here back to where // we will resume execution. - HijackFrame frame((void *)pArgs->ReturnAddress, thread, pArgs); + HijackFrame frame(thread->m_pvHJRetAddr, thread, pArgs +#if defined(TARGET_ARM64) + , thread->m_pSpForPacSign +#endif + ); #ifdef _DEBUG BOOL GCOnTransition = FALSE; @@ -5246,8 +5283,17 @@ BOOL Thread::HandledJITCase() X86_ONLY(ReturnKind returnKind;) X86_ONLY(bool hasAsyncRet;) + ARM64_ONLY(TADDR spForPacSign = 0;) if (GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind) X86_ARG(&hasAsyncRet))) { +#if defined(TARGET_ARM64) + if (!GetPacSignInfo(&ctx, &codeInfo, dac_cast(esb.m_ppvRetAddrPtr), &spForPacSign)) + { + return FALSE; + } + + esb.m_pSpForPacSign = (PVOID)spForPacSign; +#endif // TARGET_ARM64 HijackThread(&esb X86_ARG(returnKind) X86_ARG(hasAsyncRet)); } } @@ -5798,6 +5844,15 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) StackWalkerWalkingThreadHolder threadStackWalking(pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. + ARM64_ONLY(TADDR spForPacSign = 0;) +#if defined(TARGET_ARM64) + if (!GetPacSignInfo(interruptedContext, &codeInfo, dac_cast(executionState.m_ppvRetAddrPtr), &spForPacSign)) + { + return; + } + + executionState.m_pSpForPacSign = (PVOID)spForPacSign; +#endif // TARGET_ARM64 pThread->HijackThread(&executionState X86_ARG(returnKind) X86_ARG(hasAsyncRet)); } }