implement user mode catchable exceptions, refactor system status codes to use NTLSTATUS_ prefix, update build scripts to use shared literal macros for linker arguments, update named queue lifetime cleanup to include KeNamedQueueUnpublishHandle.

This commit is contained in:
maksw committed 2026-06-27 20:38:43 +02:00
1 parent 1f5477cf66
commit b7fcfce6f7
140 files changed
+7649 -3943

No files matched your search

+95 -156
View File
@@ -8,6 +8,7 @@
#include <kernel/hal/hal_apic.h>
#include <intrin.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_exception.h>
#include <kernel/ke_cpu.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_process.h>
@@ -220,12 +221,12 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
// Flushes debug Log To File.
__declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) {
if (!gDebugLogBuffer || gDebugLogSize == 0 || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
return NTLSTATUS_INVALID_PARAMETER;
if (!KeCanWait())
return STATUS_INVALID_IRQL;
return NTLSTATUS_INVALID_IRQL;
if (_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 1, 0) != 0)
return STATUS_PENDING;
return NTLSTATUS_PENDING;
size_t used;
size_t start;
@@ -236,7 +237,7 @@ __declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) {
if (used == 0) {
_InterlockedExchange((volatile long*)&gDebugLogFlushing, 0);
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
(void)IoDeleteFile(path);
@@ -264,7 +265,7 @@ __declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) {
IoCloseFile(file);
_InterlockedExchange((volatile long*)&gDebugLogFlushing, 0);
return (KSUCCESS(st) && totalWritten == used) ? STATUS_SUCCESS : st;
return (KSUCCESS(st) && totalWritten == used) ? NTLSTATUS_SUCCESS : st;
}
// Attempts to persist the debug log during a panic when the filesystem is usable.
@@ -348,73 +349,6 @@ static void KiFaultPrintf(const char* format, ...) {
va_end(args);
}
// KeBugCheckProcess - user-mode fault handler
// Resolves the faulting RIP to a module name, prints a yellow crash banner,
// kills the offending process, then exits the current thread so the scheduler
// can continue running everything else.
// 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";
}
}
// Implements the fault Has Error Code operation.
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.
@@ -703,21 +637,9 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
KiDumpUserStack(proc, (uintptr_t)f->Rsp);
}
static KSTATUS KiFaultExitStatus(int vector) {
switch (vector) {
case 0: return STATUS_PROCESS_DIVIDE_BY_ZERO;
case 3: return STATUS_PROCESS_BREAKPOINT;
case 6: return STATUS_PROCESS_ILLEGAL_OPCODE;
case 13: return STATUS_PROCESS_GENERAL_PROTECTION;
case 14: return STATUS_PROCESS_PAGE_FAULT;
case 17: return STATUS_PROCESS_ALIGNMENT_FAULT;
default: return STATUS_PROCESS_UNKNOWN_FAULT;
}
}
static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2) {
const char* faultName = KiFaultVectorName(vector);
const char* faultName = KeExceptionVectorName((uint32_t)vector);
const char* procName = proc ? proc->Name : "unknown";
uint32_t pid = proc ? proc->ProcessId : 0;
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
@@ -768,11 +690,79 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u
KiPrintCrashHeader(proc, vector, rip, errCode, cr2);
KiDumpModules(proc);
KiFinishFaultedProcess(proc, thread, KiFaultExitStatus(vector));
KiFinishFaultedProcess(proc, thread, KeExceptionProcessExitStatus((uint32_t)vector));
}
// Dispatches a CPU fault frame and returns only when a user #PF was repaired.
__declspec(dllexport) KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) {
// Panics after printing a kernel-mode CPU exception report.
static _Noreturn void KiPanicKernelFault(PROCESS* proc, THREAD* thread, const KFAULT_FRAME* frame) {
KiBeginFatalReport();
int vector = (int)frame->Vector;
const char* name = KeExceptionVectorName((uint32_t)vector);
DbgPrint("FAULT: kernel exception vector=%d name=%s rip=%016llx rsp=%016llx err=%04llx cr2=%016llx thread=0x%016llX pid=%u proc=%s\n",
vector,
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Rsp,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2,
thread,
proc ? proc->ProcessId : 0,
proc ? proc->Name : "<none>");
KiDumpFaultFrame(proc, frame);
if (vector == 14) {
MmDebugDescribeAddress((uintptr_t)frame->Cr2);
if (frame->Rip) MmDebugDescribeAddress((uintptr_t)frame->Rip);
if (frame->Rsp) MmDebugDescribeAddress((uintptr_t)frame->Rsp);
MmVadDebugDumpProcess(proc);
}
if (vector == 14) {
KePanic(KE_PANIC_CODE_FAULT, "PANIC: %s | RIP: %016llx | RSP: %016llx | Code: %04llx | CR2: %016llx\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Rsp,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2);
} else if (KeExceptionVectorHasErrorCode((uint32_t)vector)) {
const uint8_t* ip = (const uint8_t*)(uintptr_t)frame->Rip;
KePanic(KE_PANIC_CODE_FAULT,
"PANIC: %s | RIP: %016llx | Code: %04llx | CS=%04llx RFLAGS=%016llx RSP=%016llx | bytes=%02x %02x %02x %02x %02x %02x %02x %02x\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cs,
(unsigned long long)frame->Rflags,
(unsigned long long)frame->Rsp,
ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]);
} else {
const uint8_t* ip = (const uint8_t*)(uintptr_t)frame->Rip;
KePanic(KE_PANIC_CODE_FAULT,
"PANIC: %s | RIP: %016llx | CS=%04llx RFLAGS=%016llx RSP=%016llx | bytes=%02x %02x %02x %02x %02x %02x %02x %02x\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Cs,
(unsigned long long)frame->Rflags,
(unsigned long long)frame->Rsp,
ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]);
}
}
// Terminates the current process after printing an unhandled user exception report.
static _Noreturn void KiTerminateUserFault(PROCESS* proc, THREAD* thread, const KFAULT_FRAME* frame) {
KiPrintCrashHeader(proc, (int)frame->Vector,
(uintptr_t)frame->Rip,
(uintptr_t)frame->ErrorCode,
(uintptr_t)frame->Cr2);
KiDumpFaultFrame(proc, frame);
KiDumpModules(proc);
KiFinishFaultedProcess(proc, thread, KeExceptionProcessExitStatus((uint32_t)frame->Vector));
}
// Dispatches a CPU fault frame and returns only when a page fault was repaired.
__declspec(dllexport) KSTATUS KeFaultDispatchFrame(KFAULT_FRAME* frame) {
// Panic NMIs are broadcast to stop all other CPUs. Do not try to decode
// the interrupted context on a secondary CPU once another CPU owns panic.
if (KiPanicIsActive())
@@ -781,93 +771,42 @@ __declspec(dllexport) KSTATUS KeFaultDispatchFrame(const KFAULT_FRAME* frame) {
THREAD* thread = KeGetCurrentThread();
PROCESS* proc = thread ? thread->Owner : (PROCESS*)0;
if (!frame || frame->Magic != 0x3146544C5541464BULL) {
if (!KeFaultFrameIsValid(frame)) {
KePanic(KE_PANIC_CODE_FAULT, "PANIC: bad KFAULT_FRAME %016llx\n",
(unsigned long long)(uintptr_t)frame);
}
if (frame->Vector == 14) {
KSTATUS handled = MmVadResolvePageFaultByAddress((uintptr_t)frame->Cr2, frame->ErrorCode);
if (KSUCCESS(handled))
return STATUS_SUCCESS;
}
KSTATUS repaired = KeExceptionResolveRecoverableFault(frame);
if (KSUCCESS(repaired))
return NTLSTATUS_SUCCESS;
if ((frame->Cs & 3) == 0) {
KiBeginFatalReport();
if (!KeFaultFrameFromUser(frame))
KiPanicKernelFault(proc, thread, frame);
int vector = (int)frame->Vector;
const char* name = KiFaultVectorName(vector);
KSTATUS delivered = KeDeliverUserException(frame);
if (KSUCCESS(delivered))
return NTLSTATUS_SUCCESS;
DbgPrint("FAULT: kernel exception vector=%d name=%s rip=%016llx rsp=%016llx err=%04llx cr2=%016llx thread=%p pid=%u proc=%s\n",
vector,
name,
if (delivered != NTLSTATUS_NOT_FOUND)
DbgPrint("USER/EXC: delivery failed vector=%llu rip=%016llx rsp=%016llx status=%u\n",
frame->Vector,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Rsp,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2,
thread,
proc ? proc->ProcessId : 0,
proc ? proc->Name : "<none>");
KiDumpFaultFrame(proc, frame);
if (vector == 14) {
MmDebugDescribeAddress((uintptr_t)frame->Cr2);
if (frame->Rip) MmDebugDescribeAddress((uintptr_t)frame->Rip);
if (frame->Rsp) MmDebugDescribeAddress((uintptr_t)frame->Rsp);
MmVadDebugDumpProcess(proc);
}
delivered);
if (vector == 14) {
KePanic(KE_PANIC_CODE_FAULT, "PANIC: %s | RIP: %016llx | RSP: %016llx | Code: %04llx | CR2: %016llx\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Rsp,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2);
} else if (KiFaultHasErrorCode(vector)) {
const uint8_t* ip = (const uint8_t*)(uintptr_t)frame->Rip;
KePanic(KE_PANIC_CODE_FAULT,
"PANIC: %s | RIP: %016llx | Code: %04llx | CS=%04llx RFLAGS=%016llx RSP=%016llx | bytes=%02x %02x %02x %02x %02x %02x %02x %02x\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cs,
(unsigned long long)frame->Rflags,
(unsigned long long)frame->Rsp,
ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]);
} else {
const uint8_t* ip = (const uint8_t*)(uintptr_t)frame->Rip;
KePanic(KE_PANIC_CODE_FAULT,
"PANIC: %s | RIP: %016llx | CS=%04llx RFLAGS=%016llx RSP=%016llx | bytes=%02x %02x %02x %02x %02x %02x %02x %02x\n",
name,
(unsigned long long)frame->Rip,
(unsigned long long)frame->Cs,
(unsigned long long)frame->Rflags,
(unsigned long long)frame->Rsp,
ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]);
}
}
KiPrintCrashHeader(proc, (int)frame->Vector,
(uintptr_t)frame->Rip,
(uintptr_t)frame->ErrorCode,
(uintptr_t)frame->Cr2);
KiDumpFaultFrame(proc, frame);
KiDumpModules(proc);
KiFinishFaultedProcess(proc, thread, KiFaultExitStatus((int)frame->Vector));
KiTerminateUserFault(proc, thread, frame);
}
// KeFaultDispatch - single C entry point for all three IDT fault stubs.
// The asm stubs (ke_syscall.asm) collect the CPU exception frame values and
// call here. We check the CS RPL to decide: kernel fault → panic and halt,
// user fault → KeBugCheckProcess (kill process, resume scheduler).
// The asm stubs collect the CPU exception frame values and call here.
// KeFaultDispatchFrame owns the mode split and default unhandled policy.
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2,
uintptr_t cs) {
KFAULT_FRAME frame;
memset(&frame, 0, sizeof(frame));
frame.Magic = 0x3146544C5541464BULL;
frame.Magic = KFAULT_FRAME_MAGIC_VALUE;
frame.Vector = (uint64_t)vector;
frame.ErrorCode = errCode;
frame.Cr2 = cr2;