mirror of
https://github.com/reactos/reactos.git
synced 2024-09-03 17:19:20 +00:00
![Thomas Faber](/assets/img/avatar_default.png)
If SEH is used in a C trap handler, the exception frame will be registered before the call to KiEnterTrap, which means we save the wrong trap handler. We'll therefore also restore this wrong frame for the excepting code, resulting in a stale SEH chain. We avoid this problem by saving the handler in the assembly trap entry code instead of from C. While SEH in a C trap handler should now theoretically be safe, we still forbid it through asserts in the C KiEnterTrap variants to make any potential future problems more obvious. Should this functionality be needed at some point and deemed safe, these asserts can then be removed.
434 lines
13 KiB
C
434 lines
13 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: ntoskrnl/include/internal/i386/trap_x.h
|
|
* PURPOSE: Internal Inlined Functions for the Trap Handling Code
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#if defined(_MSC_VER)
|
|
#define UNREACHABLE __assume(0)
|
|
#define __builtin_expect(a,b) (a)
|
|
#elif defined(__GNUC__)
|
|
#define UNREACHABLE __builtin_unreachable()
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
//
|
|
// Helper Code
|
|
//
|
|
FORCEINLINE
|
|
BOOLEAN
|
|
KiUserTrap(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Anything else but Ring 0 is Ring 3 */
|
|
return !!(TrapFrame->SegCs & MODE_MASK);
|
|
}
|
|
|
|
//
|
|
// Debug Macros
|
|
//
|
|
FORCEINLINE
|
|
VOID
|
|
KiDumpTrapFrame(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Dump the whole thing */
|
|
DbgPrint("DbgEbp: %x\n", TrapFrame->DbgEbp);
|
|
DbgPrint("DbgEip: %x\n", TrapFrame->DbgEip);
|
|
DbgPrint("DbgArgMark: %x\n", TrapFrame->DbgArgMark);
|
|
DbgPrint("DbgArgPointer: %x\n", TrapFrame->DbgArgPointer);
|
|
DbgPrint("TempSegCs: %x\n", TrapFrame->TempSegCs);
|
|
DbgPrint("TempEsp: %x\n", TrapFrame->TempEsp);
|
|
DbgPrint("Dr0: %x\n", TrapFrame->Dr0);
|
|
DbgPrint("Dr1: %x\n", TrapFrame->Dr1);
|
|
DbgPrint("Dr2: %x\n", TrapFrame->Dr2);
|
|
DbgPrint("Dr3: %x\n", TrapFrame->Dr3);
|
|
DbgPrint("Dr6: %x\n", TrapFrame->Dr6);
|
|
DbgPrint("Dr7: %x\n", TrapFrame->Dr7);
|
|
DbgPrint("SegGs: %x\n", TrapFrame->SegGs);
|
|
DbgPrint("SegEs: %x\n", TrapFrame->SegEs);
|
|
DbgPrint("SegDs: %x\n", TrapFrame->SegDs);
|
|
DbgPrint("Edx: %x\n", TrapFrame->Edx);
|
|
DbgPrint("Ecx: %x\n", TrapFrame->Ecx);
|
|
DbgPrint("Eax: %x\n", TrapFrame->Eax);
|
|
DbgPrint("PreviousPreviousMode: %x\n", TrapFrame->PreviousPreviousMode);
|
|
DbgPrint("ExceptionList: %p\n", TrapFrame->ExceptionList);
|
|
DbgPrint("SegFs: %x\n", TrapFrame->SegFs);
|
|
DbgPrint("Edi: %x\n", TrapFrame->Edi);
|
|
DbgPrint("Esi: %x\n", TrapFrame->Esi);
|
|
DbgPrint("Ebx: %x\n", TrapFrame->Ebx);
|
|
DbgPrint("Ebp: %x\n", TrapFrame->Ebp);
|
|
DbgPrint("ErrCode: %x\n", TrapFrame->ErrCode);
|
|
DbgPrint("Eip: %x\n", TrapFrame->Eip);
|
|
DbgPrint("SegCs: %x\n", TrapFrame->SegCs);
|
|
DbgPrint("EFlags: %x\n", TrapFrame->EFlags);
|
|
DbgPrint("HardwareEsp: %x\n", TrapFrame->HardwareEsp);
|
|
DbgPrint("HardwareSegSs: %x\n", TrapFrame->HardwareSegSs);
|
|
DbgPrint("V86Es: %x\n", TrapFrame->V86Es);
|
|
DbgPrint("V86Ds: %x\n", TrapFrame->V86Ds);
|
|
DbgPrint("V86Fs: %x\n", TrapFrame->V86Fs);
|
|
DbgPrint("V86Gs: %x\n", TrapFrame->V86Gs);
|
|
}
|
|
|
|
#if DBG
|
|
FORCEINLINE
|
|
VOID
|
|
KiFillTrapFrameDebug(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Set the debug information */
|
|
TrapFrame->DbgArgPointer = TrapFrame->Edx;
|
|
TrapFrame->DbgArgMark = 0xBADB0D00;
|
|
TrapFrame->DbgEip = TrapFrame->Eip;
|
|
TrapFrame->DbgEbp = TrapFrame->Ebp;
|
|
TrapFrame->PreviousPreviousMode = (ULONG)-1;
|
|
}
|
|
|
|
#define DR7_RESERVED_READ_AS_1 0x400
|
|
|
|
#define CheckDr(DrNumner, ExpectedValue) \
|
|
{ \
|
|
ULONG DrValue = __readdr(DrNumner); \
|
|
if (DrValue != (ExpectedValue)) \
|
|
{ \
|
|
DbgPrint("Dr%ld: expected %.8lx, got %.8lx\n", \
|
|
DrNumner, ExpectedValue, DrValue); \
|
|
__debugbreak(); \
|
|
} \
|
|
}
|
|
|
|
extern BOOLEAN StopChecking;
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame,
|
|
IN BOOLEAN SkipPreviousMode)
|
|
{
|
|
/* Don't check recursively */
|
|
if (StopChecking) return;
|
|
StopChecking = TRUE;
|
|
|
|
/* Make sure interrupts are disabled */
|
|
if (__readeflags() & EFLAGS_INTERRUPT_MASK)
|
|
{
|
|
DbgPrint("Exiting with interrupts enabled: %lx\n", __readeflags());
|
|
__debugbreak();
|
|
}
|
|
|
|
/* Make sure this is a real trap frame */
|
|
if (TrapFrame->DbgArgMark != 0xBADB0D00)
|
|
{
|
|
DbgPrint("Exiting with an invalid trap frame? (No MAGIC in trap frame)\n");
|
|
KiDumpTrapFrame(TrapFrame);
|
|
__debugbreak();
|
|
}
|
|
|
|
/* Make sure we're not in user-mode or something */
|
|
if (Ke386GetFs() != KGDT_R0_PCR)
|
|
{
|
|
DbgPrint("Exiting with an invalid FS: %lx\n", Ke386GetFs());
|
|
__debugbreak();
|
|
}
|
|
|
|
/* Make sure we have a valid SEH chain */
|
|
if (KeGetPcr()->NtTib.ExceptionList == 0)
|
|
{
|
|
DbgPrint("Exiting with NULL exception chain: %p\n", KeGetPcr()->NtTib.ExceptionList);
|
|
__debugbreak();
|
|
}
|
|
|
|
/* Make sure we're restoring a valid SEH chain */
|
|
if (TrapFrame->ExceptionList == 0)
|
|
{
|
|
DbgPrint("Entered a trap with a NULL exception chain: %p\n", TrapFrame->ExceptionList);
|
|
__debugbreak();
|
|
}
|
|
|
|
/* If we're ignoring previous mode, make sure caller doesn't actually want it */
|
|
if (SkipPreviousMode && (TrapFrame->PreviousPreviousMode != -1))
|
|
{
|
|
DbgPrint("Exiting a trap witout restoring previous mode, yet previous mode seems valid: %lx\n", TrapFrame->PreviousPreviousMode);
|
|
__debugbreak();
|
|
}
|
|
|
|
/* FIXME: KDBG messes around with these improperly */
|
|
#if !defined(KDBG)
|
|
/* Check DR values */
|
|
if (KiUserTrap(TrapFrame))
|
|
{
|
|
/* Check for active debugging */
|
|
if (KeGetCurrentThread()->Header.DebugActive)
|
|
{
|
|
if ((TrapFrame->Dr7 & ~DR7_RESERVED_MASK) == 0) __debugbreak();
|
|
|
|
CheckDr(0, TrapFrame->Dr0);
|
|
CheckDr(1, TrapFrame->Dr1);
|
|
CheckDr(2, TrapFrame->Dr2);
|
|
CheckDr(3, TrapFrame->Dr3);
|
|
CheckDr(7, TrapFrame->Dr7 | DR7_RESERVED_READ_AS_1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
CheckDr(0, Prcb->ProcessorState.SpecialRegisters.KernelDr0);
|
|
CheckDr(1, Prcb->ProcessorState.SpecialRegisters.KernelDr1);
|
|
CheckDr(2, Prcb->ProcessorState.SpecialRegisters.KernelDr2);
|
|
CheckDr(3, Prcb->ProcessorState.SpecialRegisters.KernelDr3);
|
|
// CheckDr(7, Prcb->ProcessorState.SpecialRegisters.KernelDr7); // Disabled, see CORE-10165 for more details.
|
|
}
|
|
#endif
|
|
|
|
StopChecking = FALSE;
|
|
}
|
|
|
|
#else
|
|
#define KiExitTrapDebugChecks(x, y)
|
|
#define KiFillTrapFrameDebug(x)
|
|
#endif
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
KiExitSystemCallDebugChecks(IN ULONG SystemCall,
|
|
IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
/* Check if this was a user call */
|
|
if (KiUserTrap(TrapFrame))
|
|
{
|
|
/* Make sure we are not returning with elevated IRQL */
|
|
OldIrql = KeGetCurrentIrql();
|
|
if (OldIrql != PASSIVE_LEVEL)
|
|
{
|
|
/* Forcibly put us in a sane state */
|
|
KeGetPcr()->Irql = PASSIVE_LEVEL;
|
|
_disable();
|
|
|
|
/* Fail */
|
|
KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE,
|
|
SystemCall,
|
|
OldIrql,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/* Make sure we're not attached and that APCs are not disabled */
|
|
if ((KeGetCurrentThread()->ApcStateIndex != OriginalApcEnvironment) ||
|
|
(KeGetCurrentThread()->CombinedApcDisable != 0))
|
|
{
|
|
/* Fail */
|
|
KeBugCheckEx(APC_INDEX_MISMATCH,
|
|
SystemCall,
|
|
KeGetCurrentThread()->ApcStateIndex,
|
|
KeGetCurrentThread()->CombinedApcDisable,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Generic Exit Routine
|
|
//
|
|
DECLSPEC_NORETURN VOID FASTCALL KiSystemCallReturn(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiSystemCallSysExitReturn(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiSystemCallTrapReturn(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiEditedTrapReturn(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiTrapReturn(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiTrapReturnNoSegments(IN PKTRAP_FRAME TrapFrame);
|
|
DECLSPEC_NORETURN VOID FASTCALL KiTrapReturnNoSegmentsRet8(IN PKTRAP_FRAME TrapFrame);
|
|
|
|
typedef
|
|
VOID
|
|
(FASTCALL *PFAST_SYSTEM_CALL_EXIT)(
|
|
IN PKTRAP_FRAME TrapFrame
|
|
);
|
|
|
|
extern PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler;
|
|
|
|
//
|
|
// Save user mode debug registers and restore kernel values
|
|
//
|
|
FORCEINLINE
|
|
VOID
|
|
KiHandleDebugRegistersOnTrapEntry(
|
|
IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
|
|
|
/* Save all debug registers in the trap frame */
|
|
TrapFrame->Dr0 = __readdr(0);
|
|
TrapFrame->Dr1 = __readdr(1);
|
|
TrapFrame->Dr2 = __readdr(2);
|
|
TrapFrame->Dr3 = __readdr(3);
|
|
TrapFrame->Dr6 = __readdr(6);
|
|
TrapFrame->Dr7 = __readdr(7);
|
|
|
|
/* Disable all active debugging */
|
|
__writedr(7, 0);
|
|
|
|
/* Restore kernel values */
|
|
__writedr(0, Prcb->ProcessorState.SpecialRegisters.KernelDr0);
|
|
__writedr(1, Prcb->ProcessorState.SpecialRegisters.KernelDr1);
|
|
__writedr(2, Prcb->ProcessorState.SpecialRegisters.KernelDr2);
|
|
__writedr(3, Prcb->ProcessorState.SpecialRegisters.KernelDr3);
|
|
__writedr(6, Prcb->ProcessorState.SpecialRegisters.KernelDr6);
|
|
__writedr(7, Prcb->ProcessorState.SpecialRegisters.KernelDr7);
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
KiHandleDebugRegistersOnTrapExit(
|
|
PKTRAP_FRAME TrapFrame)
|
|
{
|
|
/* Disable all active debugging */
|
|
__writedr(7, 0);
|
|
|
|
/* Load all debug registers from the trap frame */
|
|
__writedr(0, TrapFrame->Dr0);
|
|
__writedr(1, TrapFrame->Dr1);
|
|
__writedr(2, TrapFrame->Dr2);
|
|
__writedr(3, TrapFrame->Dr3);
|
|
__writedr(6, TrapFrame->Dr6);
|
|
__writedr(7, TrapFrame->Dr7);
|
|
}
|
|
|
|
//
|
|
// Virtual 8086 Mode Optimized Trap Exit
|
|
//
|
|
FORCEINLINE
|
|
DECLSPEC_NORETURN
|
|
VOID
|
|
KiExitV86Trap(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
PKTHREAD Thread;
|
|
KIRQL OldIrql;
|
|
|
|
/* Get the thread */
|
|
Thread = KeGetCurrentThread();
|
|
while (TRUE)
|
|
{
|
|
/* Return if this isn't V86 mode anymore */
|
|
if (!(TrapFrame->EFlags & EFLAGS_V86_MASK)) KiEoiHelper(TrapFrame);
|
|
|
|
/* Turn off the alerted state for kernel mode */
|
|
Thread->Alerted[KernelMode] = FALSE;
|
|
|
|
/* Are there pending user APCs? */
|
|
if (__builtin_expect(!Thread->ApcState.UserApcPending, 1)) break;
|
|
|
|
/* Raise to APC level and enable interrupts */
|
|
OldIrql = KfRaiseIrql(APC_LEVEL);
|
|
_enable();
|
|
|
|
/* Deliver APCs */
|
|
KiDeliverApc(UserMode, NULL, TrapFrame);
|
|
|
|
/* Restore IRQL and disable interrupts once again */
|
|
KfLowerIrql(OldIrql);
|
|
_disable();
|
|
}
|
|
|
|
/* If we got here, we're still in a valid V8086 context, so quit it */
|
|
if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
|
|
{
|
|
/* Restore debug registers from the trap frame */
|
|
KiHandleDebugRegistersOnTrapExit(TrapFrame);
|
|
}
|
|
|
|
/* Return from interrupt */
|
|
KiTrapReturnNoSegments(TrapFrame);
|
|
}
|
|
|
|
//
|
|
// Virtual 8086 Mode Optimized Trap Entry
|
|
//
|
|
FORCEINLINE
|
|
VOID
|
|
KiEnterV86Trap(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
PVOID ExceptionList;
|
|
|
|
/* Check exception list */
|
|
ExceptionList = KeGetPcr()->NtTib.ExceptionList;
|
|
ASSERTMSG("V86 trap handler must not register an SEH frame\n",
|
|
ExceptionList == TrapFrame->ExceptionList);
|
|
|
|
/* Save DR7 and check for debugging */
|
|
TrapFrame->Dr7 = __readdr(7);
|
|
if (__builtin_expect(TrapFrame->Dr7 & ~DR7_RESERVED_MASK, 0))
|
|
{
|
|
/* Handle debug registers */
|
|
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Interrupt Trap Entry
|
|
//
|
|
FORCEINLINE
|
|
VOID
|
|
KiEnterInterruptTrap(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
PVOID ExceptionList;
|
|
|
|
/* Check exception list and terminate it */
|
|
ExceptionList = KeGetPcr()->NtTib.ExceptionList;
|
|
ASSERTMSG("Interrupt handler must not register an SEH frame\n",
|
|
ExceptionList == TrapFrame->ExceptionList);
|
|
KeGetPcr()->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
|
|
|
|
/* Default to debugging disabled */
|
|
TrapFrame->Dr7 = 0;
|
|
|
|
/* Check if the frame was from user mode or v86 mode */
|
|
if (KiUserTrap(TrapFrame) ||
|
|
(TrapFrame->EFlags & EFLAGS_V86_MASK))
|
|
{
|
|
/* Check for active debugging */
|
|
if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
|
|
{
|
|
/* Handle debug registers */
|
|
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
|
|
}
|
|
}
|
|
|
|
/* Set debug header */
|
|
KiFillTrapFrameDebug(TrapFrame);
|
|
}
|
|
|
|
//
|
|
// Generic Trap Entry
|
|
//
|
|
FORCEINLINE
|
|
VOID
|
|
KiEnterTrap(IN PKTRAP_FRAME TrapFrame)
|
|
{
|
|
PVOID ExceptionList;
|
|
|
|
/* Check exception list */
|
|
ExceptionList = KeGetPcr()->NtTib.ExceptionList;
|
|
ASSERTMSG("Trap handler must not register an SEH frame\n",
|
|
ExceptionList == TrapFrame->ExceptionList);
|
|
|
|
/* Default to debugging disabled */
|
|
TrapFrame->Dr7 = 0;
|
|
|
|
/* Check if the frame was from user mode or v86 mode */
|
|
if (KiUserTrap(TrapFrame) ||
|
|
(TrapFrame->EFlags & EFLAGS_V86_MASK))
|
|
{
|
|
/* Check for active debugging */
|
|
if (KeGetCurrentThread()->Header.DebugActive & 0xFF)
|
|
{
|
|
/* Handle debug registers */
|
|
KiHandleDebugRegistersOnTrapEntry(TrapFrame);
|
|
}
|
|
}
|
|
|
|
/* Set debug header */
|
|
KiFillTrapFrameDebug(TrapFrame);
|
|
}
|