reactos/ntoskrnl/ke/amd64/thrdini.c
Timo Kreuzer 583be404dd [NTOSKRNL/x64] Fix bug in KiInitializeContextThread
The function set CtxSwitchFrame->ApcBypass to FALSE, preventing APCs (like when user mode sets the context while the thread is suspended) from being delivered as soon as the thread lowers IRQL to PASSIVE_LEVEL. This resulted in the SetContext APC to be delivered only after the user mode APC was initialized, overwriting the user mode APC context in the trap frame. This caused kernel32_winetest process to break.
2023-09-07 01:16:14 +03:00

218 lines
6.5 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ke/amd64/thrdini.c
* PURPOSE: amd64 Thread Context Creation
* PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
* Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
extern void KiInvalidSystemThreadStartupExit(void);
extern void KiUserThreadStartupExit(void);
extern void KiServiceExit3(void);
typedef struct _KUINIT_FRAME
{
KSWITCH_FRAME CtxSwitchFrame;
KSTART_FRAME StartFrame;
KEXCEPTION_FRAME ExceptionFrame;
KTRAP_FRAME TrapFrame;
//FX_SAVE_AREA FxSaveArea;
} KUINIT_FRAME, *PKUINIT_FRAME;
typedef struct _KKINIT_FRAME
{
KSWITCH_FRAME CtxSwitchFrame;
KSTART_FRAME StartFrame;
//FX_SAVE_AREA FxSaveArea;
} KKINIT_FRAME, *PKKINIT_FRAME;
/* FUNCTIONS *****************************************************************/
VOID
NTAPI
KiInitializeContextThread(IN PKTHREAD Thread,
IN PKSYSTEM_ROUTINE SystemRoutine,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext,
IN PCONTEXT Context)
{
//PFX_SAVE_AREA FxSaveArea;
//PFXSAVE_FORMAT FxSaveFormat;
PKSTART_FRAME StartFrame;
PKSWITCH_FRAME CtxSwitchFrame;
PKTRAP_FRAME TrapFrame;
ULONG ContextFlags;
/* Check if this is a With-Context Thread */
if (Context)
{
PKUINIT_FRAME InitFrame;
/* Set up the Initial Frame */
InitFrame = ((PKUINIT_FRAME)Thread->InitialStack) - 1;
StartFrame = &InitFrame->StartFrame;
CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
/* Save back the new value of the kernel stack. */
Thread->KernelStack = (PVOID)InitFrame;
/* Tell the thread it will run in User Mode */
Thread->PreviousMode = UserMode;
// FIXME Setup the Fx Area
/* Set the Thread's NPX State */
Thread->NpxState = 0xA;
Thread->Header.NpxIrql = PASSIVE_LEVEL;
/* Make sure, we have control registers, disable debug registers */
ASSERT((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL);
ContextFlags = Context->ContextFlags & ~CONTEXT_DEBUG_REGISTERS;
/* Setup the Trap Frame */
TrapFrame = &InitFrame->TrapFrame;
/* Zero out the trap frame */
RtlZeroMemory(TrapFrame, sizeof(KTRAP_FRAME));
RtlZeroMemory(&InitFrame->ExceptionFrame, sizeof(KEXCEPTION_FRAME));
/* Set up a trap frame from the context. */
KeContextToTrapFrame(Context,
&InitFrame->ExceptionFrame,
TrapFrame,
CONTEXT_AMD64 | ContextFlags,
UserMode);
/* Set SS, DS, ES's RPL Mask properly */
TrapFrame->SegSs |= RPL_MASK;
TrapFrame->SegDs |= RPL_MASK;
TrapFrame->SegEs |= RPL_MASK;
TrapFrame->Dr7 = 0;
/* Set the previous mode as user */
TrapFrame->PreviousMode = UserMode;
/* Terminate the Exception Handler List */
TrapFrame->ExceptionFrame = 0;
/* KiThreadStartup returns to KiUserThreadStartupExit */
StartFrame->Return = (ULONG64)KiUserThreadStartupExit;
/* KiUserThreadStartupExit returns to KiServiceExit3 */
InitFrame->ExceptionFrame.Return = (ULONG64)KiServiceExit3;
}
else
{
PKKINIT_FRAME InitFrame;
/* Set up the Initial Frame for the system thread */
InitFrame = ((PKKINIT_FRAME)Thread->InitialStack) - 1;
StartFrame = &InitFrame->StartFrame;
CtxSwitchFrame = &InitFrame->CtxSwitchFrame;
/* Save back the new value of the kernel stack. */
Thread->KernelStack = (PVOID)InitFrame;
/* Tell the thread it will run in Kernel Mode */
Thread->PreviousMode = KernelMode;
// FIXME Setup the Fx Area
/* No NPX State */
Thread->NpxState = 0xA;
/* This must never return! */
StartFrame->Return = (ULONG64)KiInvalidSystemThreadStartupExit;
}
/* Set up the Context Switch Frame */
CtxSwitchFrame->Return = (ULONG64)KiThreadStartup;
CtxSwitchFrame->ApcBypass = TRUE;
StartFrame->P1Home = (ULONG64)StartRoutine;
StartFrame->P2Home = (ULONG64)StartContext;
StartFrame->P3Home = 0;
StartFrame->P4Home = (ULONG64)SystemRoutine;
StartFrame->Reserved = 0;
}
BOOLEAN
KiSwapContextResume(
_In_ BOOLEAN ApcBypass,
_In_ PKTHREAD OldThread,
_In_ PKTHREAD NewThread)
{
PKIPCR Pcr = (PKIPCR)KeGetPcr();
PKPROCESS OldProcess, NewProcess;
/* Setup ring 0 stack pointer */
Pcr->TssBase->Rsp0 = (ULONG64)NewThread->InitialStack; // FIXME: NPX save area?
Pcr->Prcb.RspBase = Pcr->TssBase->Rsp0;
/* Now we are the new thread. Check if it's in a new process */
OldProcess = OldThread->ApcState.Process;
NewProcess = NewThread->ApcState.Process;
if (OldProcess != NewProcess)
{
/* Switch address space and flush TLB */
__writecr3(NewProcess->DirectoryTableBase[0]);
/* Set new TSS fields */
//Pcr->TssBase->IoMapBase = NewProcess->IopmOffset;
}
/* Set TEB pointer and GS base */
Pcr->NtTib.Self = (PVOID)NewThread->Teb;
if (NewThread->Teb)
{
/* This will switch the usermode gs */
__writemsr(MSR_GS_SWAP, (ULONG64)NewThread->Teb);
}
/* Increase context switch count */
Pcr->ContextSwitches++;
NewThread->ContextSwitches++;
/* DPCs shouldn't be active */
if (Pcr->Prcb.DpcRoutineActive)
{
/* Crash the machine */
KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
(ULONG_PTR)OldThread,
(ULONG_PTR)NewThread,
(ULONG_PTR)OldThread->InitialStack,
0);
}
/* Old thread os no longer busy */
OldThread->SwapBusy = FALSE;
/* Kernel APCs may be pending */
if (NewThread->ApcState.KernelApcPending)
{
/* Are APCs enabled? */
if ((NewThread->SpecialApcDisable == 0) &&
(ApcBypass == 0))
{
/* Return TRUE to indicate that we want APCs to be delivered */
return TRUE;
}
/* Request an APC interrupt to be delivered later */
HalRequestSoftwareInterrupt(APC_LEVEL);
}
/* Return stating that no kernel APCs are pending*/
return FALSE;
}
/* EOF */