[NTOS:KE:X64] Change the logic of KeSwitchKernelStack and friends to be standards conforming

The previous version (like the x86 one) used a combination of C and asm code, called from C code to switch the stack. This is problematic, since there is no guarantee what assumptions C code makes about the stack (i.e. it can place any kind of stack pointers into registers or on the stack itself.) The new algorithm returns back to the systemcall entry point in asm, which then calls KiConvertToGuiThread, which is also asm and calls KeSwitchKernelStack ...
This commit is contained in:
Timo Kreuzer 2018-02-09 20:59:33 +01:00
parent 18b1aafd82
commit c86c55ace7
2 changed files with 57 additions and 12 deletions

View file

@ -13,6 +13,10 @@
#define NDEBUG
#include <debug.h>
ULONG ProcessCount;
BOOLEAN CcPfEnablePrefetcher;
SIZE_T KeXStateLength = sizeof(XSAVE_FORMAT);
VOID
KiRetireDpcListInDpcStack(
PKPRCB Prcb,
@ -96,14 +100,28 @@ KiSwitchKernelStackHelper(
LONG_PTR StackOffset,
PVOID OldStackBase);
/*
* Kernel stack layout (example pointers):
* 0xFFFFFC0F'2D008000 KTHREAD::StackBase
* [XSAVE_AREA size == KeXStateLength = 0x440]
* 0xFFFFFC0F'2D007BC0 KTHREAD::StateSaveArea _XSAVE_FORMAT
* 0xFFFFFC0F'2D007B90 KTHREAD::InitialStack
* [0x190 bytes KTRAP_FRAME]
* 0xFFFFFC0F'2D007A00 KTHREAD::TrapFrame
* [KSTART_FRAME] or ...
* [KSWITCH_FRAME]
* 0xFFFFFC0F'2D007230 KTHREAD::KernelStack
*/
PVOID
NTAPI
KeSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
KiSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
{
PKTHREAD CurrentThread;
PVOID OldStackBase;
LONG_PTR StackOffset;
SIZE_T StackSize;
PKIPCR Pcr;
/* Get the current thread */
CurrentThread = KeGetCurrentThread();
@ -139,20 +157,17 @@ KeSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
CurrentThread->StackLimit = (ULONG_PTR)StackLimit;
CurrentThread->LargeStack = TRUE;
/* Adjust the PCR fields */
__addgsqword(FIELD_OFFSET(KPCR, NtTib.StackBase), StackOffset);
__addgsqword(FIELD_OFFSET(KIPCR, Prcb.RspBase), StackOffset);
/* Adjust RspBase in the PCR */
Pcr = (PKIPCR)KeGetPcr();
Pcr->Prcb.RspBase += StackOffset;
/* Finally switch RSP to the new stack.
We pass OldStackBase to make sure it is not lost. */
OldStackBase = KiSwitchKernelStackHelper(StackOffset, OldStackBase);
/* Reenable interrupts */
_enable();
/* Adjust Rsp0 in the TSS */
Pcr->TssBase->Rsp0 += StackOffset;
return OldStackBase;
}
NTSTATUS
NTAPI
KeUserModeCallback(IN ULONG RoutineIndex,

View file

@ -22,7 +22,6 @@ EXTERN KiDeliverApc:PROC
EXTERN KiDpcInterruptHandler:PROC
EXTERN PsConvertToGuiThread:PROC
EXTERN MmCreateKernelStack:PROC
EXTERN KeSwitchKernelStack:PROC
EXTERN MmDeleteKernelStack:PROC
#ifdef _WINKD_
@ -774,7 +773,7 @@ PUBLIC KiSystemCallEntry64
.PROC KiSystemCall64Again
/* Old stack pointer is in rcx, lie and say we saved it in rbp */
/* Old stack pointer is in rcx, lie and say we saved it in rbp */
.setframe rbp, 0
.endprolog
@ -1023,6 +1022,37 @@ KiSwitchKernelStackHelper:
mov rax, rdx
ret
EXTERN KiSwitchKernelStack:PROC
PUBLIC KeSwitchKernelStack
FUNC KeSwitchKernelStack
sub rsp, 40
.allocstack 40
; Save rcx
mov [rsp], rcx
.savereg rcx, 0
.endprolog
; Call the C handler, which returns the old stack in rax
call KiSwitchKernelStack
; Restore rcx (StackBase)
mov rcx, [rsp]
; Switch to new stack: RSP += (StackBase - OldStackBase)
sub rcx, rax
add rsp, rcx
; Deallocate the home frame
add rsp, 40
ret
ENDFUNC
#ifdef _MSC_VER
#undef lgdt