add missing fault handlers instead of throwing everything into the GP bin, add more ways to crash in native shell

This commit is contained in:
maksw authored and Maksymilian Wilczynski committed 2026-05-26 14:46:52 +02:00
1 parent e1248f4afe
commit 03bf767ac2
9 files changed
+425 -88

No files matched your search

+54 -30
View File
@@ -745,36 +745,60 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
EFI_MEMORY_DESCRIPTOR* efiMap = AllocatePool(mapSize);
gBS->GetMemoryMap(&mapSize, efiMap, &mapKey, &descSz, &descVer);
{
UINT64 idHoles[4];
UINT64 winHoles[4];
UINTN holeCount = 0;
if (fbBytes) {
idHoles[holeCount * 2 + 0] = fbBase;
idHoles[holeCount * 2 + 1] = fbBase + fbBytes;
winHoles[holeCount * 2 + 0] = PHYS_MAP_BASE + fbBase;
winHoles[holeCount * 2 + 1] = PHYS_MAP_BASE + fbBase + fbBytes;
holeCount++;
}
idHoles[holeCount * 2 + 0] = 0xFEC00000ULL;
idHoles[holeCount * 2 + 1] = 0xFEE01000ULL;
winHoles[holeCount * 2 + 0] = PHYS_MAP_BASE + 0xFEC00000ULL;
winHoles[holeCount * 2 + 1] = PHYS_MAP_BASE + 0xFEE01000ULL;
holeCount++;
UINT64 idHoles[8];
UINT64 winHoles[8];
UINTN idHoleCount = 0;
UINTN winHoleCount = 0;
if (holeCount == 2) {
if (idHoles[2] < idHoles[0]) {
UINT64 t0 = idHoles[0], t1 = idHoles[1];
idHoles[0] = idHoles[2];
idHoles[1] = idHoles[3];
idHoles[2] = t0;
idHoles[3] = t1;
// Leave virtual page zero genuinely unmapped. Mapping it supervisor/NX
// still hides null pointer bugs from the page-fault path and makes
// diagnostics worse. The physical-window alias for frame zero remains
// available at PHYS_MAP_BASE if low physical memory is ever needed.
idHoles[idHoleCount * 2 + 0] = 0;
idHoles[idHoleCount * 2 + 1] = PAGE_SIZE;
idHoleCount++;
if (fbBytes) {
idHoles[idHoleCount * 2 + 0] = fbBase;
idHoles[idHoleCount * 2 + 1] = fbBase + fbBytes;
idHoleCount++;
winHoles[winHoleCount * 2 + 0] = PHYS_MAP_BASE + fbBase;
winHoles[winHoleCount * 2 + 1] = PHYS_MAP_BASE + fbBase + fbBytes;
winHoleCount++;
}
idHoles[idHoleCount * 2 + 0] = 0xFEC00000ULL;
idHoles[idHoleCount * 2 + 1] = 0xFEE01000ULL;
idHoleCount++;
winHoles[winHoleCount * 2 + 0] = PHYS_MAP_BASE + 0xFEC00000ULL;
winHoles[winHoleCount * 2 + 1] = PHYS_MAP_BASE + 0xFEE01000ULL;
winHoleCount++;
for (UINTN pass = 0; pass < idHoleCount; pass++) {
for (UINTN i = 1; i < idHoleCount; i++) {
if (idHoles[i * 2] < idHoles[(i - 1) * 2]) {
UINT64 s0 = idHoles[(i - 1) * 2 + 0];
UINT64 e0 = idHoles[(i - 1) * 2 + 1];
idHoles[(i - 1) * 2 + 0] = idHoles[i * 2 + 0];
idHoles[(i - 1) * 2 + 1] = idHoles[i * 2 + 1];
idHoles[i * 2 + 0] = s0;
idHoles[i * 2 + 1] = e0;
}
}
if (winHoles[2] < winHoles[0]) {
UINT64 t0 = winHoles[0], t1 = winHoles[1];
winHoles[0] = winHoles[2];
winHoles[1] = winHoles[3];
winHoles[2] = t0;
winHoles[3] = t1;
}
for (UINTN pass = 0; pass < winHoleCount; pass++) {
for (UINTN i = 1; i < winHoleCount; i++) {
if (winHoles[i * 2] < winHoles[(i - 1) * 2]) {
UINT64 s0 = winHoles[(i - 1) * 2 + 0];
UINT64 e0 = winHoles[(i - 1) * 2 + 1];
winHoles[(i - 1) * 2 + 0] = winHoles[i * 2 + 0];
winHoles[(i - 1) * 2 + 1] = winHoles[i * 2 + 1];
winHoles[i * 2 + 0] = s0;
winHoles[i * 2 + 1] = e0;
}
}
}
@@ -791,8 +815,8 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
// memory because of buggy UEFI size reporting.
end += 2 * PAGE_SIZE;
MapRangeSkipping(start, start, end - start, 0x03, idHoles, holeCount);
MapRangeSkipping(PHYS_MAP_BASE + start, start, end - start, 0x03, winHoles, holeCount);
MapRangeSkipping(start, start, end - start, 0x03, idHoles, idHoleCount);
MapRangeSkipping(PHYS_MAP_BASE + start, start, end - start, 0x03, winHoles, winHoleCount);
}
}
}
+69 -18
View File
@@ -76,16 +76,66 @@ __declspec(dllexport) void KePrintConsole(const char* buf, size_t len) {
// can continue running everything else.
// ---------------------------------------------------------------------------
// Return the short exception name for a vector number.
// Return the short exception name for a CPU exception vector.
// Keep this table complete for vectors 0..31 so faults never collapse into
// a vague "general protection" bucket just because the IDT entry existed
// first or the crash printer lacked a name.
static const char* KiFaultVectorName(int vector) {
switch (vector) {
case 0: return "#DE Divide Error";
case 1: return "#DB Debug";
case 2: return "NMI Interrupt";
case 3: return "#BP Breakpoint";
case 4: return "#OF Overflow";
case 5: return "#BR Bound Range Exceeded";
case 6: return "#UD Invalid Opcode";
case 7: return "#NM Device Not Available";
case 8: return "#DF Double Fault";
case 9: return "Coprocessor Segment Overrun";
case 10: return "#TS Invalid TSS";
case 11: return "#NP Segment Not Present";
case 12: return "#SS Stack-Segment Fault";
case 13: return "#GP General Protection";
case 14: return "#PF Page Fault";
case 15: return "Reserved Exception 15";
case 16: return "#MF x87 Floating-Point Exception";
case 17: return "#AC Alignment Check";
case 18: return "#MC Machine Check";
case 19: return "#XM SIMD Floating-Point Exception";
case 20: return "#VE Virtualization Exception";
case 21: return "#CP Control Protection Exception";
case 22: return "Reserved Exception 22";
case 23: return "Reserved Exception 23";
case 24: return "Reserved Exception 24";
case 25: return "Reserved Exception 25";
case 26: return "Reserved Exception 26";
case 27: return "Reserved Exception 27";
case 28: return "#HV Hypervisor Injection Exception";
case 29: return "#VC VMM Communication Exception";
case 30: return "#SX Security Exception";
case 31: return "Reserved Exception 31";
default: return "Unknown Exception";
}
}
static int KiFaultHasErrorCode(int vector) {
switch (vector) {
case 8: // #DF
case 10: // #TS
case 11: // #NP
case 12: // #SS
case 13: // #GP
case 14: // #PF
case 17: // #AC
case 21: // #CP
case 29: // #VC
case 30: // #SX
return 1;
default:
return 0;
}
}
// Walk the process module list to find which image owns an address.
// Returns the module name string, or "unknown" if the address is outside all
// known image ranges. Base/size are optional and are useful for offset dumps.
@@ -391,23 +441,24 @@ __declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* fr
}
if ((frame->Cs & 3) == 0) {
switch ((int)frame->Vector) {
case 0:
KePanic("PANIC: #DE Divide Error | RIP: %016llx\n",
(unsigned long long)frame->Rip);
case 13:
KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n",
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode);
case 14:
KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2);
default:
KePanic("PANIC: Exception #%llu | RIP: %016llx\n",
(unsigned long long)frame->Vector,
(unsigned long long)frame->Rip);
int vector = (int)frame->Vector;
const char* name = KiFaultVectorName(vector);
if (vector == 14) {
KePanic("PANIC: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2);
} else if (KiFaultHasErrorCode(vector)) {
KePanic("PANIC: %s | RIP: %016llx | Code: %04llx\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode);
} else {
KePanic("PANIC: %s | RIP: %016llx\n",
name,
(unsigned long long)frame->Rip);
}
}
+47 -3
View File
@@ -41,6 +41,36 @@ extern void KeIrqStub21(void);
extern void KeIrqStub22(void);
extern void KeIrqStub23(void);
extern void KeFaultDB(void);
extern void KeFaultNMI(void);
extern void KeFaultBP(void);
extern void KeFaultOF(void);
extern void KeFaultBR(void);
extern void KeFaultUD(void);
extern void KeFaultNM(void);
extern void KeFaultDF(void);
extern void KeFaultCSO(void);
extern void KeFaultTS(void);
extern void KeFaultNP(void);
extern void KeFaultSS(void);
extern void KeFaultRSV15(void);
extern void KeFaultMF(void);
extern void KeFaultAC(void);
extern void KeFaultMC(void);
extern void KeFaultXM(void);
extern void KeFaultVE(void);
extern void KeFaultCP(void);
extern void KeFaultRSV22(void);
extern void KeFaultRSV23(void);
extern void KeFaultRSV24(void);
extern void KeFaultRSV25(void);
extern void KeFaultRSV26(void);
extern void KeFaultRSV27(void);
extern void KeFaultHV(void);
extern void KeFaultVC(void);
extern void KeFaultSX(void);
extern void KeFaultRSV31(void);
#define NUM_IRQS 24
// ---------------------------------------------------------------------------
@@ -150,9 +180,23 @@ static void KiIdtSetGate(int vec, uintptr_t handler,
void KeInitializeIdt(void) {
memset(gIdt, 0, sizeof(gIdt));
KiIdtSetGate(0, (uintptr_t)KeFaultDE, 0x8E, 0);
KiIdtSetGate(13, (uintptr_t)KeFaultGP, 0x8E, 0);
KiIdtSetGate(14, (uintptr_t)KeFaultPF, 0x8E, 0);
static void* faultStubs[32] = {
(void*)KeFaultDE, (void*)KeFaultDB, (void*)KeFaultNMI,
(void*)KeFaultBP, (void*)KeFaultOF, (void*)KeFaultBR,
(void*)KeFaultUD, (void*)KeFaultNM, (void*)KeFaultDF,
(void*)KeFaultCSO, (void*)KeFaultTS, (void*)KeFaultNP,
(void*)KeFaultSS, (void*)KeFaultGP, (void*)KeFaultPF,
(void*)KeFaultRSV15, (void*)KeFaultMF, (void*)KeFaultAC,
(void*)KeFaultMC, (void*)KeFaultXM, (void*)KeFaultVE,
(void*)KeFaultCP, (void*)KeFaultRSV22, (void*)KeFaultRSV23,
(void*)KeFaultRSV24, (void*)KeFaultRSV25, (void*)KeFaultRSV26,
(void*)KeFaultRSV27, (void*)KeFaultHV, (void*)KeFaultVC,
(void*)KeFaultSX, (void*)KeFaultRSV31,
};
for (int i = 0; i < 32; i++)
KiIdtSetGate(i, (uintptr_t)faultStubs[i], 0x8E, 0);
KiIdtSetGate(0x40, (uintptr_t)KeSyscallEntry, 0xEE, 0);
static void *irqStubs[NUM_IRQS] = {
+35 -34
View File
@@ -1,34 +1,35 @@
#pragma once
// ke_idt.h - IDT, GDT, and TSS setup
#include <stdint.h>
// 64-bit TSS - minimal; we only care about RSP0.
#pragma pack(push, 1)
typedef struct TSS64 {
uint32_t Reserved0;
uint64_t RSP0; // loaded into RSP on ring-0 entry
uint64_t RSP1;
uint64_t RSP2;
uint64_t Reserved1;
uint64_t IST[7];
uint64_t Reserved2;
uint16_t Reserved3;
uint16_t IoMapBase;
} TSS64;
#pragma pack(pop)
extern TSS64* gTss;
void KeInitializeGdt(void);
void KeInitializeIdt(void);
// Called from hal_timer.c after HPET/APIC timer fires.
void KeTimerInterrupt(void);
// Syscall entry - implemented in ke_syscall.asm.
extern void KeSyscallEntry(void);
// Fault stubs installed by KeInitializeIdt.
extern void KeFaultDE(void); // #DE divide error
extern void KeFaultGP(void); // #GP general protection
extern void KeFaultPF(void); // #PF page fault
#pragma once
// ke_idt.h - IDT, GDT, and TSS setup
#include <stdint.h>
// 64-bit TSS - minimal; we only care about RSP0.
#pragma pack(push, 1)
typedef struct TSS64 {
uint32_t Reserved0;
uint64_t RSP0; // loaded into RSP on ring-0 entry
uint64_t RSP1;
uint64_t RSP2;
uint64_t Reserved1;
uint64_t IST[7];
uint64_t Reserved2;
uint16_t Reserved3;
uint16_t IoMapBase;
} TSS64;
#pragma pack(pop)
extern TSS64* gTss;
void KeInitializeGdt(void);
void KeInitializeIdt(void);
// Called from hal_timer.c after HPET/APIC timer fires.
void KeTimerInterrupt(void);
// Syscall entry - implemented in ke_syscall.asm.
extern void KeSyscallEntry(void);
// Fault stubs installed by KeInitializeIdt.
extern void KeFaultDE(void); // #DE divide error
extern void KeFaultGP(void); // #GP general protection
extern void KeFaultPF(void); // #PF page fault
extern void KeFaultUD(void); // #UD invalid opcode
+19
View File
@@ -477,6 +477,24 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
// -------------------------------------------------------------------------
// Virtual memory mapping invariants
// -------------------------------------------------------------------------
static void KiRunMemoryMappingSelfTest(void) {
PHYSICAL_ADDRESS phys = 0;
uint64_t flags = 0;
KSTATUS s = MmQueryPage(0, &phys, &flags);
if (s != STATUS_NOT_FOUND) {
KePanic("MM selftest: virtual page zero is mapped phys=%016llx flags=%016llx status=%u\n",
(unsigned long long)phys,
(unsigned long long)flags,
(unsigned)s);
}
DbgPrint("MM selftest: page zero unmapped\n");
}
// -------------------------------------------------------------------------
// String-view ABI self-test
// -------------------------------------------------------------------------
@@ -732,6 +750,7 @@ static void KiRunTrapFrameSelfTest(void) {
void KiRunKernelSelfTests(const HAL_API* hal) {
DbgPrint("Kernel selftests: begin\n");
KiRunKernelApcSelfTest();
KiRunMemoryMappingSelfTest();
KiRunStringViewSelfTest();
KiRunIrpKernelApcSelfTest(hal);
KiRunAsyncFileIoSelfTest();
+161
View File
@@ -377,6 +377,167 @@ pf_call:
call KeFaultDispatchFrame
KeFaultPF ENDP
; ---------------------------------------------------------
; Generic CPU exception stubs.
; Each vector gets its own IDT entry and vector number so faults like #UD
; do not fall through an absent descriptor and reappear as #GP.
; ---------------------------------------------------------
FAULT_NOERR MACRO StubName, VecNum
LOCAL noerr_kernel_frame, noerr_call
StubName PROC
push rax
push r11
mov r11, rsp
sub rsp, KFAULT_SIZE
mov rax, rsp
mov [rax + KF_R10], r10
mov r10, KFAULT_MAGIC
mov [rax + KF_Magic], r10
mov qword ptr [rax + KF_Vector], VecNum
mov qword ptr [rax + KF_ErrorCode], 0
mov qword ptr [rax + KF_Cr2], 0
mov [rax + KF_Rcx], rcx
mov [rax + KF_Rdx], rdx
mov [rax + KF_Rbx], rbx
mov [rax + KF_Rbp], rbp
mov [rax + KF_Rsi], rsi
mov [rax + KF_Rdi], rdi
mov [rax + KF_R8], r8
mov [rax + KF_R9], r9
mov r10, [r11]
mov [rax + KF_R11], r10
mov r10, [r11+8]
mov [rax + KF_Rax], r10
mov [rax + KF_R12], r12
mov [rax + KF_R13], r13
mov [rax + KF_R14], r14
mov [rax + KF_R15], r15
lea rdx, [r11+16]
mov r10, [rdx]
mov [rax + KF_Rip], r10
mov r10, [rdx+8]
mov [rax + KF_Cs], r10
mov r10, [rdx+16]
mov [rax + KF_Rflags], r10
mov r10, [rax + KF_Cs]
test r10, 3
jz noerr_kernel_frame
mov r10, [rdx+24]
mov [rax + KF_Rsp], r10
mov r10, [rdx+32]
mov [rax + KF_Ss], r10
jmp noerr_call
noerr_kernel_frame:
mov qword ptr [rax + KF_Rsp], 0
mov qword ptr [rax + KF_Ss], 0
noerr_call:
and rsp, -16
sub rsp, FAULT_CALL_SHADOW
mov rcx, rax
call KeFaultDispatchFrame
StubName ENDP
ENDM
FAULT_ERR MACRO StubName, VecNum
LOCAL err_kernel_frame, err_call
StubName PROC
push rax
push r11
mov r11, rsp
sub rsp, KFAULT_SIZE
mov rax, rsp
mov [rax + KF_R10], r10
mov r10, KFAULT_MAGIC
mov [rax + KF_Magic], r10
mov qword ptr [rax + KF_Vector], VecNum
mov qword ptr [rax + KF_Cr2], 0
mov [rax + KF_Rcx], rcx
mov [rax + KF_Rdx], rdx
mov [rax + KF_Rbx], rbx
mov [rax + KF_Rbp], rbp
mov [rax + KF_Rsi], rsi
mov [rax + KF_Rdi], rdi
mov [rax + KF_R8], r8
mov [rax + KF_R9], r9
mov r10, [r11]
mov [rax + KF_R11], r10
mov r10, [r11+8]
mov [rax + KF_Rax], r10
mov [rax + KF_R12], r12
mov [rax + KF_R13], r13
mov [rax + KF_R14], r14
mov [rax + KF_R15], r15
lea rdx, [r11+16]
mov r10, [rdx]
mov [rax + KF_ErrorCode], r10
mov r10, [rdx+8]
mov [rax + KF_Rip], r10
mov r10, [rdx+16]
mov [rax + KF_Cs], r10
mov r10, [rdx+24]
mov [rax + KF_Rflags], r10
mov r10, [rax + KF_Cs]
test r10, 3
jz err_kernel_frame
mov r10, [rdx+32]
mov [rax + KF_Rsp], r10
mov r10, [rdx+40]
mov [rax + KF_Ss], r10
jmp err_call
err_kernel_frame:
mov qword ptr [rax + KF_Rsp], 0
mov qword ptr [rax + KF_Ss], 0
err_call:
and rsp, -16
sub rsp, FAULT_CALL_SHADOW
mov rcx, rax
call KeFaultDispatchFrame
StubName ENDP
ENDM
FAULT_NOERR KeFaultDB, 1
FAULT_NOERR KeFaultNMI, 2
FAULT_NOERR KeFaultBP, 3
FAULT_NOERR KeFaultOF, 4
FAULT_NOERR KeFaultBR, 5
FAULT_NOERR KeFaultUD, 6
FAULT_NOERR KeFaultNM, 7
FAULT_ERR KeFaultDF, 8
FAULT_NOERR KeFaultCSO, 9
FAULT_ERR KeFaultTS, 10
FAULT_ERR KeFaultNP, 11
FAULT_ERR KeFaultSS, 12
FAULT_NOERR KeFaultRSV15, 15
FAULT_NOERR KeFaultMF, 16
FAULT_ERR KeFaultAC, 17
FAULT_NOERR KeFaultMC, 18
FAULT_NOERR KeFaultXM, 19
FAULT_NOERR KeFaultVE, 20
FAULT_ERR KeFaultCP, 21
FAULT_NOERR KeFaultRSV22, 22
FAULT_NOERR KeFaultRSV23, 23
FAULT_NOERR KeFaultRSV24, 24
FAULT_NOERR KeFaultRSV25, 25
FAULT_NOERR KeFaultRSV26, 26
FAULT_NOERR KeFaultRSV27, 27
FAULT_NOERR KeFaultHV, 28
FAULT_ERR KeFaultVC, 29
FAULT_ERR KeFaultSX, 30
FAULT_NOERR KeFaultRSV31, 31
EXTERN KeIrqDispatch:PROC
EXTERN KiEnterInterrupt:PROC
EXTERN KiExitInterrupt:PROC
+38 -1
View File
@@ -209,7 +209,44 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; }
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strncmp(cmd, "crash", 5) == 0) {
// Check the sub-option trailing "crash " (offset 6)
if (strcmp(cmd + 6, "void") == 0) {
// Jumping into the void
void (*invalid_func)(void) = NULL;
invalid_func();
return 0;
}
else if (strcmp(cmd + 6, "noncanonical") == 0) {
// Non-canonical pointer dereference
volatile int *ptr = (volatile int*)0x4242424242424242ULL;
*ptr = 1337;
return 0;
}
else if (strcmp(cmd + 6, "ud2") == 0) {
__ud2();
return 0;
}
else if (strcmp(cmd + 6, "nx") == 0) {
// NX / DEP Violation
unsigned char code[] = { 0xC3 };
void (*execute_stack)(void) = (void (*)(void))code;
execute_stack();
return 0;
}
else if (strcmp(cmd + 6, "divzero") == 0) {
// Division by zero
volatile int zero = 0;
volatile int result = 1 / zero;
(void)result;
return 0;
}
else {
// Default fallback if just "crash" or an unknown sub-option is passed
*(volatile int*)0 = 42;
return 0;
}
}
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }