NESTED_ENTRY OnHijackTripThread, _TEXT
; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay
- ; and HijackObjectArgs
+ ; and HijackArgs
+ mov rdx, rsp
push rax ; make room for the real return address (Rip)
+ push rdx
PUSH_CALLEE_SAVED_REGISTERS
push_vol_reg rax
mov rcx, rsp
- alloc_stack 30h ; make extra room for xmm0
+ alloc_stack 38h ; make extra room for xmm0, argument home slots and align the SP
save_xmm128_postrsp xmm0, 20h
movdqa xmm0, [rsp + 20h]
- add rsp, 30h
+ add rsp, 38h
pop rax
POP_CALLEE_SAVED_REGISTERS
+ pop rdx
ret ; return to the correct place, adjusted by our caller
NESTED_END OnHijackTripThread, _TEXT
pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
pRD->pCurrentContext->Rip = m_ReturnAddress;
+#ifdef TARGET_WINDOWS
+ pRD->pCurrentContext->Rsp = m_Args->Rsp;
+#else
pRD->pCurrentContext->Rsp = PTR_TO_MEMBER_TADDR(HijackArgs, m_Args, Rip) + sizeof(void *);
+#endif
UpdateRegDisplayFromCalleeSavedRegisters(pRD, &(m_Args->Regs));
ULONG64 Rax;
ULONG64 ReturnValue[1];
};
-#else // FEATURE_MULTIREG_RETURN
+#else // !FEATURE_MULTIREG_RETURN
union
{
struct
};
ULONG64 ReturnValue[2];
};
-#endif // TARGET_UNIX
+#endif // !FEATURE_MULTIREG_RETURN
CalleeSavedRegisters Regs;
+#ifdef TARGET_WINDOWS
+ ULONG64 Rsp;
+#endif
union
{
ULONG64 Rip;
}
}
+#ifdef TARGET_AMD64
+
+#ifndef STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT
+#define STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT ((DWORD)0x80000033L)
+#endif
+
+ if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT)
+ {
+ HijackArgs hijackArgs;
+ hijackArgs.Rax = pExceptionInfo->ContextRecord->Rax;
+ hijackArgs.Rsp = pExceptionInfo->ContextRecord->Rsp;
+
+ bool areCetShadowStacksEnabled = Thread::AreCetShadowStacksEnabled();
+ if (areCetShadowStacksEnabled)
+ {
+ // When the CET is enabled, the return address is still on stack, so we need to set the Rsp as
+ // if it was popped.
+ hijackArgs.Rsp += 8;
+ }
+ hijackArgs.Rip = 0 ; // The OnHijackWorker sets this
+ #define CALLEE_SAVED_REGISTER(regname) hijackArgs.Regs.regname = pExceptionInfo->ContextRecord->regname;
+ ENUM_CALLEE_SAVED_REGISTERS();
+ #undef CALLEE_SAVED_REGISTER
+
+ OnHijackWorker(&hijackArgs);
+
+ #define CALLEE_SAVED_REGISTER(regname) pExceptionInfo->ContextRecord->regname = hijackArgs.Regs.regname;
+ ENUM_CALLEE_SAVED_REGISTERS();
+ #undef CALLEE_SAVED_REGISTER
+ pExceptionInfo->ContextRecord->Rax = hijackArgs.Rax;
+
+ if (areCetShadowStacksEnabled)
+ {
+ // The context refers to the return instruction
+ // Set the return address on the stack to the original one
+ *(size_t *)pExceptionInfo->ContextRecord->Rsp = hijackArgs.ReturnAddress;
+ }
+ else
+ {
+ // The context refers to the location after the return was processed
+ pExceptionInfo->ContextRecord->Rip = hijackArgs.ReturnAddress;
+ }
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+#endif
// We need to unhijack the thread here if it is not unhijacked already. On x86 systems,
// we do this in Thread::StackWalkFramesEx, but on amd64 systems we have the OS walk the
ThreadSuspend::SUSPEND_REASON ThreadSuspend::m_suspendReason;
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+void* ThreadSuspend::g_returnAddressHijackTarget = NULL;
+#endif
+
// If you add any thread redirection function, make sure the debugger can 1) recognize the redirection
// function, and 2) retrieve the original CONTEXT. See code:Debugger.InitializeHijackFunctionAddress and
// code:DacDbiInterfaceImpl.RetrieveHijackedContext.
CONTRACTL_END;
_ASSERTE(IsValidReturnKind(returnKind));
+
VOID *pvHijackAddr = reinterpret_cast<VOID *>(OnHijackTripThread);
+
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+ void* returnAddressHijackTarget = ThreadSuspend::GetReturnAddressHijackTarget();
+ if (returnAddressHijackTarget != NULL)
+ {
+ pvHijackAddr = returnAddressHijackTarget;
+ }
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
#ifdef TARGET_X86
if (returnKind == RT_Float)
{
// Initialize thread suspension support
void ThreadSuspend::Initialize()
{
-#if defined(FEATURE_HIJACK) && defined(TARGET_UNIX)
+#ifdef FEATURE_HIJACK
+#if defined(TARGET_UNIX)
::PAL_SetActivationFunction(HandleSuspensionForInterruptedThread, CheckActivationSafePoint);
-#endif
+#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())
+ {
+ 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();
+ }
+ }
+ }
+#endif // TARGET_WINDOWS && TARGET_AMD64
+#endif // FEATURE_HIJACK
}
#ifdef _DEBUG
private:
static CLREvent * g_pGCSuspendEvent;
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+ static void* g_returnAddressHijackTarget;
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
// 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
// SuspendRuntimeInProgressHolder placed in SuspendRuntime, SysStartSuspendForDebug,
return g_pSuspensionThread;
}
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+ static void* GetReturnAddressHijackTarget()
+ {
+ return g_returnAddressHijackTarget;
+ }
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
private:
static LONG m_DebugWillSyncCount;
};