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:
1 parent
1f5477cf66
commit
b7fcfce6f7
140 files changed
+7649
-3943
No files matched your search
@@ -0,0 +1,151 @@
|
||||
// Native exception runtime smoke test. This file verifies process-start registration and handler dispatch.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/exception.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
static volatile uint32_t gSearchHits;
|
||||
static volatile uint32_t gContinueHits;
|
||||
|
||||
// Prints one NUL-terminated string to the kernel console.
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
// Prints one unsigned integer.
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (v == 0) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (v && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i--) {
|
||||
char c = buf[i];
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
}
|
||||
|
||||
// Terminates the current process.
|
||||
static void exit_now(int32_t code) {
|
||||
for (;;) SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Fails the test with one message.
|
||||
static void fail(const char* message) {
|
||||
print("native_exception_runtime_smoke: ");
|
||||
print(message);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Fails the test with one status value.
|
||||
static void fail_status(const char* what, uint32_t status) {
|
||||
print("native_exception_runtime_smoke: ");
|
||||
print(what);
|
||||
print(" status=");
|
||||
print_u64(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Requires one syscall to return the expected status.
|
||||
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
|
||||
if (got != expected) {
|
||||
print("native_exception_runtime_smoke: ");
|
||||
print(what);
|
||||
print(" expected=");
|
||||
print_u64(expected);
|
||||
print(" got=");
|
||||
print_u64(got);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the native runtime installed the process dispatcher during startup.
|
||||
static void verify_startup_registration(void) {
|
||||
SYS_EXCEPTION_DISPATCHER_REQUEST request;
|
||||
for (size_t i = 0; i < sizeof(request); i++)
|
||||
((char*)&request)[i] = 0;
|
||||
request.Size = sizeof(request);
|
||||
|
||||
uint32_t status = SysGetExceptionDispatcher(&request);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
fail_status("get dispatcher", status);
|
||||
if (!request.DispatcherRoutine)
|
||||
fail("runtime dispatcher was not registered");
|
||||
}
|
||||
|
||||
// Verifies one breakpoint exception record has the expected native shape.
|
||||
static void verify_breakpoint_record(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context) {
|
||||
if (!record || !context)
|
||||
fail("handler received null state");
|
||||
if (record->Size != sizeof(*record) || record->Version != SYS_EXCEPTION_ABI_VERSION)
|
||||
fail("record ABI mismatch");
|
||||
if (context->Size != sizeof(*context) || context->Version != SYS_EXCEPTION_ABI_VERSION)
|
||||
fail("context ABI mismatch");
|
||||
if (record->Code != SYS_EXCEPTION_CODE_BREAKPOINT)
|
||||
fail("unexpected exception code");
|
||||
}
|
||||
|
||||
// Records a first-chance exception and asks the dispatcher to continue searching.
|
||||
static int search_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
|
||||
if (userContext != (void*)(uintptr_t)0x1111)
|
||||
fail("search handler context mismatch");
|
||||
verify_breakpoint_record(record, context);
|
||||
gSearchHits++;
|
||||
return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// Records a first-chance exception and asks the kernel to resume the saved context.
|
||||
static int continue_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
|
||||
if (userContext != (void*)(uintptr_t)0x2222)
|
||||
fail("continue handler context mismatch");
|
||||
verify_breakpoint_record(record, context);
|
||||
gContinueHits++;
|
||||
return NTL_EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
// Verifies the process-local handler list can dispatch and remove handlers.
|
||||
static void verify_handler_dispatch(void) {
|
||||
expect_status("remove null", NtlRemoveExceptionHandler(0), NTLSTATUS_INVALID_PARAMETER);
|
||||
if (NtlAddExceptionHandler(0, 0, 0))
|
||||
fail("null handler was accepted");
|
||||
|
||||
void* search = NtlAddExceptionHandler(0, search_handler, (void*)(uintptr_t)0x1111);
|
||||
void* cont = NtlAddExceptionHandler(0, continue_handler, (void*)(uintptr_t)0x2222);
|
||||
if (!search || !cont)
|
||||
fail("handler registration failed");
|
||||
|
||||
__debugbreak();
|
||||
if (gSearchHits != 1 || gContinueHits != 1)
|
||||
fail("breakpoint did not visit both handlers");
|
||||
|
||||
expect_status("remove search", NtlRemoveExceptionHandler(search), NTLSTATUS_SUCCESS);
|
||||
__debugbreak();
|
||||
if (gSearchHits != 1 || gContinueHits != 2)
|
||||
fail("removed handler still participated");
|
||||
|
||||
expect_status("remove continue", NtlRemoveExceptionHandler(cont), NTLSTATUS_SUCCESS);
|
||||
expect_status("remove continue twice", NtlRemoveExceptionHandler(cont), NTLSTATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Runs the native exception runtime smoke test.
|
||||
int main(void) {
|
||||
print("native_exception_runtime_smoke: start\n");
|
||||
|
||||
verify_startup_registration();
|
||||
verify_handler_dispatch();
|
||||
|
||||
print("native_exception_runtime_smoke: PASS\n");
|
||||
exit_now(0);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user