https://git.reactos.org/?p=reactos.git;a=commitdiff;h=45f75d5d320caf7ab1113…
commit 45f75d5d320caf7ab1113f0a2cf7c853e9ad4216
Author: Timo Kreuzer <timo.kreuzer(a)reactos.org>
AuthorDate: Sat Jun 26 18:49:47 2021 +0200
Commit: Timo Kreuzer <timo.kreuzer(a)reactos.org>
CommitDate: Thu Jul 14 18:35:28 2022 +0200
[NTOS:KE/x64] Handle user faults in KiGeneralProtectionFaultHandler
---
ntoskrnl/ke/amd64/except.c | 190 ++++++++++++++++++++++++++++++++++++++++++---
ntoskrnl/ke/amd64/trap.S | 16 +++-
sdk/lib/rtl/amd64/unwind.c | 7 +-
3 files changed, 198 insertions(+), 15 deletions(-)
diff --git a/ntoskrnl/ke/amd64/except.c b/ntoskrnl/ke/amd64/except.c
index dfaee9ed3cc..a04d3038bd9 100644
--- a/ntoskrnl/ke/amd64/except.c
+++ b/ntoskrnl/ke/amd64/except.c
@@ -425,6 +425,184 @@ KiNpxNotAvailableFaultHandler(
return -1;
}
+static
+BOOLEAN
+KiIsPrivilegedInstruction(PUCHAR Ip, BOOLEAN Wow64)
+{
+ ULONG i;
+
+ /* Handle prefixes */
+ for (i = 0; i < 15; i++)
+ {
+ if (!Wow64)
+ {
+ /* Check for REX prefix */
+ if ((Ip[0] >= 0x40) && (Ip[0] <= 0x4F))
+ {
+ Ip++;
+ continue;
+ }
+ }
+
+ switch (Ip[0])
+ {
+ /* Check prefixes */
+ case 0x26: // ES
+ case 0x2E: // CS / null
+ case 0x36: // SS
+ case 0x3E: // DS
+ case 0x64: // FS
+ case 0x65: // GS
+ case 0x66: // OP
+ case 0x67: // ADDR
+ case 0xF0: // LOCK
+ case 0xF2: // REP
+ case 0xF3: // REP INS/OUTS
+ Ip++;
+ continue;
+ }
+
+ break;
+ }
+
+ if (i == 15)
+ {
+ /* Too many prefixes. Should only happen, when the code was concurrently
modified. */
+ return FALSE;
+ }
+
+ switch (Ip[0])
+ {
+ case 0xF4: // HLT
+ case 0xFA: // CLI
+ case 0xFB: // STI
+ return TRUE;
+
+ case 0x0F:
+ {
+ switch (Ip[1])
+ {
+ case 0x06: // CLTS
+ case 0x07: // SYSRET
+ case 0x08: // INVD
+ case 0x09: // WBINVD
+ case 0x20: // MOV CR, XXX
+ case 0x21: // MOV DR, XXX
+ case 0x22: // MOV XXX, CR
+ case 0x23: // MOV YYY, DR
+ case 0x30: // WRMSR
+ case 0x32: // RDMSR
+ case 0x33: // RDPMC
+ case 0x35: // SYSEXIT
+ case 0x78: // VMREAD
+ case 0x79: // VMWRITE
+ return TRUE;
+
+ case 0x00:
+ {
+ /* Check MODRM Reg field */
+ switch ((Ip[2] >> 3) & 0x7)
+ {
+ case 2: // LLDT
+ case 3: // LTR
+ return TRUE;
+ }
+ break;
+ }
+
+ case 0x01:
+ {
+ switch (Ip[2])
+ {
+ case 0xC1: // VMCALL
+ case 0xC2: // VMLAUNCH
+ case 0xC3: // VMRESUME
+ case 0xC4: // VMXOFF
+ case 0xC8: // MONITOR
+ case 0xC9: // MWAIT
+ case 0xD1: // XSETBV
+ case 0xF8: // SWAPGS
+ return TRUE;
+ }
+
+ /* Check MODRM Reg field */
+ switch ((Ip[2] >> 3) & 0x7)
+ {
+ case 2: // LGDT
+ case 3: // LIDT
+ case 6: // LMSW
+ case 7: // INVLPG / SWAPGS / RDTSCP
+ return TRUE;
+ }
+ break;
+ }
+
+ case 0x38:
+ {
+ switch (Ip[2])
+ {
+ case 0x80: // INVEPT
+ case 0x81: // INVVPID
+ return TRUE;
+ }
+ break;
+ }
+
+ case 0xC7:
+ {
+ /* Check MODRM Reg field */
+ switch ((Ip[2] >> 3) & 0x7)
+ {
+ case 0x06: // VMPTRLD, VMCLEAR, VMXON
+ case 0x07: // VMPTRST
+ return TRUE;
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static
+NTSTATUS
+KiGeneralProtectionFaultUserMode(
+ _In_ PKTRAP_FRAME TrapFrame)
+{
+ BOOLEAN Wow64 = TrapFrame->SegCs == KGDT64_R3_CMCODE;
+ PUCHAR InstructionPointer;
+ NTSTATUS Status;
+
+ /* We need to decode the instruction at RIP */
+ InstructionPointer = (PUCHAR)TrapFrame->Rip;
+
+ _SEH2_TRY
+ {
+ /* Probe the instruction address */
+ ProbeForRead(InstructionPointer, 64, 1);
+
+ /* Check if it's a privileged instruction */
+ if (KiIsPrivilegedInstruction(InstructionPointer, Wow64))
+ {
+ Status = STATUS_PRIVILEGED_INSTRUCTION;
+ }
+ else
+ {
+ Status = STATUS_ACCESS_VIOLATION;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+
+ return Status;
+}
NTSTATUS
NTAPI
@@ -436,8 +614,7 @@ KiGeneralProtectionFaultHandler(
/* Check for user-mode GPF */
if (TrapFrame->SegCs & 3)
{
- UNIMPLEMENTED;
- ASSERT(FALSE);
+ return KiGeneralProtectionFaultUserMode(TrapFrame);
}
/* Check for lazy segment load */
@@ -454,15 +631,6 @@ KiGeneralProtectionFaultHandler(
return STATUS_SUCCESS;
}
- /* Check for nested exception */
- if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) &&
- (TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler))
- {
- /* Not implemented */
- UNIMPLEMENTED;
- ASSERT(FALSE);
- }
-
/* Get Instruction Pointer */
Instructions = (PUCHAR)TrapFrame->Rip;
diff --git a/ntoskrnl/ke/amd64/trap.S b/ntoskrnl/ke/amd64/trap.S
index 1598cef290c..644b4c2032d 100644
--- a/ntoskrnl/ke/amd64/trap.S
+++ b/ntoskrnl/ke/amd64/trap.S
@@ -285,7 +285,7 @@ KiInvalidOpcodeKernel:
/* Kernel mode fault */
/* Dispatch the exception */
- DispatchException STATUS_ILLEGAL_INSTRUCTION, 3, 0, 0, 0
+ DispatchException STATUS_ILLEGAL_INSTRUCTION, 0, 0, 0, 0
/* Return */
ExitTrap TF_SAVE_ALL
@@ -385,8 +385,18 @@ FUNC KiGeneralProtectionFault
test eax, eax
jge KiGpfExit
- /* Dispatch the exception */
- DispatchException eax, 3, 0, 0, 0
+ /* Check for access violation */
+ cmp eax, STATUS_ACCESS_VIOLATION
+ je DispatchAccessViolation
+
+ /* Dispatch privileged instruction fault */
+ DispatchException eax, 0, 0, 0, 0
+ jmp KiGpfFatal
+
+DispatchAccessViolation:
+
+ /* Dispatch access violation */
+ DispatchException eax, 2, 0, -1, 0
KiGpfFatal:
diff --git a/sdk/lib/rtl/amd64/unwind.c b/sdk/lib/rtl/amd64/unwind.c
index 49e3740bec4..619d31b6bb9 100644
--- a/sdk/lib/rtl/amd64/unwind.c
+++ b/sdk/lib/rtl/amd64/unwind.c
@@ -701,9 +701,15 @@ RtlpUnwindInternal(
Note: this can happen after the first frame as the result of an exception
*/
UnwindContext.Rip = *(DWORD64*)UnwindContext.Rsp;
UnwindContext.Rsp += sizeof(DWORD64);
+
+ /* Copy the context back for the next iteration */
+ *ContextRecord = UnwindContext;
continue;
}
+ /* Save Rip before the virtual unwind */
+ DispatcherContext.ControlPc = UnwindContext.Rip;
+
/* Do a virtual unwind to get the next frame */
ExceptionRoutine = RtlVirtualUnwind(HandlerType,
ImageBase,
@@ -749,7 +755,6 @@ RtlpUnwindInternal(
sizeof(DispatcherContext));
/* Set up the variable fields of the dispatcher context */
- DispatcherContext.ControlPc = ContextRecord->Rip;
DispatcherContext.ImageBase = ImageBase;
DispatcherContext.FunctionEntry = FunctionEntry;
DispatcherContext.LanguageHandler = ExceptionRoutine;