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

+14 -7
View File
@@ -173,7 +173,7 @@ static int doom_eof_wrapper(void* handle)
static void doom_gettime_wrapper(int* sec, int* usec)
{
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS || !frequency) {
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS || !frequency) {
*sec = 0;
*usec = 0;
return;
@@ -278,7 +278,7 @@ static bool g_prev_key[KEYMAP_COUNT];
static bool ntlite_key_down(INPUT_KEY key)
{
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
static void poll_input(void)
@@ -309,9 +309,16 @@ static uint32_t g_surface_pitch_pixels;
static void init_surface(void)
{
uint32_t s = SysCreateSurface(DISP_W, DISP_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &g_surface_handle);
if (s != STATUS_SUCCESS || !g_surface_handle) {
SYS_CREATE_SURFACE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Width = DISP_W;
req.Height = DISP_H;
req.Format = SYS_SURFACE_FORMAT_XRGB8888;
req.Flags = SYS_SURFACE_FLAG_NONE;
req.HandleOut = &g_surface_handle;
uint32_t s = SysCreateSurface(&req);
if (s != NTLSTATUS_SUCCESS || !g_surface_handle) {
doom_print_wrapper("doom: SysCreateSurface failed\n");
SysExitProcess(1);
}
@@ -319,7 +326,7 @@ static void init_surface(void)
void* base = 0;
uint32_t pitchBytes = 0;
s = SysMapSurface(g_surface_handle, &base, &pitchBytes);
if (s != STATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
if (s != NTLSTATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
doom_print_wrapper("doom: SysMapSurface failed\n");
SysExitProcess(1);
}
@@ -359,7 +366,7 @@ static void render_frame(const uint8_t* rgba)
DISPLAY_RECT rect = { 0, 0, DISP_W, DISP_H };
uint32_t code = SysPresentSurface(g_surface_handle, &rect, 1);
if (code != STATUS_SUCCESS) {
if (code != NTLSTATUS_SUCCESS) {
SysExitProcess(code);
}
}
+1 -1
View File
@@ -508,7 +508,7 @@ static void editor_loop(void) {
render();
INPUT_KEY key = INPUT_KEY_NONE;
if (SysReadKey(&key) != STATUS_SUCCESS) continue;
if (SysReadKey(&key) != NTLSTATUS_SUCCESS) continue;
uint32_t mods = SysGetKeyModifiers();
bool ctrl = (mods & INPUT_MOD_CTRL) != 0;
+8 -8
View File
@@ -160,7 +160,7 @@ static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) {
address->Size = sizeof(*address);
address->Family = SYS_AF_INET;
memcpy(address->Address, ip, 4);
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
size_t resolvedSize = sizeof(*address);
@@ -173,8 +173,8 @@ static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) {
req.AddressSizeInOut = &resolvedSize;
uint32_t st = SysResolveHost(&req);
if (st == STATUS_SUCCESS)
return STATUS_SUCCESS;
if (st == NTLSTATUS_SUCCESS)
return NTLSTATUS_SUCCESS;
resolvedSize = sizeof(*address);
memset(address, 0, sizeof(*address));
@@ -224,7 +224,7 @@ int main(void) {
}
uint32_t st = resolve_host(host, &addr);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("httpget: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
usage();
SysExitProcess(1);
@@ -242,7 +242,7 @@ int main(void) {
uint32_t sock = 0;
st = SysCreateSocket(addr.Family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("httpget: SysCreateSocket failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
@@ -250,7 +250,7 @@ int main(void) {
addr.Port = 80;
st = SysConnectSocket(sock, &addr, sizeof(addr));
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("httpget: SysConnectSocket failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
@@ -269,7 +269,7 @@ int main(void) {
while (totalSent < off) {
size_t sent = 0;
st = SysSendSocket(sock, request + totalSent, off - totalSent, 0, &sent);
if (st != STATUS_SUCCESS || sent == 0) {
if (st != NTLSTATUS_SUCCESS || sent == 0) {
print("httpget: SysSendSocket failed "); print_u64(st); print(" sent="); print_u64(sent); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
@@ -282,7 +282,7 @@ int main(void) {
for (;;) {
size_t got = 0;
st = SysRecvSocket(sock, buf, sizeof(buf) - 1, 0, &got);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("\nhttpget: SysRecvSocket failed "); print_u64(st); print(" after "); print_u64(total); print(" bytes\n");
SysCloseHandle(sock);
SysExitProcess(1);
+2 -2
View File
@@ -30,8 +30,8 @@ int main(void) {
char metricsBuf[128];
while (true) {
if (SysGetConsoleInfo(&console) == STATUS_SUCCESS &&
SysGetMemoryStatus(&mem) == STATUS_SUCCESS) {
if (SysGetConsoleInfo(&console) == NTLSTATUS_SUCCESS &&
SysGetMemoryStatus(&mem) == NTLSTATUS_SUCCESS) {
// If the system isn't reporting columns correctly, default to a safe standard width
int columns = (console.Columns > 0) ? (int)console.Columns : 80;
+10 -10
View File
@@ -133,7 +133,7 @@ static bool parse_u32(const char* s, uint32_t* out) {
// Implements the resolve host operation.
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
if (parse_ipv4(host, ip))
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
SYS_SOCKET_ADDRESS resolved;
size_t resolvedSize = sizeof(resolved);
@@ -146,16 +146,16 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
req.AddressSizeInOut = &resolvedSize;
uint32_t st = SysResolveHost(&req);
if (st != STATUS_SUCCESS)
if (st != NTLSTATUS_SUCCESS)
return st;
if (resolved.Family != SYS_AF_INET)
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
ip[0] = resolved.Address[0];
ip[1] = resolved.Address[1];
ip[2] = resolved.Address[2];
ip[3] = resolved.Address[3];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
// Implements the icmp checksum operation.
@@ -258,7 +258,7 @@ int main(void) {
uint8_t ip[4];
uint32_t st = resolve_host(host, ip);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("ping: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
SysExitProcess(1);
}
@@ -268,7 +268,7 @@ int main(void) {
uint32_t sock = 0;
st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_RAW, SYS_IPPROTO_ICMP, &sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("ping: SysCreateSocket(raw icmp) failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
@@ -283,7 +283,7 @@ int main(void) {
uint64_t tickFrequency = 0;
st = SysGetTickFrequency(&tickFrequency);
if (st != STATUS_SUCCESS || !tickFrequency) {
if (st != NTLSTATUS_SUCCESS || !tickFrequency) {
print("ping: SysGetTickFrequency failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
@@ -323,7 +323,7 @@ int main(void) {
uint64_t start = SysGetTick();
st = SysSendToSocket(sock, &sendReq);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("ping: send failed seq="); print_u64(seq); print(" status="); print_u64(st); print("\n");
if (i + 1 < count) SysSleep(1000);
continue;
@@ -346,9 +346,9 @@ int main(void) {
recvReq.ReceivedOut = &got;
st = SysRecvFromSocket(sock, &recvReq);
if (st == STATUS_TIMEOUT)
if (st == NTLSTATUS_TIMEOUT)
break;
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("ping: recv failed status="); print_u64(st); print("\n");
break;
}
+2 -2
View File
@@ -109,12 +109,12 @@ int main(void) {
req.AddressSizeInOut = &addrSize;
uint32_t st = SysResolveHost(&req);
if (st != STATUS_SUCCESS && family == 0) {
if (st != NTLSTATUS_SUCCESS && family == 0) {
req.Family = SYS_AF_INET;
addrSize = sizeof(addr);
st = SysResolveHost(&req);
}
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("resolve: SysResolveHost failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
+44 -18
View File
@@ -138,14 +138,14 @@ static void printUInt64Width(uint64_t n, size_t width) {
// Names nt-lite process-abort exit statuses.
static const char* processExitStatusName(uint32_t code) {
switch (code) {
case STATUS_PROCESS_KILLED: return "killed";
case STATUS_PROCESS_PAGE_FAULT: return "page-fault";
case STATUS_PROCESS_GENERAL_PROTECTION: return "general-protection";
case STATUS_PROCESS_ILLEGAL_OPCODE: return "illegal-opcode";
case STATUS_PROCESS_DIVIDE_BY_ZERO: return "divide-by-zero";
case STATUS_PROCESS_BREAKPOINT: return "breakpoint";
case STATUS_PROCESS_ALIGNMENT_FAULT: return "alignment-fault";
case STATUS_PROCESS_UNKNOWN_FAULT: return "unknown-fault";
case NTLSTATUS_PROCESS_KILLED: return "killed";
case NTLSTATUS_PROCESS_PAGE_FAULT: return "page-fault";
case NTLSTATUS_PROCESS_GENERAL_PROTECTION: return "general-protection";
case NTLSTATUS_PROCESS_ILLEGAL_OPCODE: return "illegal-opcode";
case NTLSTATUS_PROCESS_DIVIDE_BY_ZERO: return "divide-by-zero";
case NTLSTATUS_PROCESS_BREAKPOINT: return "breakpoint";
case NTLSTATUS_PROCESS_ALIGNMENT_FAULT: return "alignment-fault";
case NTLSTATUS_PROCESS_UNKNOWN_FAULT: return "unknown-fault";
default: return NULL;
}
}
@@ -527,7 +527,7 @@ static bool readLine(char* buf, int len) {
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
inputReq.RecordOut = &input;
if (SysReadInputEvent(&inputReq) != STATUS_SUCCESS) { SysYield(); continue; }
if (SysReadInputEvent(&inputReq) != NTLSTATUS_SUCCESS) { SysYield(); continue; }
INPUT_KEY key = input.Key;
if (key == INPUT_KEY_NONE) { SysYield(); continue; }
@@ -653,6 +653,7 @@ static void cmd_stats(void) {
SYS_DISK_STATS disk;
SYS_PTE_STATS pte;
SYS_SURFACE_STATS surface;
SYS_EXCEPTION_STATS exceptionStats;
uint32_t status;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &sched, sizeof(sched), NULL);
@@ -682,7 +683,14 @@ static void cmd_stats(void) {
size_t cpuPages = (cpuSize + 4095u) / 4096u;
if (cpuPages == 0) cpuPages = 1;
status = SysAllocVm(0, cpuPages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &cpuBuffer);
SYS_ALLOC_VM_REQUEST allocReq;
memset(&allocReq, 0, sizeof(allocReq));
allocReq.Size = sizeof(allocReq);
allocReq.PageCount = cpuPages;
allocReq.Protect = SYS_VM_READWRITE;
allocReq.Flags = SYS_VM_FLAG_NONE;
allocReq.BaseOut = &cpuBuffer;
status = SysAllocVm(&allocReq);
if (status != 0 || !cpuBuffer) {
print("stats: cpu buffer allocation failed: ");
printUInt(status);
@@ -772,6 +780,15 @@ static void cmd_stats(void) {
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, &exceptionStats, sizeof(exceptionStats), NULL);
if (status != 0) {
print("stats: exception query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
print("Scheduler:\n");
print(" ticks: "); printUInt64(sched.Ticks); print("\n");
print(" context switches: "); printUInt64(sched.ContextSwitches); print("\n");
@@ -785,6 +802,15 @@ static void cmd_stats(void) {
print(" SMT deferrals: "); printUInt64(sched.SmtSiblingDeferrals); print("\n");
print(" SMT primary pokes: "); printUInt64(sched.SmtPrimaryPokes); print("\n");
print("Exceptions:\n");
print(" delivered: "); printUInt64(exceptionStats.Delivered); print("\n");
print(" user-raised: "); printUInt64(exceptionStats.UserRaised); print("\n");
print(" continued: "); printUInt64(exceptionStats.Continued); print("\n");
print(" unhandled: "); printUInt64(exceptionStats.Unhandled); print("\n");
print(" invalid continue: "); printUInt64(exceptionStats.InvalidContinue); print("\n");
print(" max nesting: "); printUInt(exceptionStats.MaxNestedDispatch); print("\n");
print(" last code: "); printUInt(exceptionStats.LastCode); print("\n");
print("CPUs:\n");
const SYS_CPU_STAT* cpuRecords = SYS_CPU_STATS_CONST_RECORDS(cpu);
uint32_t n = cpu->RecordCount;
@@ -1352,11 +1378,11 @@ static void cmd_kill(const char* args) {
uint32_t hProc = 0;
uint32_t status = SysOpenProcess(pid, &hProc);
if (status == 0) {
status = SysKillProcess(hProc, (int32_t)STATUS_PROCESS_KILLED);
status = SysKillProcess(hProc, (int32_t)NTLSTATUS_PROCESS_KILLED);
SysCloseHandle(hProc);
}
if (status == STATUS_ACCESS_DENIED) {
if (status == NTLSTATUS_ACCESS_DENIED) {
print("kill: process ");
printUInt(pid);
print(" is termination-protected.\n");
@@ -1466,7 +1492,7 @@ static bool shellConsumeQueuedCtrlC(void) {
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = 0;
inputReq.RecordOut = &input;
if (SysPeekInputEvent(&inputReq) != STATUS_SUCCESS) return false;
if (SysPeekInputEvent(&inputReq) != NTLSTATUS_SUCCESS) return false;
if ((input.Modifiers & INPUT_MOD_CTRL) && input.Key == INPUT_KEY_C) {
(void)SysReadInputEvent(&inputReq);
@@ -1487,7 +1513,7 @@ static bool shellConsumeQueuedCtrlC(void) {
// Returns whether one input key is currently held.
static bool shellIsKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
// Waits for a foreground process while watching Ctrl-C without stealing ordinary stdin.
@@ -1511,7 +1537,7 @@ static uint32_t waitForegroundProcess(uint32_t hProc) {
for (;;) {
index = 0;
uint32_t status = SysWaitMany(&waitReq);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(inputQueue);
return status;
}
@@ -1525,7 +1551,7 @@ static uint32_t waitForegroundProcess(uint32_t hProc) {
if (((modifiers & INPUT_MOD_CTRL) && shellIsKeyDown(INPUT_KEY_C)) || shellConsumeQueuedCtrlC()) {
if (!sentBreak) {
print("^C\n");
(void)SysKillProcess(hProc, (int32_t)STATUS_PROCESS_KILLED);
(void)SysKillProcess(hProc, (int32_t)NTLSTATUS_PROCESS_KILLED);
sentBreak = true;
}
}
@@ -1624,7 +1650,7 @@ static int runCommand(const char* cmd) {
} else {
// Foreground mode: Wait for completion and flush standard keyboard stream.
uint32_t waitStatus = waitForegroundProcess(hProc);
if (waitStatus != STATUS_SUCCESS) {
if (waitStatus != NTLSTATUS_SUCCESS) {
print("shell: wait failed: ");
printUInt(waitStatus);
print("\n");
@@ -1644,7 +1670,7 @@ static int runCommand(const char* cmd) {
print("\n");
SysCloseHandle(hProc);
INPUT_KEY drainedKey;
while (SysPollKeyboard(&drainedKey) == STATUS_SUCCESS) (void)SysReadKey(&drainedKey);
while (SysPollKeyboard(&drainedKey) == NTLSTATUS_SUCCESS) (void)SysReadKey(&drainedKey);
}
} else {
print("shell: launch failed\n");
+7 -7
View File
@@ -131,7 +131,7 @@ int main(void) {
uint32_t listener = 0;
uint32_t st = SysCreateSocket(family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("tcpserve: SysCreateSocket failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
@@ -143,14 +143,14 @@ int main(void) {
bindAddr.Port = port;
st = SysBindSocket(listener, &bindAddr, sizeof(bindAddr));
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("tcpserve: SysBindSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
}
st = SysListenSocket(listener, 4);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("tcpserve: SysListenSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
@@ -165,7 +165,7 @@ int main(void) {
size_t remoteSize = sizeof(remote);
uint32_t client = 0;
st = SysAcceptSocket(listener, &remote, &remoteSize, &client);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("tcpserve: SysAcceptSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
@@ -178,7 +178,7 @@ int main(void) {
char request[1025];
size_t got = 0;
st = SysRecvSocket(client, request, sizeof(request) - 1, 0, &got);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("tcpserve: SysRecvSocket failed "); print_u64(st); print("\n");
SysCloseHandle(client);
SysCloseHandle(listener);
@@ -217,9 +217,9 @@ int main(void) {
size_t sent = 0;
st = SysSendSocket(client, response, off, 0, &sent);
if (st == STATUS_SUCCESS)
if (st == NTLSTATUS_SUCCESS)
st = SysSendSocket(client, body, sizeof(body) - 1, 0, &sent);
if (st != STATUS_SUCCESS)
if (st != NTLSTATUS_SUCCESS)
print("tcpserve: SysSendSocket failed "), print_u64(st), print("\n");
SysShutdownSocket(client, SYS_SHUTDOWN_BOTH);
+9 -9
View File
@@ -148,7 +148,7 @@ static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) {
out->Size = sizeof(*out);
out->Family = SYS_AF_INET;
memcpy(out->Address, ip, 4);
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
SYS_RESOLVE_HOST_REQUEST req;
@@ -161,8 +161,8 @@ static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) {
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status == STATUS_SUCCESS)
return STATUS_SUCCESS;
if (status == NTLSTATUS_SUCCESS)
return NTLSTATUS_SUCCESS;
addressSize = sizeof(*out);
memset(out, 0, sizeof(*out));
@@ -179,7 +179,7 @@ static int run_send(const char* cmd) {
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0]) { usage(); return 1; }
uint32_t status = resolve_address(tok, &remote);
if (status != STATUS_SUCCESS) { print("udp: resolve failed "); print_u64(status); print("\n"); return 1; }
if (status != NTLSTATUS_SUCCESS) { print("udp: resolve failed "); print_u64(status); print("\n"); return 1; }
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
@@ -197,7 +197,7 @@ static int run_send(const char* cmd) {
uint32_t sock = 0;
uint32_t st = SysCreateSocket(remote.Family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
@@ -215,7 +215,7 @@ static int run_send(const char* cmd) {
st = SysSendToSocket(sock, &req);
SysCloseHandle(sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysSendToSocket failed "); print_u64(st); print("\n");
return 1;
}
@@ -242,7 +242,7 @@ static int run_listen(const char* cmd) {
uint32_t sock = 0;
uint32_t st = SysCreateSocket(family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
@@ -254,7 +254,7 @@ static int run_listen(const char* cmd) {
bindAddr.Port = port;
st = SysBindSocket(sock, &bindAddr, sizeof(bindAddr));
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysBindSocket failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
return 1;
@@ -278,7 +278,7 @@ static int run_listen(const char* cmd) {
st = SysRecvFromSocket(sock, &req);
SysCloseHandle(sock);
if (st != STATUS_SUCCESS) {
if (st != NTLSTATUS_SUCCESS) {
print("udp: SysRecvFromSocket failed "); print_u64(st); print("\n");
return 1;
}
+16 -9
View File
@@ -150,7 +150,7 @@ int main(void) {
uint32_t file = 0;
uint32_t status = open_file_view(pathView, &file);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
char pathBuf[260];
print("video: failed to open input file: ");
print(path_to_cstr(pathView, pathBuf, sizeof(pathBuf)));
@@ -160,7 +160,7 @@ int main(void) {
uint64_t fileSize = 0;
status = SysGetFileSize(file, &fileSize);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(file);
fail_status("SysGetFileSize", status);
}
@@ -172,7 +172,7 @@ int main(void) {
uint32_t mapping = 0;
status = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, fileSize, &mapping);
if (status != STATUS_SUCCESS || !mapping) {
if (status != NTLSTATUS_SUCCESS || !mapping) {
SysCloseHandle(file);
fail_status("SysCreateFileMapping", status);
}
@@ -180,7 +180,7 @@ int main(void) {
void* buf = 0;
status = SysMapFile(mapping, 0, (size_t)fileSize,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &buf);
if (status != STATUS_SUCCESS || !buf) {
if (status != NTLSTATUS_SUCCESS || !buf) {
SysCloseHandle(mapping);
SysCloseHandle(file);
fail_status("SysMapFile", status);
@@ -214,9 +214,16 @@ int main(void) {
}
uint32_t surface = 0;
status = SysCreateSurface(width, height, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (status != STATUS_SUCCESS || !surface) {
SYS_CREATE_SURFACE_REQUEST surfaceReq;
memset(&surfaceReq, 0, sizeof(surfaceReq));
surfaceReq.Size = sizeof(surfaceReq);
surfaceReq.Width = width;
surfaceReq.Height = height;
surfaceReq.Format = SYS_SURFACE_FORMAT_XRGB8888;
surfaceReq.Flags = SYS_SURFACE_FLAG_NONE;
surfaceReq.HandleOut = &surface;
status = SysCreateSurface(&surfaceReq);
if (status != NTLSTATUS_SUCCESS || !surface) {
BinkClose(b);
SysFreeVm(buf);
fail_status("SysCreateSurface", status);
@@ -225,7 +232,7 @@ int main(void) {
void* surfaceBase = 0;
uint32_t pitchBytes = 0;
status = SysMapSurface(surface, &surfaceBase, &pitchBytes);
if (status != STATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) {
if (status != NTLSTATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
@@ -253,7 +260,7 @@ int main(void) {
BinkCopyToBuffer(b, surfaceBase, pitchBytes, height, 0, 0, BINKSURFACE32);
status = SysPresentSurface(surface, &fullFrame, 1);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
+2 -2
View File
@@ -82,7 +82,7 @@ __declspec(dllexport) char* realpath(const char* path, char* resolved_path) {
&required,
};
uint32_t status = SysRealPath(&query);
if (status != STATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
if (required == 0 || required > SYS_CWD_MAX) { errno = ERANGE; return (char*)0; }
char nativeResolved[SYS_CWD_MAX];
@@ -95,7 +95,7 @@ __declspec(dllexport) char* realpath(const char* path, char* resolved_path) {
&required,
};
status = SysRealPath(&request);
if (status != STATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
char posix[SYS_CWD_MAX];
NativeToPosix(nativeResolved, posix, sizeof(posix));
+3 -3
View File
@@ -58,7 +58,7 @@ __declspec(dllexport) void* dlopen(const char* filename, int flags) {
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS || handle == 0) {
if (status != NTLSTATUS_SUCCESS || handle == 0) {
DlSetStatusError("dlopen failed", status);
return 0;
}
@@ -84,7 +84,7 @@ __declspec(dllexport) void* dlsym(void* handle, const char* symbol) {
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
if (status != STATUS_SUCCESS || address == 0) {
if (status != NTLSTATUS_SUCCESS || address == 0) {
DlSetStatusError("dlsym failed", status);
return 0;
}
@@ -102,7 +102,7 @@ __declspec(dllexport) int dlclose(void* handle) {
}
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)handle);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
DlSetStatusError("dlclose failed", status);
return -1;
}
+17 -17
View File
@@ -9,23 +9,23 @@ int errno = 0;
// Implements the status To Errno operation.
int PosixStatusToErrno(uint32_t s) {
switch (s) {
case STATUS_SUCCESS: return 0;
case STATUS_NOT_SUPPORTED: return ENOSYS;
case STATUS_TIMEOUT: return EAGAIN;
case STATUS_WOULD_BLOCK: return EAGAIN;
case STATUS_INVALID_PARAMETER: return EINVAL;
case STATUS_INVALID_HANDLE: return EBADF;
case STATUS_BUFFER_TOO_SMALL: return ERANGE;
case STATUS_NO_MEMORY: return ENOMEM;
case STATUS_ACCESS_DENIED: return EACCES;
case STATUS_NOT_FOUND: return ENOENT;
case STATUS_ALREADY_EXISTS: return EEXIST;
case STATUS_OBJECT_NAME_COLLISION: return EEXIST;
case STATUS_OBJECT_DEAD: return ECONNRESET;
case STATUS_QUEUE_FULL: return EAGAIN;
case STATUS_QUEUE_EMPTY: return EAGAIN;
case STATUS_CANCELLED: return ECANCELED;
case STATUS_NO_MORE_FILES: return ENOENT;
case NTLSTATUS_SUCCESS: return 0;
case NTLSTATUS_NOT_SUPPORTED: return ENOSYS;
case NTLSTATUS_TIMEOUT: return EAGAIN;
case NTLSTATUS_WOULD_BLOCK: return EAGAIN;
case NTLSTATUS_INVALID_PARAMETER: return EINVAL;
case NTLSTATUS_INVALID_HANDLE: return EBADF;
case NTLSTATUS_BUFFER_TOO_SMALL: return ERANGE;
case NTLSTATUS_NO_MEMORY: return ENOMEM;
case NTLSTATUS_ACCESS_DENIED: return EACCES;
case NTLSTATUS_NOT_FOUND: return ENOENT;
case NTLSTATUS_ALREADY_EXISTS: return EEXIST;
case NTLSTATUS_OBJECT_NAME_COLLISION: return EEXIST;
case NTLSTATUS_OBJECT_DEAD: return ECONNRESET;
case NTLSTATUS_QUEUE_FULL: return EAGAIN;
case NTLSTATUS_QUEUE_EMPTY: return EAGAIN;
case NTLSTATUS_CANCELLED: return ECANCELED;
case NTLSTATUS_NO_MORE_FILES: return ENOENT;
default: return EIO;
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ __declspec(dllexport) void clearscreen(void) {
// Implements the clearkeyboard operation.
__declspec(dllexport) void clearkeyboard(void) {
INPUT_KEY key;
while (SysPollKeyboard(&key) == STATUS_SUCCESS) {
while (SysPollKeyboard(&key) == NTLSTATUS_SUCCESS) {
char ignored;
(void)SysReadKeyboardChar(&ignored);
}
@@ -90,7 +90,7 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
char* dst = (char*)buf;
for (size_t i = 0; i < count; i++) {
uint32_t s = SysReadKeyboardChar(&dst[i]);
if (s != STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
if (s != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
}
return (ssize_t)count;
+3 -3
View File
@@ -52,7 +52,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
uint32_t mapping = 0;
uint32_t s = SysCreateFileMapping(e->KHandle, protect, SYS_MAP_FLAG_NONE,
maxSize, &mapping);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
@@ -62,7 +62,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
PosixProtToSysMapAccess(prot, flags),
SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
@@ -73,7 +73,7 @@ __declspec(dllexport) int munmap(void* addr, size_t length) {
(void)length;
if (!addr) { errno = EINVAL; return -1; }
uint32_t s = SysFreeVm(addr);
if (s != STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
if (s != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
+8 -1
View File
@@ -535,7 +535,14 @@ __declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout) {
size_t ready = 0;
uint32_t timeoutMs = timeout < 0 ? SYS_POLL_TIMEOUT_INFINITE : (uint32_t)timeout;
uint32_t s = SysPoll(kfds, nfds, timeoutMs, &ready);
SYS_POLL_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.TimeoutMs = timeoutMs;
req.Fds = kfds;
req.Count = nfds;
req.ReadyOut = &ready;
KSTATUS s = SysPoll(&req);
if (s != 0) {
free(kfds);
errno = PosixStatusToErrno(s);
+531 -48
View File
@@ -23,12 +23,14 @@
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include <ntlite/exception.h>
extern size_t _malloc_usable_size(const void* ptr);
// Types.
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef unsigned long long DWORD64;
typedef unsigned short WORD;
typedef int BOOL;
typedef unsigned char BYTE;
@@ -252,13 +254,13 @@ static uint32_t K32SetCwdNative(const char* path) {
// Implements the k32 Get Cwd Native operation.
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return STATUS_INVALID_PARAMETER;
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return NTLSTATUS_INVALID_PARAMETER;
if (lengthOut) *lengthOut = cwd.Length;
if (!buffer || cwd.Length + 1 > capacity) return STATUS_BUFFER_TOO_SMALL;
if (!buffer || cwd.Length + 1 > capacity) return NTLSTATUS_BUFFER_TOO_SMALL;
if (cwd.Length) memcpy(buffer, cwd.Data, cwd.Length);
buffer[cwd.Length] = '\0';
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
// Implements the k32 Create Process Native operation.
@@ -289,6 +291,7 @@ typedef struct WIN32_THREAD_ENV {
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
void* FlsSlots[FLS_MAX_SLOTS];
void* ExceptionFrameHead;
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
@@ -429,7 +432,7 @@ __declspec(dllexport) void OutputDebugStringA(const char* str) {
// Queries the native monotonic tick frequency.
static uint64_t K32TickFrequency(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS) return 0;
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS) return 0;
return frequency;
}
@@ -525,9 +528,17 @@ __declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
size_t pages = (dwSize + 4095u) / 4096u;
void* base = NULL;
uint32_t flags = lpAddress ? SYS_VM_FLAG_FIXED : SYS_VM_FLAG_NONE;
uint32_t s = SysAllocVm(lpAddress, pages, WinProtectToSysVm(flProtect), flags, &base);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_FOUND || s == STATUS_INVALID_PARAMETER) ? 87 : 8;
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.PreferredBase = lpAddress;
req.PageCount = pages;
req.Protect = WinProtectToSysVm(flProtect);
req.Flags = flags;
req.BaseOut = &base;
KSTATUS s = SysAllocVm(&req);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_FOUND || s == NTLSTATUS_INVALID_PARAMETER) ? 87 : 8;
return NULL;
}
return base;
@@ -538,7 +549,7 @@ __declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwF
(void)dwSize; (void)dwFreeType;
if (!lpAddress) return TRUE;
uint32_t s = SysFreeVm(lpAddress);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -549,7 +560,7 @@ __declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize,
uint32_t oldSysProtect = 0;
uint32_t s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect),
lpflOldProtect ? &oldSysProtect : NULL);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (lpflOldProtect) *lpflOldProtect = SysProtectVmToWin(oldSysProtect);
return TRUE;
}
@@ -564,7 +575,7 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
SYS_VM_REGION_INFO info;
uint32_t s = SysQueryVm((void*)lpAddress, &info);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
gLastError = 87;
return 0;
}
@@ -600,8 +611,8 @@ __declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
SYS_MAP_FLAG_NONE,
maximumSize,
&kh);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
@@ -636,8 +647,8 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
WinFileMapAccessToSys(dwDesiredAccess),
SYS_MAP_FLAG_NONE,
&base);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
@@ -646,7 +657,7 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -663,7 +674,7 @@ __declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwN
// Returns whether one input key is currently held for Win32 adapters.
static bool Win32IsInputKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
// GetAsyncKeyState implementation over the canonical input key namespace.
@@ -731,7 +742,7 @@ __declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
// Drain the keyboard buffer by reading until empty.
INPUT_KEY key;
while (SysPollKeyboard(&key) == STATUS_SUCCESS) {
while (SysPollKeyboard(&key) == NTLSTATUS_SUCCESS) {
char ignored;
(void)SysReadKeyboardChar(&ignored);
}
@@ -832,8 +843,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
uint32_t kh = 0;
uint32_t s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh);
if (s != 0) {
gLastError = (s == STATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == STATUS_NOT_FOUND) ? 2 : 5;
gLastError = (s == NTLSTATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == NTLSTATUS_NOT_FOUND) ? 2 : 5;
return INVALID_HANDLE_VALUE;
}
@@ -1115,7 +1126,7 @@ __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return NULL;
}
@@ -1160,7 +1171,7 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
req.Name = SysSvFromCstr(lpProcName);
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
if (status != STATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
if (status != NTLSTATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
return (void*)address;
}
@@ -1184,7 +1195,7 @@ __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE)
return TRUE;
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)hLibModule);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return FALSE;
}
@@ -1223,7 +1234,7 @@ __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitC
uint32_t s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode);
if (s != 0) {
gLastError = (s == STATUS_INVALID_HANDLE) ? 6 : 5;
gLastError = (s == NTLSTATUS_INVALID_HANDLE) ? 6 : 5;
return FALSE;
}
return TRUE;
@@ -1491,13 +1502,13 @@ static BOOL K32IsConsoleOutputHandle(HANDLE h) {
// Implements the k32 Set Console Last Error operation.
static void K32SetConsoleLastError(uint32_t status) {
switch (status) {
case STATUS_SUCCESS:
case NTLSTATUS_SUCCESS:
gLastError = 0;
break;
case STATUS_INVALID_PARAMETER:
case NTLSTATUS_INVALID_PARAMETER:
gLastError = 87;
break;
case STATUS_NO_MEMORY:
case NTLSTATUS_NO_MEMORY:
gLastError = 8;
break;
default:
@@ -1527,7 +1538,7 @@ __declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
while (i < nNumberOfCharsToRead) {
char c = 0;
if (SysReadKeyboardChar(&c) != STATUS_SUCCESS) continue;
if (SysReadKeyboardChar(&c) != NTLSTATUS_SUCCESS) continue;
// Ignore unmapped keys (Shift, Ctrl, etc.).
if (c == 0) continue;
@@ -1579,7 +1590,7 @@ __declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
SYS_CONSOLE_INFO info;
uint32_t s = SysGetConsoleInfo(&info);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1610,7 +1621,7 @@ __declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
}
uint32_t s = SysSetConsoleCursor((int)dwCursorPosition.X, (int)dwCursorPosition.Y);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1639,7 +1650,7 @@ __declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written;
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1668,7 +1679,7 @@ __declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written;
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -2220,7 +2231,7 @@ __declspec(dllexport) void GetSystemTime(SYSTEMTIME* lpSystemTime) {
if (!lpSystemTime) return;
SYS_SYSTEM_TIME st;
if (SysGetSystemTime(&st) != STATUS_SUCCESS) {
if (SysGetSystemTime(&st) != NTLSTATUS_SUCCESS) {
memset(lpSystemTime, 0, sizeof(*lpSystemTime));
return;
}
@@ -2258,7 +2269,7 @@ __declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime)
SYS_SYSTEM_TIME st;
uint64_t ft;
if (SysGetSystemTime(&st) == STATUS_SUCCESS) {
if (SysGetSystemTime(&st) == NTLSTATUS_SUCCESS) {
ft = K32EpochToFileTime(st.EpochSeconds, st.Nanosecond);
} else {
uint64_t frequency = K32TickFrequency();
@@ -2436,32 +2447,504 @@ __declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
// Exception Handling & Debugging.
#define EXCEPTION_CONTINUE_EXECUTION ((LONG)-1)
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_NONCONTINUABLE 0x1UL
#define EXCEPTION_MAXIMUM_PARAMETERS SYS_EXCEPTION_MAXIMUM_PARAMETERS
#define WIN_VECTORED_MAX_HANDLERS 16u
#define WIN_SEH_MAX_FRAMES 16u
// Implements the add Vectored Exception Handler operation.
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
(void)First; (void)Handler;
// Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler.
return (void*)(uintptr_t)0x56455843UL; // 'VEXC'
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD* ExceptionRecord;
void* ExceptionAddress;
DWORD NumberParameters;
uintptr_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
typedef struct _CONTEXT {
DWORD64 P1Home;
DWORD64 P2Home;
DWORD64 P3Home;
DWORD64 P4Home;
DWORD64 P5Home;
DWORD64 P6Home;
DWORD ContextFlags;
DWORD MxCsr;
WORD SegCs;
WORD SegDs;
WORD SegEs;
WORD SegFs;
WORD SegGs;
WORD SegSs;
DWORD EFlags;
DWORD64 Dr0;
DWORD64 Dr1;
DWORD64 Dr2;
DWORD64 Dr3;
DWORD64 Dr6;
DWORD64 Dr7;
DWORD64 Rax;
DWORD64 Rcx;
DWORD64 Rdx;
DWORD64 Rbx;
DWORD64 Rsp;
DWORD64 Rbp;
DWORD64 Rsi;
DWORD64 Rdi;
DWORD64 R8;
DWORD64 R9;
DWORD64 R10;
DWORD64 R11;
DWORD64 R12;
DWORD64 R13;
DWORD64 R14;
DWORD64 R15;
DWORD64 Rip;
} CONTEXT;
typedef struct _EXCEPTION_POINTERS {
EXCEPTION_RECORD* ExceptionRecord;
CONTEXT* ContextRecord;
} EXCEPTION_POINTERS;
typedef LONG (*PVECTORED_EXCEPTION_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo);
typedef LONG (*PRTL_EXCEPTION_FRAME_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo, void* UserContext);
typedef struct _RTL_EXCEPTION_FRAME {
DWORD Size;
DWORD Flags;
struct _RTL_EXCEPTION_FRAME* Previous;
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} RTL_EXCEPTION_FRAME;
typedef struct WIN_VECTORED_HANDLER_ENTRY {
uint32_t Active;
uint32_t IsContinue;
uint32_t Previous;
uint32_t Next;
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_ENTRY;
typedef struct WIN_VECTORED_HANDLER_SNAPSHOT {
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_SNAPSHOT;
typedef struct WIN_SEH_FRAME_SNAPSHOT {
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} WIN_SEH_FRAME_SNAPSHOT;
static volatile long gWinVectoredLock;
static uint32_t gWinVectoredExceptionHead = UINT32_MAX;
static uint32_t gWinVectoredExceptionTail = UINT32_MAX;
static uint32_t gWinVectoredContinueHead = UINT32_MAX;
static uint32_t gWinVectoredContinueTail = UINT32_MAX;
static WIN_VECTORED_HANDLER_ENTRY gWinVectoredHandlers[WIN_VECTORED_MAX_HANDLERS];
static void* gWinExceptionNativeHandle;
// Acquires the Win32 vectored-handler list lock.
static void WinpVectoredLock(void) {
while (_InterlockedCompareExchange(&gWinVectoredLock, 1, 0) != 0)
_mm_pause();
_ReadWriteBarrier();
}
// Releases vectored Exception Handler.
// Releases the Win32 vectored-handler list lock.
static void WinpVectoredUnlock(void) {
_ReadWriteBarrier();
_InterlockedExchange(&gWinVectoredLock, 0);
}
// Returns the list head slot for one vectored-handler kind.
static uint32_t* WinpVectoredHeadSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueHead : &gWinVectoredExceptionHead;
}
// Returns the list tail slot for one vectored-handler kind.
static uint32_t* WinpVectoredTailSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueTail : &gWinVectoredExceptionTail;
}
// Copies native exception state into the Win32-facing exception record.
static void WinpBuildExceptionRecord(const SYS_EXCEPTION_RECORD* source, EXCEPTION_RECORD* target) {
memset(target, 0, sizeof(*target));
target->ExceptionCode = source->Code;
if (source->Flags & SYS_EXCEPTION_FLAG_NONCONTINUABLE)
target->ExceptionFlags |= EXCEPTION_NONCONTINUABLE;
target->ExceptionAddress = (void*)(uintptr_t)source->ExceptionAddress;
target->NumberParameters = source->ParameterCount;
if (target->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS)
target->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
for (DWORD i = 0; i < target->NumberParameters; i++)
target->ExceptionInformation[i] = (uintptr_t)source->Parameters[i];
}
// Copies native user context into the subset of x64 CONTEXT currently exposed by Win32.
static void WinpBuildContext(const SYS_USER_CONTEXT* source, CONTEXT* target) {
memset(target, 0, sizeof(*target));
target->SegCs = (WORD)source->SegCs;
target->SegSs = (WORD)source->SegSs;
target->EFlags = (DWORD)source->Rflags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies handler-edited Win32 context back to the native resume context.
static void WinpStoreContext(SYS_USER_CONTEXT* target, const CONTEXT* source) {
target->Rflags = source->EFlags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies one vectored-handler list so callbacks can run without the list lock.
static size_t WinpSnapshotVectoredHandlers(uint32_t isContinue,
WIN_VECTORED_HANDLER_SNAPSHOT* snapshot,
size_t capacity) {
size_t count = 0;
WinpVectoredLock();
uint32_t head = *WinpVectoredHeadSlot(isContinue);
for (uint32_t index = head;
index != UINT32_MAX && count < capacity;
index = gWinVectoredHandlers[index].Next) {
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[index];
if (!entry->Active || entry->IsContinue != isContinue || !entry->Handler)
continue;
snapshot[count++].Handler = entry->Handler;
}
WinpVectoredUnlock();
return count;
}
// Runs one Win32 vectored-handler list against the current exception state.
static int WinpRunVectoredList(uint32_t isContinue,
EXCEPTION_POINTERS* pointers,
SYS_USER_CONTEXT* nativeContext) {
WIN_VECTORED_HANDLER_SNAPSHOT snapshot[WIN_VECTORED_MAX_HANDLERS];
size_t count = WinpSnapshotVectoredHandlers(isContinue, snapshot, WIN_VECTORED_MAX_HANDLERS);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Returns the current thread's top runtime-registered SEH frame.
static RTL_EXCEPTION_FRAME* WinpSehFrameHead(void) {
return (RTL_EXCEPTION_FRAME*)K32ThreadEnv()->ExceptionFrameHead;
}
// Updates the current thread's top runtime-registered SEH frame.
static void WinpSetSehFrameHead(RTL_EXCEPTION_FRAME* frame) {
K32ThreadEnv()->ExceptionFrameHead = frame;
}
// Copies the current SEH frame chain so callbacks can mutate registration safely.
static size_t WinpSnapshotSehFrames(WIN_SEH_FRAME_SNAPSHOT* snapshot, size_t capacity) {
size_t count = 0;
for (RTL_EXCEPTION_FRAME* frame = WinpSehFrameHead();
frame && count < capacity;
frame = frame->Previous) {
if (frame->Size != sizeof(*frame) || !frame->Handler)
break;
snapshot[count].Handler = frame->Handler;
snapshot[count].UserContext = frame->UserContext;
count++;
}
return count;
}
// Runs runtime-registered SEH frames after VEH has declined the exception.
static int WinpRunSehFrames(EXCEPTION_POINTERS* pointers, SYS_USER_CONTEXT* nativeContext) {
WIN_SEH_FRAME_SNAPSHOT snapshot[WIN_SEH_MAX_FRAMES];
size_t count = WinpSnapshotSehFrames(snapshot, WIN_SEH_MAX_FRAMES);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers, snapshot[i].UserContext);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
if (disposition == EXCEPTION_EXECUTE_HANDLER) {
// Runtime frames do not have a compiler-generated landing pad yet.
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Bridges the native exception runtime into Win32 exception policy.
static int WinpExceptionNativeDispatch(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
(void)userContext;
EXCEPTION_RECORD winRecord;
CONTEXT winContext;
EXCEPTION_POINTERS pointers;
WinpBuildExceptionRecord(record, &winRecord);
WinpBuildContext(context, &winContext);
pointers.ExceptionRecord = &winRecord;
pointers.ContextRecord = &winContext;
int disposition = WinpRunVectoredList(0, &pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
disposition = WinpRunSehFrames(&pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
return WinpRunVectoredList(1, &pointers, context);
}
// Ensures the Win32 exception bridge is registered with the native exception runtime.
static BOOL WinpEnsureExceptionBridge(void) {
if (gWinExceptionNativeHandle)
return TRUE;
WinpVectoredLock();
if (gWinExceptionNativeHandle) {
WinpVectoredUnlock();
return TRUE;
}
void* handle = NtlAddExceptionHandler(1, WinpExceptionNativeDispatch, NULL);
if (!handle) {
WinpVectoredUnlock();
gLastError = 8;
return FALSE;
}
gWinExceptionNativeHandle = handle;
WinpVectoredUnlock();
return TRUE;
}
// Adds one Win32 vectored exception or continue handler.
static void* WinpAddVectoredHandler(uint32_t isContinue, unsigned long first, void* handler) {
if (!handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (!gWinVectoredHandlers[i].Active) {
slot = i;
break;
}
}
if (slot == UINT32_MAX) {
WinpVectoredUnlock();
gLastError = 8;
return NULL;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
entry->Active = 1;
entry->IsContinue = isContinue;
entry->Handler = (PVECTORED_EXCEPTION_HANDLER)handler;
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
if (*head == UINT32_MAX) {
*head = slot;
*tail = slot;
} else if (first) {
entry->Next = *head;
gWinVectoredHandlers[*head].Previous = slot;
*head = slot;
} else {
entry->Previous = *tail;
gWinVectoredHandlers[*tail].Next = slot;
*tail = slot;
}
WinpVectoredUnlock();
return entry;
}
// Removes one Win32 vectored exception or continue handler.
static unsigned long WinpRemoveVectoredHandler(uint32_t isContinue, void* handle) {
if (!handle) {
gLastError = 6;
return 0;
}
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (handle == &gWinVectoredHandlers[i]) {
slot = i;
break;
}
}
if (slot == UINT32_MAX || !gWinVectoredHandlers[slot].Active ||
gWinVectoredHandlers[slot].IsContinue != isContinue) {
WinpVectoredUnlock();
gLastError = 6;
return 0;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
if (entry->Previous != UINT32_MAX)
gWinVectoredHandlers[entry->Previous].Next = entry->Next;
else
*head = entry->Next;
if (entry->Next != UINT32_MAX)
gWinVectoredHandlers[entry->Next].Previous = entry->Previous;
else
*tail = entry->Previous;
memset(entry, 0, sizeof(*entry));
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
WinpVectoredUnlock();
return 1;
}
// Registers one Win32 vectored exception handler.
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(0, First, Handler);
}
// Removes one Win32 vectored exception handler.
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
(void)Handle;
return 1; // success
return WinpRemoveVectoredHandler(0, Handle);
}
// Registers one Win32 vectored continue handler.
__declspec(dllexport) void* AddVectoredContinueHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(1, First, Handler);
}
// Removes one Win32 vectored continue handler.
__declspec(dllexport) unsigned long RemoveVectoredContinueHandler(void* Handle) {
return WinpRemoveVectoredHandler(1, Handle);
}
// Pushes one explicit runtime SEH frame for tests and hand-written personality code.
__declspec(dllexport) void* RtlPushExceptionFrame(RTL_EXCEPTION_FRAME* Frame,
PRTL_EXCEPTION_FRAME_HANDLER Handler,
void* UserContext) {
if (!Frame || !Handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
memset(Frame, 0, sizeof(*Frame));
Frame->Size = sizeof(*Frame);
Frame->Previous = WinpSehFrameHead();
Frame->Handler = Handler;
Frame->UserContext = UserContext;
WinpSetSehFrameHead(Frame);
return Frame;
}
// Pops the current explicit runtime SEH frame and enforces stack discipline.
__declspec(dllexport) BOOL RtlPopExceptionFrame(RTL_EXCEPTION_FRAME* Frame) {
if (!Frame || WinpSehFrameHead() != Frame) {
gLastError = 87;
return FALSE;
}
WinpSetSehFrameHead(Frame->Previous);
memset(Frame, 0, sizeof(*Frame));
return TRUE;
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
(void)dwExceptionCode; (void)dwExceptionFlags;
(void)nNumberOfArguments; (void)lpArguments;
// Best-effort: nothing to unwind; just halt.
while (true) ;
}
uint64_t arguments[EXCEPTION_MAXIMUM_PARAMETERS];
DWORD count = nNumberOfArguments;
if (count > EXCEPTION_MAXIMUM_PARAMETERS)
count = EXCEPTION_MAXIMUM_PARAMETERS;
typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS;
for (DWORD i = 0; i < count; i++)
arguments[i] = lpArguments ? (uint64_t)lpArguments[i] : 0;
SYS_RAISE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Code = dwExceptionCode;
request.Flags = (dwExceptionFlags & EXCEPTION_NONCONTINUABLE) ?
SYS_RAISE_EXCEPTION_FLAG_NONCONTINUABLE :
SYS_RAISE_EXCEPTION_FLAG_NONE;
request.ParameterCount = count;
request.Parameters = count ? arguments : NULL;
uint32_t status = SysRaiseException(&request);
if (status != NTLSTATUS_SUCCESS)
SysExitProcess((int32_t)status);
}
// Implements the unhandled Exception Filter operation.
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
@@ -2483,7 +2966,7 @@ __declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// RTL Exception / Unwind (SEH infrastructure - all stubs).
// RTL Exception / Unwind metadata helpers.
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
+1 -1
View File
@@ -50,7 +50,7 @@ typedef struct {
__declspec(dllexport) unsigned long timeGetTime(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS || !frequency) return 0;
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS || !frequency) return 0;
return (unsigned long)SysTicksToMilliseconds(SysGetTick(), frequency);
}
+9 -2
View File
@@ -116,8 +116,15 @@ static HEAP_SEGMENT* AddSegment(size_t payloadSize) {
if (pages < USER_HEAP_MIN_PAGES) pages = USER_HEAP_MIN_PAGES;
void* base = 0;
uint32_t s = SysAllocVm(0, pages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &base);
if (s != STATUS_SUCCESS || !base)
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.PageCount = pages;
req.Protect = SYS_VM_READWRITE;
req.Flags = SYS_VM_FLAG_NONE;
req.BaseOut = &base;
KSTATUS s = SysAllocVm(&req);
if (s != NTLSTATUS_SUCCESS || !base)
return (HEAP_SEGMENT*)0;
HEAP_SEGMENT* seg = (HEAP_SEGMENT*)base;
+256
View File
@@ -0,0 +1,256 @@
// Native user-mode exception runtime. This file owns process-local native handler dispatch.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
#define NTL_EXCEPTION_MAX_HANDLERS 16u
typedef struct NTL_EXCEPTION_HANDLER_ENTRY {
uint32_t Active;
uint32_t Previous;
uint32_t Next;
NTL_EXCEPTION_HANDLER Handler;
void* Context;
} NTL_EXCEPTION_HANDLER_ENTRY;
typedef struct NTL_EXCEPTION_HANDLER_SNAPSHOT {
NTL_EXCEPTION_HANDLER Handler;
void* Context;
} NTL_EXCEPTION_HANDLER_SNAPSHOT;
static volatile long gNtlExceptionLock;
static uint32_t gNtlExceptionListHead = UINT32_MAX;
static uint32_t gNtlExceptionListTail = UINT32_MAX;
static NTL_EXCEPTION_HANDLER_ENTRY gNtlExceptionHandlers[NTL_EXCEPTION_MAX_HANDLERS];
// Acquires the process-local exception handler lock.
static void NtlpExceptionLock(void) {
while (_InterlockedCompareExchange(&gNtlExceptionLock, 1, 0) != 0)
_mm_pause();
_ReadWriteBarrier();
}
// Releases the process-local exception handler lock.
static void NtlpExceptionUnlock(void) {
_ReadWriteBarrier();
_InterlockedExchange(&gNtlExceptionLock, 0);
}
// Returns a generic status after a failed exception-continue syscall.
static uint32_t NtlpStatusOrFailure(uint32_t status) {
return status == NTLSTATUS_SUCCESS ? NTLSTATUS_FAILURE : status;
}
// Asks the kernel to terminate the active exception dispatch.
static void NtlpTerminateExceptionDispatch(void) {
SYS_CONTINUE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Disposition = SYS_EXCEPTION_DISPOSITION_TERMINATE_PROCESS;
uint32_t status = SysContinueException(&request);
for (;;) SysExitProcess((int32_t)NtlpStatusOrFailure(status));
}
// Asks the kernel to continue searching from the top-level native dispatcher.
static void NtlpContinueExceptionSearch(void) {
SYS_CONTINUE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_SEARCH;
uint32_t status = SysContinueException(&request);
for (;;) SysExitProcess((int32_t)NtlpStatusOrFailure(status));
}
// Asks the kernel to resume execution with a handler-edited user context.
static void NtlpContinueExecution(SYS_USER_CONTEXT* context) {
SYS_CONTINUE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION;
request.Context = context;
uint32_t status = SysContinueException(&request);
for (;;) SysExitProcess((int32_t)NtlpStatusOrFailure(status));
}
// Returns whether one kernel-provided exception frame has the expected ABI shape.
static int NtlpFrameIsValid(SYS_EXCEPTION_FRAME* frame) {
if (!frame)
return 0;
if (frame->Size != sizeof(*frame) || frame->Version != SYS_EXCEPTION_ABI_VERSION)
return 0;
if (frame->ExceptionRecord.Size != sizeof(frame->ExceptionRecord) ||
frame->ExceptionRecord.Version != SYS_EXCEPTION_ABI_VERSION)
return 0;
if (frame->ContextRecord.Size != sizeof(frame->ContextRecord) ||
frame->ContextRecord.Version != SYS_EXCEPTION_ABI_VERSION)
return 0;
return 1;
}
// Copies the current handler list so callbacks can run without holding the list lock.
static size_t NtlpSnapshotExceptionHandlers(NTL_EXCEPTION_HANDLER_SNAPSHOT* snapshot, size_t capacity) {
size_t count = 0;
NtlpExceptionLock();
for (uint32_t index = gNtlExceptionListHead;
index != UINT32_MAX && count < capacity;
index = gNtlExceptionHandlers[index].Next) {
NTL_EXCEPTION_HANDLER_ENTRY* entry = &gNtlExceptionHandlers[index];
if (!entry->Active || !entry->Handler)
continue;
snapshot[count].Handler = entry->Handler;
snapshot[count].Context = entry->Context;
count++;
}
NtlpExceptionUnlock();
return count;
}
// Dispatches one kernel-delivered exception through the native handler list.
static void NtlpUserExceptionDispatcher(SYS_EXCEPTION_FRAME* frame) {
if (!NtlpFrameIsValid(frame))
NtlpTerminateExceptionDispatch();
NTL_EXCEPTION_HANDLER_SNAPSHOT snapshot[NTL_EXCEPTION_MAX_HANDLERS];
size_t count = NtlpSnapshotExceptionHandlers(snapshot, NTL_EXCEPTION_MAX_HANDLERS);
for (size_t i = 0; i < count; i++) {
int disposition = snapshot[i].Handler(&frame->ExceptionRecord,
&frame->ContextRecord,
snapshot[i].Context);
if (disposition == NTL_EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == NTL_EXCEPTION_CONTINUE_EXECUTION)
NtlpContinueExecution(&frame->ContextRecord);
if (disposition == NTL_EXCEPTION_TERMINATE_PROCESS)
NtlpTerminateExceptionDispatch();
NtlpTerminateExceptionDispatch();
}
NtlpContinueExceptionSearch();
}
// Registers the native dispatcher for the current process when no other dispatcher owns the process.
NTL_EXCEPTION_API uint32_t NtlInitializeExceptionRuntime(void) {
NtlpExceptionLock();
SYS_EXCEPTION_DISPATCHER_REQUEST current;
memset(&current, 0, sizeof(current));
current.Size = sizeof(current);
uint32_t status = SysGetExceptionDispatcher(&current);
if (status != NTLSTATUS_SUCCESS) {
NtlpExceptionUnlock();
return status;
}
if (current.DispatcherRoutine == NtlpUserExceptionDispatcher) {
NtlpExceptionUnlock();
return NTLSTATUS_SUCCESS;
}
if (current.DispatcherRoutine) {
NtlpExceptionUnlock();
return NTLSTATUS_ALREADY_EXISTS;
}
SYS_EXCEPTION_DISPATCHER_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.DispatcherRoutine = NtlpUserExceptionDispatcher;
status = SysSetExceptionDispatcher(&request);
NtlpExceptionUnlock();
return status;
}
// Adds one process-local native exception handler.
NTL_EXCEPTION_API void* NtlAddExceptionHandler(int first, NTL_EXCEPTION_HANDLER handler, void* context) {
if (!handler)
return 0;
if (NtlInitializeExceptionRuntime() != NTLSTATUS_SUCCESS)
return 0;
NtlpExceptionLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < NTL_EXCEPTION_MAX_HANDLERS; i++) {
if (!gNtlExceptionHandlers[i].Active) {
slot = i;
break;
}
}
if (slot == UINT32_MAX) {
NtlpExceptionUnlock();
return 0;
}
NTL_EXCEPTION_HANDLER_ENTRY* entry = &gNtlExceptionHandlers[slot];
entry->Active = 1;
entry->Handler = handler;
entry->Context = context;
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
if (gNtlExceptionListHead == UINT32_MAX) {
gNtlExceptionListHead = slot;
gNtlExceptionListTail = slot;
} else if (first) {
entry->Next = gNtlExceptionListHead;
gNtlExceptionHandlers[gNtlExceptionListHead].Previous = slot;
gNtlExceptionListHead = slot;
} else {
entry->Previous = gNtlExceptionListTail;
gNtlExceptionHandlers[gNtlExceptionListTail].Next = slot;
gNtlExceptionListTail = slot;
}
NtlpExceptionUnlock();
return entry;
}
// Removes one process-local native exception handler.
NTL_EXCEPTION_API uint32_t NtlRemoveExceptionHandler(void* handle) {
if (!handle)
return NTLSTATUS_INVALID_PARAMETER;
NtlpExceptionLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < NTL_EXCEPTION_MAX_HANDLERS; i++) {
if (handle == &gNtlExceptionHandlers[i]) {
slot = i;
break;
}
}
if (slot == UINT32_MAX || !gNtlExceptionHandlers[slot].Active) {
NtlpExceptionUnlock();
return NTLSTATUS_NOT_FOUND;
}
NTL_EXCEPTION_HANDLER_ENTRY* entry = &gNtlExceptionHandlers[slot];
if (entry->Previous != UINT32_MAX)
gNtlExceptionHandlers[entry->Previous].Next = entry->Next;
else
gNtlExceptionListHead = entry->Next;
if (entry->Next != UINT32_MAX)
gNtlExceptionHandlers[entry->Next].Previous = entry->Previous;
else
gNtlExceptionListTail = entry->Previous;
memset(entry, 0, sizeof(*entry));
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
NtlpExceptionUnlock();
return NTLSTATUS_SUCCESS;
}
+18
View File
@@ -0,0 +1,18 @@
// Native process startup. This file owns process-local native runtime initialization before main runs.
#include <stdint.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
extern int main(void);
// Initializes native process state and exits with main's return value.
void NtlNativeStart(void) {
uint32_t status = NtlInitializeExceptionRuntime();
if (status != NTLSTATUS_SUCCESS) {
for (;;) SysExitProcess((int32_t)status);
}
int exitCode = main();
for (;;) SysExitProcess(exitCode);
}
+11 -2
View File
@@ -1,13 +1,22 @@
// POSIX process startup. This file owns process-local POSIX personality initialization before main runs.
#include <stdint.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
extern int main(void);
extern void exit(int status);
extern void PosixFdInit(void);
// Initializes POSIX process state and exits with main's return value.
void PosixStart(void) {
uint32_t status = NtlInitializeExceptionRuntime();
if (status != NTLSTATUS_SUCCESS) {
for (;;) SysExitProcess((int32_t)status);
}
PosixFdInit();
int status = main();
exit(status);
int exitCode = main();
exit(exitCode);
for (;;) {}
}
+126 -121
View File
@@ -1,140 +1,145 @@
; Syscall numbers shared by MASM user-mode stubs.
; Process, threads, modules, waits, and APC.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_OPEN_PROCESS EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_GET_THREAD_LIST EQU 9
SYS_LOAD_MODULE EQU 10
SYS_UNLOAD_MODULE EQU 11
SYS_GET_MODULE_INFO EQU 12
SYS_GET_MODULE_EXPORT EQU 13
SYS_GET_MODULE_LIST EQU 14
SYS_GET_CURRENT_TID EQU 15
SYS_GET_CURRENT_PID EQU 16
SYS_SET_THREAD_PRIORITY EQU 17
SYS_YIELD EQU 18
SYS_SLEEP EQU 19
SYS_WAIT_SINGLE EQU 20
SYS_WAIT_MANY EQU 21
SYS_SET_USER_APC_DISPATCHER EQU 22
SYS_APC_RETURN EQU 23
; Process, threads, modules, waits, APC, and exceptions.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_OPEN_PROCESS EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_GET_THREAD_LIST EQU 9
SYS_LOAD_MODULE EQU 10
SYS_UNLOAD_MODULE EQU 11
SYS_GET_MODULE_INFO EQU 12
SYS_GET_MODULE_EXPORT EQU 13
SYS_GET_MODULE_LIST EQU 14
SYS_GET_CURRENT_TID EQU 15
SYS_GET_CURRENT_PID EQU 16
SYS_SET_THREAD_PRIORITY EQU 17
SYS_YIELD EQU 18
SYS_SLEEP EQU 19
SYS_WAIT_SINGLE EQU 20
SYS_WAIT_MANY EQU 21
SYS_SET_USER_APC_DISPATCHER EQU 22
SYS_APC_RETURN EQU 23
SYS_SET_EXCEPTION_DISPATCHER EQU 24
SYS_GET_EXCEPTION_DISPATCHER EQU 25
SYS_CONTINUE_EXCEPTION EQU 26
SYS_RAISE_EXCEPTION EQU 27
; Virtual memory, mappings, and handles.
SYS_ALLOC_VM EQU 24
SYS_FREE_VM EQU 25
SYS_PROTECT_VM EQU 26
SYS_QUERY_VM EQU 27
SYS_CLOSE_HANDLE EQU 28
SYS_DUPLICATE_HANDLE EQU 29
SYS_CREATE_FILE_MAPPING EQU 30
SYS_MAP_FILE EQU 31
SYS_ALLOC_VM EQU 28
SYS_FREE_VM EQU 29
SYS_PROTECT_VM EQU 30
SYS_QUERY_VM EQU 31
SYS_CLOSE_HANDLE EQU 32
SYS_DUPLICATE_HANDLE EQU 33
SYS_CREATE_FILE_MAPPING EQU 34
SYS_MAP_FILE EQU 35
; Synchronization objects.
SYS_CREATE_MUTEX EQU 32
SYS_TRY_ACQUIRE_MUTEX EQU 33
SYS_RELEASE_MUTEX EQU 34
SYS_CREATE_SEM EQU 35
SYS_TRY_WAIT_SEM EQU 36
SYS_SIGNAL_SEM EQU 37
SYS_CREATE_EVENT EQU 38
SYS_SIGNAL_EVENT EQU 39
SYS_CLEAR_EVENT EQU 40
SYS_CREATE_MUTEX EQU 36
SYS_TRY_ACQUIRE_MUTEX EQU 37
SYS_RELEASE_MUTEX EQU 38
SYS_CREATE_SEM EQU 39
SYS_TRY_WAIT_SEM EQU 40
SYS_SIGNAL_SEM EQU 41
SYS_CREATE_EVENT EQU 42
SYS_SIGNAL_EVENT EQU 43
SYS_CLEAR_EVENT EQU 44
; Queues and named queues.
SYS_CREATE_QUEUE EQU 41
SYS_SEND_QUEUE EQU 42
SYS_RECV_QUEUE EQU 43
SYS_PUBLISH_QUEUE EQU 44
SYS_OPEN_NAMED_QUEUE EQU 45
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 46
SYS_CREATE_QUEUE EQU 45
SYS_SEND_QUEUE EQU 46
SYS_RECV_QUEUE EQU 47
SYS_PUBLISH_QUEUE EQU 48
SYS_OPEN_NAMED_QUEUE EQU 49
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 50
; Files, directories, and current directory.
SYS_OPEN_FILE EQU 47
SYS_READ_FILE EQU 48
SYS_WRITE_FILE EQU 49
SYS_READ_FILE_ASYNC EQU 50
SYS_WRITE_FILE_ASYNC EQU 51
SYS_CANCEL_IO EQU 52
SYS_SEEK_FILE EQU 53
SYS_GET_FILE_POS EQU 54
SYS_GET_FILE_SIZE EQU 55
SYS_GET_FILE_STAT EQU 56
SYS_STAT_FILE EQU 57
SYS_TRUNCATE_FILE EQU 58
SYS_FLUSH_FILE EQU 59
SYS_DELETE_FILE EQU 60
SYS_RENAME_FILE EQU 61
SYS_LOCK_FILE EQU 62
SYS_UNLOCK_FILE EQU 63
SYS_OPEN_DIR EQU 64
SYS_READ_DIR EQU 65
SYS_CREATE_DIR EQU 66
SYS_REMOVE_DIR EQU 67
SYS_SET_CWD EQU 68
SYS_REAL_PATH EQU 69
SYS_OPEN_FILE EQU 51
SYS_READ_FILE EQU 52
SYS_WRITE_FILE EQU 53
SYS_READ_FILE_ASYNC EQU 54
SYS_WRITE_FILE_ASYNC EQU 55
SYS_CANCEL_IO EQU 56
SYS_SEEK_FILE EQU 57
SYS_GET_FILE_POS EQU 58
SYS_GET_FILE_SIZE EQU 59
SYS_GET_FILE_STAT EQU 60
SYS_STAT_FILE EQU 61
SYS_TRUNCATE_FILE EQU 62
SYS_FLUSH_FILE EQU 63
SYS_DELETE_FILE EQU 64
SYS_RENAME_FILE EQU 65
SYS_LOCK_FILE EQU 66
SYS_UNLOCK_FILE EQU 67
SYS_OPEN_DIR EQU 68
SYS_READ_DIR EQU 69
SYS_CREATE_DIR EQU 70
SYS_REMOVE_DIR EQU 71
SYS_SET_CWD EQU 72
SYS_REAL_PATH EQU 73
; Console, display, and surfaces.
SYS_WRITE_CONSOLE EQU 70
SYS_GET_CONSOLE_INFO EQU 71
SYS_SET_CONSOLE_CURSOR EQU 72
SYS_FILL_CONSOLE_CHARACTER EQU 73
SYS_FILL_CONSOLE_ATTRIBUTE EQU 74
SYS_PUT_SCREEN_CHAR EQU 75
SYS_CLEAR_SCREEN EQU 76
SYS_GET_SCREEN_SIZE EQU 77
SYS_QUERY_DISPLAY_INFO EQU 78
SYS_CREATE_SURFACE EQU 79
SYS_MAP_SURFACE EQU 80
SYS_QUERY_SURFACE EQU 81
SYS_PRESENT_SURFACE EQU 82
SYS_WRITE_CONSOLE EQU 74
SYS_GET_CONSOLE_INFO EQU 75
SYS_SET_CONSOLE_CURSOR EQU 76
SYS_FILL_CONSOLE_CHARACTER EQU 77
SYS_FILL_CONSOLE_ATTRIBUTE EQU 78
SYS_PUT_SCREEN_CHAR EQU 79
SYS_CLEAR_SCREEN EQU 80
SYS_GET_SCREEN_SIZE EQU 81
SYS_QUERY_DISPLAY_INFO EQU 82
SYS_CREATE_SURFACE EQU 83
SYS_MAP_SURFACE EQU 84
SYS_QUERY_SURFACE EQU 85
SYS_PRESENT_SURFACE EQU 86
; Input.
SYS_POLL_KEYBOARD EQU 83
SYS_IS_KEY_DOWN EQU 84
SYS_READ_KEYBOARD_CHAR EQU 85
SYS_READ_KEY EQU 86
SYS_GET_KEY_MODIFIERS EQU 87
SYS_OPEN_INPUT_QUEUE EQU 88
SYS_READ_INPUT_EVENT EQU 89
SYS_PEEK_INPUT_EVENT EQU 90
SYS_PEEK_MOUSE EQU 91
SYS_READ_MOUSE EQU 92
SYS_GET_MOUSE_POS EQU 93
SYS_POLL_KEYBOARD EQU 87
SYS_IS_KEY_DOWN EQU 88
SYS_READ_KEYBOARD_CHAR EQU 89
SYS_READ_KEY EQU 90
SYS_GET_KEY_MODIFIERS EQU 91
SYS_OPEN_INPUT_QUEUE EQU 92
SYS_READ_INPUT_EVENT EQU 93
SYS_PEEK_INPUT_EVENT EQU 94
SYS_PEEK_MOUSE EQU 95
SYS_READ_MOUSE EQU 96
SYS_GET_MOUSE_POS EQU 97
; Time, debug, and system information.
SYS_CREATE_TIMER EQU 94
SYS_SET_TIMER EQU 95
SYS_CANCEL_TIMER EQU 96
SYS_GET_TICK EQU 97
SYS_GET_TICK_FREQUENCY EQU 98
SYS_GET_SYSTEM_TIME EQU 99
SYS_PRINT_DEBUG EQU 100
SYS_QUERY_TRAP_FRAME EQU 101
SYS_GET_MEMORY_STATUS EQU 102
SYS_QUERY_KERNEL_STATS EQU 103
SYS_CREATE_TIMER EQU 98
SYS_SET_TIMER EQU 99
SYS_CANCEL_TIMER EQU 100
SYS_GET_TICK EQU 101
SYS_GET_TICK_FREQUENCY EQU 102
SYS_GET_SYSTEM_TIME EQU 103
SYS_PRINT_DEBUG EQU 104
SYS_QUERY_TRAP_FRAME EQU 105
SYS_GET_MEMORY_STATUS EQU 106
SYS_QUERY_KERNEL_STATS EQU 107
; Native sockets and polling.
SYS_CREATE_SOCKET EQU 104
SYS_SOCKET_IO_ASYNC EQU 105
SYS_BIND_SOCKET EQU 106
SYS_LISTEN_SOCKET EQU 107
SYS_ACCEPT_SOCKET EQU 108
SYS_CONNECT_SOCKET EQU 109
SYS_SEND_SOCKET EQU 110
SYS_RECV_SOCKET EQU 111
SYS_SHUTDOWN_SOCKET EQU 112
SYS_SEND_TO_SOCKET EQU 113
SYS_RECV_FROM_SOCKET EQU 114
SYS_RESOLVE_HOST EQU 115
SYS_SET_SOCKET_OPTION EQU 116
SYS_GET_SOCKET_OPTION EQU 117
SYS_POLL EQU 118
SYS_CREATE_SOCKET EQU 108
SYS_SOCKET_IO_ASYNC EQU 109
SYS_BIND_SOCKET EQU 110
SYS_LISTEN_SOCKET EQU 111
SYS_ACCEPT_SOCKET EQU 112
SYS_CONNECT_SOCKET EQU 113
SYS_SEND_SOCKET EQU 114
SYS_RECV_SOCKET EQU 115
SYS_SHUTDOWN_SOCKET EQU 116
SYS_SEND_TO_SOCKET EQU 117
SYS_RECV_FROM_SOCKET EQU 118
SYS_RESOLVE_HOST EQU 119
SYS_SET_SOCKET_OPTION EQU 120
SYS_GET_SOCKET_OPTION EQU 121
SYS_POLL EQU 122
SYS_UNPUBLISH_QUEUE EQU 123
SYS_MAX EQU 119
SYS_MAX EQU 124
+7 -2
View File
@@ -104,6 +104,10 @@ SYSCALL_STUB2 Yield, SYS_YIELD
SYSCALL_STUB2 Sleep, SYS_SLEEP
SYSCALL_STUB2 SetUserApcDispatcher, SYS_SET_USER_APC_DISPATCHER
SYSCALL_STUB2 ApcReturn, SYS_APC_RETURN
SYSCALL_STUB2 SetExceptionDispatcher, SYS_SET_EXCEPTION_DISPATCHER
SYSCALL_STUB2 GetExceptionDispatcher, SYS_GET_EXCEPTION_DISPATCHER
SYSCALL_STUB2 ContinueException, SYS_CONTINUE_EXCEPTION
SYSCALL_STUB2 RaiseException, SYS_RAISE_EXCEPTION
; ---------------------------------------------------------------------------
; User APC dispatcher
@@ -137,7 +141,7 @@ SysUserApcDispatcher ENDP
; ---------------------------------------------------------------------------
; Virtual memory and handles
; ---------------------------------------------------------------------------
SYSCALL_STUB6 AllocVm, SYS_ALLOC_VM
SYSCALL_STUB2 AllocVm, SYS_ALLOC_VM
SYSCALL_STUB2 FreeVm, SYS_FREE_VM
SYSCALL_STUB2 ProtectVm, SYS_PROTECT_VM
SYSCALL_STUB2 QueryVm, SYS_QUERY_VM
@@ -166,6 +170,7 @@ SYSCALL_STUB2 CreateQueue, SYS_CREATE_QUEUE
SYSCALL_STUB2 SendQueue, SYS_SEND_QUEUE
SYSCALL_STUB2 RecvQueue, SYS_RECV_QUEUE
SYSCALL_STUB2 PublishQueue, SYS_PUBLISH_QUEUE
SYSCALL_STUB2 UnpublishQueue, SYS_UNPUBLISH_QUEUE
SYSCALL_STUB2 OpenNamedQueue, SYS_OPEN_NAMED_QUEUE
SYSCALL_STUB2 OpenNamedQueueOwnerProcess, SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS
@@ -206,7 +211,7 @@ SYSCALL_STUB2 SetConsoleCursor, SYS_SET_CONSOLE_CURSOR
SYSCALL_STUB6 FillConsoleCharacter, SYS_FILL_CONSOLE_CHARACTER
SYSCALL_STUB6 FillConsoleAttribute, SYS_FILL_CONSOLE_ATTRIBUTE
SYSCALL_STUB6 PutScreenChar, SYS_PUT_SCREEN_CHAR
SYSCALL_STUB5 CreateSurface, SYS_CREATE_SURFACE
SYSCALL_STUB2 CreateSurface, SYS_CREATE_SURFACE
SYSCALL_STUB2 MapSurface, SYS_MAP_SURFACE
SYSCALL_STUB2 QuerySurface, SYS_QUERY_SURFACE
SYSCALL_STUB2 PresentSurface, SYS_PRESENT_SURFACE
+9 -3
View File
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
// Implements the itoa operation.
@@ -54,8 +55,13 @@ static void debug_print(const char* s) {
(void)SysPrintDebug(&req);
}
// Initializes init process state and supervises the configured child process.
void _start(void)
{
uint32_t runtimeStatus = NtlInitializeExceptionRuntime();
if (runtimeStatus != NTLSTATUS_SUCCESS)
SysExitProcess((int32_t)runtimeStatus);
SYS_STRING_VIEW args = SysGetPeb()->CommandLine;
uint32_t h = 0;
uint32_t s = 0;
@@ -64,7 +70,7 @@ void _start(void)
debug_print("[init] starting\n");
SYS_CREATE_PROCESS_REQUEST cp = { args, SysSvFromParts(0, 0), &h };
s = SysCreateProcess(&cp);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
debug_print("[init] FATAL: failed to start!\n");
char buffer[12];
itoa(s, buffer);
@@ -75,11 +81,11 @@ void _start(void)
debug_print("[init] waiting\n");
s = SysWait(h, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
debug_print("[init] WARNING: SysWait returned error\n");
int32_t exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
if (SysGetProcessExitCode(h, &exitCode) != NTLSTATUS_SUCCESS) {
debug_print("[init] WARNING: failed to read exit code\n");
exitCode = 1;
}
@@ -57,13 +57,13 @@ static uint32_t open_test_file(uint32_t* handleOut) {
SYS_FILE_FLAG_NONE,
&handle);
uint32_t status = SysOpenFile(&request);
if (status == STATUS_SUCCESS) {
if (status == NTLSTATUS_SUCCESS) {
*handleOut = handle;
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
typedef struct APC_RETURN_STATE {
@@ -75,7 +75,7 @@ typedef struct APC_RETURN_STATE {
// Records that the user APC callback ran and observed the I/O status block.
static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t information) {
APC_RETURN_STATE* state = (APC_RETURN_STATE*)context;
state->Status = iosb ? iosb->Status : STATUS_FAILURE;
state->Status = iosb ? iosb->Status : NTLSTATUS_FAILURE;
state->Information = information;
state->Delivered++;
}
@@ -97,29 +97,29 @@ int main(void) {
print("apc_return_smoke: start\n");
uint32_t status = SysApcReturn((const SYS_USER_APC_FRAME*)0);
if (status != STATUS_INVALID_PARAMETER)
fail_status("inactive null return", status, STATUS_INVALID_PARAMETER);
if (status != NTLSTATUS_INVALID_PARAMETER)
fail_status("inactive null return", status, NTLSTATUS_INVALID_PARAMETER);
SYS_USER_APC_FRAME fakeFrame;
memset(&fakeFrame, 0, sizeof(fakeFrame));
fakeFrame.Magic = SYS_USER_APC_FRAME_MAGIC;
status = SysApcReturn(&fakeFrame);
if (status != STATUS_INVALID_PARAMETER)
fail_status("inactive forged return", status, STATUS_INVALID_PARAMETER);
if (status != NTLSTATUS_INVALID_PARAMETER)
fail_status("inactive forged return", status, NTLSTATUS_INVALID_PARAMETER);
status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
if (status != STATUS_SUCCESS)
fail_status("set dispatcher", status, STATUS_SUCCESS);
if (status != NTLSTATUS_SUCCESS)
fail_status("set dispatcher", status, NTLSTATUS_SUCCESS);
uint32_t file = 0;
status = open_test_file(&file);
if (status != STATUS_SUCCESS)
fail_status("open", status, STATUS_SUCCESS);
if (status != NTLSTATUS_SUCCESS)
fail_status("open", status, NTLSTATUS_SUCCESS);
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS)
fail_status("create event", status, STATUS_SUCCESS);
if (status != NTLSTATUS_SUCCESS)
fail_status("create event", status, NTLSTATUS_SUCCESS);
uint8_t* buffer = (uint8_t*)malloc(512);
if (!buffer) {
@@ -146,16 +146,16 @@ int main(void) {
request.ApcContext = &state;
status = SysReadFileAsync(&request);
if (status != STATUS_PENDING)
fail_status("submit", status, STATUS_PENDING);
if (status != NTLSTATUS_PENDING)
fail_status("submit", status, NTLSTATUS_PENDING);
while (!state.Delivered) {
status = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
if (status != STATUS_USER_APC && status != STATUS_SUCCESS && status != STATUS_PENDING)
fail_status("alertable wait", status, STATUS_USER_APC);
if (status != NTLSTATUS_USER_APC && status != NTLSTATUS_SUCCESS && status != NTLSTATUS_PENDING)
fail_status("alertable wait", status, NTLSTATUS_USER_APC);
}
uint32_t ok = state.Delivered == 1 && state.Status == STATUS_SUCCESS && state.Information == 512;
uint32_t ok = state.Delivered == 1 && state.Status == NTLSTATUS_SUCCESS && state.Information == 512;
SysCloseHandle(event);
SysCloseHandle(file);
+9 -9
View File
@@ -39,15 +39,15 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
// Prints hex8.
@@ -95,7 +95,7 @@ int main(void) {
print("async_apc: start\n");
uint32_t s = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
print("async_apc: dispatcher registration failed status=");
print_u64(s);
print("\n");
@@ -105,7 +105,7 @@ int main(void) {
uint32_t file = 0;
const char* openedPath = 0;
s = open_test_file(&file, &openedPath);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
print("async_apc: open failed status=");
print_u64(s);
print("\n");
@@ -118,7 +118,7 @@ int main(void) {
uint32_t event = 0;
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
print("async_apc: event create failed status=");
print_u64(s);
print("\n");
@@ -151,7 +151,7 @@ int main(void) {
req.ApcContext = &state;
s = SysReadFileAsync(&req);
if (s != STATUS_PENDING) {
if (s != NTLSTATUS_PENDING) {
print("async_apc: submit failed status=");
print_u64(s);
print("\n");
@@ -163,7 +163,7 @@ int main(void) {
print("async_apc: alertable wait returned ");
print_u64(s);
print("\n");
if (s != STATUS_USER_APC && s != STATUS_SUCCESS && s != STATUS_PENDING)
if (s != NTLSTATUS_USER_APC && s != NTLSTATUS_SUCCESS && s != NTLSTATUS_PENDING)
break;
}
@@ -179,7 +179,7 @@ int main(void) {
print_hex8(buffer[2]); print(" ");
print_hex8(buffer[3]); print("\n");
uint32_t ok = (state.Delivered == 1 && state.Status == STATUS_SUCCESS &&
uint32_t ok = (state.Delivered == 1 && state.Status == NTLSTATUS_SUCCESS &&
state.Information == 512 && buffer[0] == 'M' && buffer[1] == 'Z');
SysCloseHandle(event);
+11 -11
View File
@@ -38,15 +38,15 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
// Prints u64.
@@ -77,7 +77,7 @@ int main(void) {
const char* openedPath = 0;
uint32_t s = open_test_file(&file, &openedPath);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
print("async_cancel: open failed status="); print_u64(s); print("\n");
SysExitProcess(1);
}
@@ -87,7 +87,7 @@ int main(void) {
print("\n");
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
print("async_cancel: event create failed status="); print_u64(s); print("\n");
SysExitProcess(1);
}
@@ -114,7 +114,7 @@ int main(void) {
req.RequestHandleOut = &request;
s = SysReadFileAsync(&req);
if (s != STATUS_PENDING || request == 0) {
if (s != NTLSTATUS_PENDING || request == 0) {
print("async_cancel: submit failed status="); print_u64(s);
print(" request="); print_u64(request); print("\n");
SysExitProcess(1);
@@ -123,10 +123,10 @@ int main(void) {
uint32_t cs = SysCancelIo(request);
print("async_cancel: cancel returned "); print_u64(cs); print("\n");
while (iosb.Status == STATUS_PENDING) {
while (iosb.Status == NTLSTATUS_PENDING) {
s = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
print("async_cancel: wait returned "); print_u64(s); print("\n");
if (s != STATUS_SUCCESS && s != STATUS_KERNEL_APC && s != STATUS_USER_APC && s != STATUS_ALERTED)
if (s != NTLSTATUS_SUCCESS && s != NTLSTATUS_KERNEL_APC && s != NTLSTATUS_USER_APC && s != NTLSTATUS_ALERTED)
break;
}
@@ -135,9 +135,9 @@ int main(void) {
// If the worker had already started, cancellation is best-effort and the
// request may complete successfully. If it was still queued, it must report
// STATUS_CANCELLED. Both outcomes are valid; STATUS_PENDING is not.
uint32_t ok = (iosb.Status == STATUS_CANCELLED) ||
(iosb.Status == STATUS_SUCCESS && iosb.Information == 512);
// NTLSTATUS_CANCELLED. Both outcomes are valid; NTLSTATUS_PENDING is not.
uint32_t ok = (iosb.Status == NTLSTATUS_CANCELLED) ||
(iosb.Status == NTLSTATUS_SUCCESS && iosb.Information == 512);
SysCloseHandle(request);
SysCloseHandle(event);
@@ -58,7 +58,7 @@ static void fail(const char* what) {
static void set_cwd_or_fail(const char* path, const char* what) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
uint32_t status = SysSetCwd(&req);
if (status != STATUS_SUCCESS) fail_status(what, status);
if (status != NTLSTATUS_SUCCESS) fail_status(what, status);
}
// Verifies the current-directory PEB view against one native path.
@@ -87,11 +87,11 @@ int main(void) {
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(tempDir) };
uint32_t status = SysRemoveDir(&dirReq);
if (status != STATUS_SUCCESS && status != STATUS_NOT_FOUND)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_NOT_FOUND)
fail_status("remove stale temp dir", status);
status = SysCreateDir(&dirReq);
if (status != STATUS_SUCCESS) fail_status("create temp dir", status);
if (status != NTLSTATUS_SUCCESS) fail_status("create temp dir", status);
set_cwd_or_fail(tempDir, "set temp cwd");
expect_cwd(tempDir);
@@ -100,7 +100,7 @@ int main(void) {
expect_cwd(rootPath);
status = SysRemoveDir(&dirReq);
if (status != STATUS_SUCCESS) fail_status("remove temp dir", status);
if (status != NTLSTATUS_SUCCESS) fail_status("remove temp dir", status);
print("cwd_peb_smoke: PASS\n");
exit_now(0);
@@ -79,19 +79,19 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
@@ -102,7 +102,7 @@ static uint32_t open_path(const char* path, uint32_t* fileOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
*fileOut = h;
return s;
}
@@ -118,7 +118,7 @@ static void checked_write(uint32_t file, const void* buffer, size_t size, const
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -127,13 +127,13 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
}
static void expect_writeback_progress(const SYS_FILE_CACHE_STATS* before,
@@ -158,7 +158,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s);
print("dirty_writeback: using ");
print(path ? path : "<unknown>");
@@ -170,11 +170,11 @@ int main(void) {
SYS_FILE_CACHE_STATS beforeClose;
SYS_FILE_CACHE_STATS afterClose;
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != NTLSTATUS_SUCCESS) fail("initial stats query failed");
checked_write(file, gPatternA, sizeof(gPatternA), "write pattern A");
if (query_cache(&afterWrite) != STATUS_SUCCESS) fail("post-write stats query failed");
if (query_cache(&afterWrite) != NTLSTATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.DirtyMarks <= before.DirtyMarks) fail("write did not mark dirty cache pages");
if (afterWrite.DirtyPages <= before.DirtyPages) fail("write did not leave dirty pages resident");
@@ -185,9 +185,9 @@ int main(void) {
if (memcmp(gReadback, gPatternA, sizeof(gPatternA)) != 0) fail("cached read before flush mismatch");
s = SysFlushFile(file);
if (s != STATUS_SUCCESS) fail_status("explicit flush", s);
if (s != NTLSTATUS_SUCCESS) fail_status("explicit flush", s);
if (query_cache(&afterFlush) != STATUS_SUCCESS) fail("post-flush stats query failed");
if (query_cache(&afterFlush) != NTLSTATUS_SUCCESS) fail("post-flush stats query failed");
if (afterFlush.FlushFileCalls <= afterWrite.FlushFileCalls) fail("flush call counter did not increase");
expect_writeback_progress(&afterWrite, &afterFlush, "explicit flush");
@@ -196,19 +196,19 @@ int main(void) {
checked_seek(file, REWRITE_SIZE, "seek close-tail");
checked_write(file, gPatternA + REWRITE_SIZE, CLOSE_SIZE - REWRITE_SIZE, "write close tail");
if (query_cache(&beforeClose) != STATUS_SUCCESS) fail("pre-close stats query failed");
if (query_cache(&beforeClose) != NTLSTATUS_SUCCESS) fail("pre-close stats query failed");
if (beforeClose.DirtyMarks <= afterFlush.DirtyMarks) fail("rewrite did not mark dirty pages");
s = SysCloseHandle(file);
file = 0;
if (s != STATUS_SUCCESS) fail_status("close", s);
if (s != NTLSTATUS_SUCCESS) fail_status("close", s);
if (query_cache(&afterClose) != STATUS_SUCCESS) fail("post-close stats query failed");
if (query_cache(&afterClose) != NTLSTATUS_SUCCESS) fail("post-close stats query failed");
if (afterClose.FlushFileCalls <= beforeClose.FlushFileCalls) fail("close did not flush file");
expect_writeback_progress(&beforeClose, &afterClose, "close flush");
s = open_path(path, &file);
if (s != STATUS_SUCCESS) fail_status("reopen", s);
if (s != NTLSTATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, CLOSE_SIZE, &got, "read after reopen");
if (got != CLOSE_SIZE) fail("read after reopen size mismatch");
@@ -218,7 +218,7 @@ int main(void) {
s = SysCloseHandle(file);
file = 0;
if (s != STATUS_SUCCESS) fail_status("close reopened", s);
if (s != NTLSTATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("dirty_writeback: dirty-marks+=");
@@ -0,0 +1,197 @@
// User-mode smoke test for native exception dispatcher registration.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
// 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("exception_dispatcher_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("exception_dispatcher_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Dummy dispatcher used only as a validated user-mode code pointer.
static void smoke_dispatcher(SYS_EXCEPTION_FRAME* frame) {
(void)frame;
}
// Dispatcher hit count used by the delivery/continue stage.
static volatile uint32_t gBreakpointDispatchCount;
// Continues from a breakpoint exception using the kernel-provided context.
static void smoke_continue_dispatcher(SYS_EXCEPTION_FRAME* frame) {
if (!frame)
fail("dispatcher received null frame");
if (frame->Size != sizeof(*frame) || frame->Version != SYS_EXCEPTION_ABI_VERSION)
fail("dispatcher frame ABI mismatch");
if (frame->ExceptionRecord.Code != SYS_EXCEPTION_CODE_BREAKPOINT)
fail("dispatcher received unexpected exception code");
if (frame->DispatcherContext != (void*)(uintptr_t)0xBEEF)
fail("dispatcher context mismatch during delivery");
gBreakpointDispatchCount++;
SYS_CONTINUE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION;
request.Context = &frame->ContextRecord;
uint32_t status = SysContinueException(&request);
fail_status("continue exception returned", status);
}
// 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("exception_dispatcher_smoke: ");
print(what);
print(" expected=");
print_u64(expected);
print(" got=");
print_u64(got);
print("\n");
exit_now(1);
}
}
// Reads the current dispatcher registration.
static SYS_EXCEPTION_DISPATCHER_REQUEST get_registration(void) {
SYS_EXCEPTION_DISPATCHER_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
uint32_t status = SysGetExceptionDispatcher(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("get dispatcher", status);
if (request.Size != sizeof(request))
fail("get dispatcher size mismatch");
return request;
}
// Runs the native dispatcher registration smoke test.
int main(void) {
print("exception_dispatcher_smoke: start\n");
expect_status("null get", SysGetExceptionDispatcher(0), NTLSTATUS_INVALID_PARAMETER);
expect_status("null set", SysSetExceptionDispatcher(0), NTLSTATUS_INVALID_PARAMETER);
SYS_EXCEPTION_DISPATCHER_REQUEST initial = get_registration();
SYS_EXCEPTION_DISPATCHER_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request) - 1;
request.DispatcherRoutine = smoke_dispatcher;
expect_status("bad size", SysSetExceptionDispatcher(&request), NTLSTATUS_INVALID_PARAMETER);
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Flags = 1;
request.DispatcherRoutine = smoke_dispatcher;
expect_status("bad flags", SysSetExceptionDispatcher(&request), NTLSTATUS_INVALID_PARAMETER);
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.DispatcherRoutine = (SYS_USER_EXCEPTION_DISPATCHER)(uintptr_t)0xFFFF800000000000ull;
expect_status("kernel dispatcher", SysSetExceptionDispatcher(&request), NTLSTATUS_ACCESS_DENIED);
char tinyStack[32];
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.DispatcherRoutine = smoke_dispatcher;
request.ExceptionStackBase = tinyStack;
request.ExceptionStackSize = sizeof(tinyStack);
expect_status("tiny exception stack", SysSetExceptionDispatcher(&request), NTLSTATUS_BUFFER_TOO_SMALL);
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.DispatcherRoutine = smoke_dispatcher;
request.DispatcherContext = (void*)(uintptr_t)0x1234;
uint32_t status = SysSetExceptionDispatcher(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("set dispatcher", status);
SYS_EXCEPTION_DISPATCHER_REQUEST snapshot = get_registration();
if (snapshot.DispatcherRoutine != smoke_dispatcher)
fail("dispatcher routine mismatch");
if (snapshot.DispatcherContext != (void*)(uintptr_t)0x1234)
fail("dispatcher context mismatch");
if (snapshot.ExceptionStackBase || snapshot.ExceptionStackSize)
fail("unexpected exception stack registration");
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.DispatcherRoutine = smoke_continue_dispatcher;
request.DispatcherContext = (void*)(uintptr_t)0xBEEF;
status = SysSetExceptionDispatcher(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("set continue dispatcher", status);
__debugbreak();
if (gBreakpointDispatchCount != 1)
fail("breakpoint was not delivered exactly once");
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
status = SysSetExceptionDispatcher(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("clear dispatcher", status);
snapshot = get_registration();
if (snapshot.DispatcherRoutine || snapshot.DispatcherContext ||
snapshot.ExceptionStackBase || snapshot.ExceptionStackSize)
fail("dispatcher did not clear");
if (initial.DispatcherRoutine) {
status = SysSetExceptionDispatcher(&initial);
if (status != NTLSTATUS_SUCCESS)
fail_status("restore initial dispatcher", status);
}
print("exception_dispatcher_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,256 @@
// Native exception pipeline integration smoke test.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
#define CHILD_DIVIDE_BY_ZERO "child-divide-by-zero"
#define CHILD_ACCESS_VIOLATION "child-access-violation"
#define CHILD_INVALID_RESUME "child-invalid-resume"
#define CHILD_NONCONTINUABLE "child-noncontinuable"
#define SMOKE_NONCONTINUABLE_CODE 0xE1111003u
// 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("exception_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("exception_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Returns whether a string view exactly matches a literal string.
static int sv_equals_cstr(SYS_STRING_VIEW sv, const char* literal) {
size_t len = strlen(literal);
return sv.Length == len && sv.Data && memcmp(sv.Data, literal, len) == 0;
}
// Returns the current process command line.
static SYS_STRING_VIEW current_command_line(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb)
return SysSvFromParts(NULL, 0);
return peb->CommandLine;
}
// 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("exception_smoke: ");
print(what);
print(" expected=");
print_u64(expected);
print(" got=");
print_u64(got);
print("\n");
exit_now(1);
}
}
// Runs this image as a child and requires the expected exit code.
static void run_child_expect_exit(const char* mode, uint32_t expectedExit) {
SYS_PEB* peb = SysGetPeb();
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0)
fail("missing image path");
uint32_t child = 0;
SYS_CREATE_PROCESS_REQUEST request = {
peb->ImagePath,
SysSvFromCstr(mode),
&child,
};
uint32_t status = SysCreateProcess(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("create child", status);
status = SysWait(child, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS)
fail_status("wait child", status);
int32_t exitCode = 0;
status = SysGetProcessExitCode(child, &exitCode);
if (status != NTLSTATUS_SUCCESS)
fail_status("query child", status);
SysCloseHandle(child);
if ((uint32_t)exitCode != expectedExit) {
print("exception_smoke: child ");
print(mode);
print(" expected exit=");
print_u64(expectedExit);
print(" got=");
print_u64((uint32_t)exitCode);
print("\n");
exit_now(1);
}
}
// Triggers a divide-by-zero with no registered native handlers.
static void child_divide_by_zero(void) {
volatile int one = 1;
volatile int zero = 0;
volatile int sink = one / zero;
(void)sink;
exit_now(2);
}
// Observes an access violation and exits from inside the native handler.
static int access_violation_handler(SYS_EXCEPTION_RECORD* record,
SYS_USER_CONTEXT* context,
void* userContext) {
(void)context;
if (userContext != (void*)(uintptr_t)0xA11C)
exit_now(10);
if (!record || record->Code != SYS_EXCEPTION_CODE_ACCESS_VIOLATION)
exit_now(11);
if (record->FaultAddress != 0)
exit_now(12);
if (record->ParameterCount != 2 || record->Parameters[1] != 0)
exit_now(13);
exit_now(0);
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
// Triggers a null read so the handler can inspect the fault address.
static void child_access_violation(void) {
void* handle = NtlAddExceptionHandler(1, access_violation_handler, (void*)(uintptr_t)0xA11C);
if (!handle)
exit_now(20);
volatile uint8_t* p = (volatile uint8_t*)0;
volatile uint8_t value = *p;
(void)value;
exit_now(21);
}
// Returns a resume context with an invalid user RIP.
static int invalid_resume_handler(SYS_EXCEPTION_RECORD* record,
SYS_USER_CONTEXT* context,
void* userContext) {
(void)userContext;
if (!record || !context || record->Code != SYS_EXCEPTION_CODE_BREAKPOINT)
exit_now(30);
context->Rip = 0;
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
// Triggers a breakpoint whose handler asks the kernel to resume at RIP zero.
static void child_invalid_resume(void) {
void* handle = NtlAddExceptionHandler(1, invalid_resume_handler, NULL);
if (!handle)
exit_now(31);
__debugbreak();
exit_now(32);
}
// Attempts to continue a noncontinuable user-raised exception.
static int noncontinuable_handler(SYS_EXCEPTION_RECORD* record,
SYS_USER_CONTEXT* context,
void* userContext) {
(void)context;
(void)userContext;
if (!record || record->Code != SMOKE_NONCONTINUABLE_CODE)
exit_now(40);
if ((record->Flags & SYS_EXCEPTION_FLAG_NONCONTINUABLE) == 0)
exit_now(41);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
// Raises a noncontinuable exception that a handler must not resume.
static void child_noncontinuable(void) {
void* handle = NtlAddExceptionHandler(1, noncontinuable_handler, NULL);
if (!handle)
exit_now(42);
SYS_RAISE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Flags = SYS_RAISE_EXCEPTION_FLAG_NONCONTINUABLE;
request.Code = SMOKE_NONCONTINUABLE_CODE;
uint32_t status = SysRaiseException(&request);
exit_now((int32_t)status);
}
// Dispatches the child process mode selected by the command line.
static void run_child_if_requested(void) {
SYS_STRING_VIEW command = current_command_line();
if (sv_equals_cstr(command, CHILD_DIVIDE_BY_ZERO))
child_divide_by_zero();
if (sv_equals_cstr(command, CHILD_ACCESS_VIOLATION))
child_access_violation();
if (sv_equals_cstr(command, CHILD_INVALID_RESUME))
child_invalid_resume();
if (sv_equals_cstr(command, CHILD_NONCONTINUABLE))
child_noncontinuable();
}
// Runs validation that does not intentionally fault this process.
static void verify_continue_validation(void) {
expect_status("null continue", SysContinueException(NULL), NTLSTATUS_INVALID_PARAMETER);
SYS_CONTINUE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION;
expect_status("inactive continue", SysContinueException(&request), NTLSTATUS_INVALID_PARAMETER);
}
// Runs the native exception integration smoke test.
int main(void) {
run_child_if_requested();
print("exception_smoke: start\n");
verify_continue_validation();
run_child_expect_exit(CHILD_DIVIDE_BY_ZERO, NTLSTATUS_PROCESS_DIVIDE_BY_ZERO);
run_child_expect_exit(CHILD_ACCESS_VIOLATION, NTLSTATUS_SUCCESS);
run_child_expect_exit(CHILD_INVALID_RESUME, NTLSTATUS_ACCESS_DENIED);
run_child_expect_exit(CHILD_NONCONTINUABLE, NTLSTATUS_PROCESS_UNKNOWN_FAULT);
print("exception_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -75,7 +75,7 @@ static uint32_t open_existing_lockable(const char* path, uint32_t* fileOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
*fileOut = h;
return s;
}
@@ -98,13 +98,13 @@ static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
*pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
// Locks a range through the public request ABI.
@@ -175,49 +175,49 @@ static void run_child(SYS_STRING_VIEW commandLine) {
uint32_t file = 0;
uint32_t s = open_existing_lockable(path, &file);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
exit_now(3);
if (str_eq(mode, "excl-blocked")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
SysCloseHandle(file);
exit_now(s == STATUS_WOULD_BLOCK ? 0 : 4);
exit_now(s == NTLSTATUS_WOULD_BLOCK ? 0 : 4);
}
if (str_eq(mode, "excl-timeout")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 20);
SysCloseHandle(file);
exit_now(s == STATUS_TIMEOUT ? 0 : 5);
exit_now(s == NTLSTATUS_TIMEOUT ? 0 : 5);
}
if (str_eq(mode, "excl-wait")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, SYS_WAIT_TIMEOUT_INFINITE);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 6);
exit_now(s == NTLSTATUS_SUCCESS ? 0 : 6);
}
if (str_eq(mode, "shared-ok")) {
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 7);
exit_now(s == NTLSTATUS_SUCCESS ? 0 : 7);
}
if (str_eq(mode, "excl-nonoverlap")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, NONOVERLAP_OFFSET, LOCK_LENGTH, 0);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
(void)unlock_range(file, NONOVERLAP_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 8);
exit_now(s == NTLSTATUS_SUCCESS ? 0 : 8);
}
if (str_eq(mode, "hold-exit")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 9);
exit_now(s == NTLSTATUS_SUCCESS ? 0 : 9);
}
SysCloseHandle(file);
@@ -251,14 +251,14 @@ static void run_child_expect_success(const char* mode, const char* path) {
&child,
};
uint32_t s = SysCreateProcess(&req);
if (s != STATUS_SUCCESS) fail_status("create child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create child", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query child", s);
SysCloseHandle(child);
if (exitCode != 0) {
print("file_lock_smoke: child ");
@@ -282,12 +282,12 @@ static void test_validation(uint32_t file) {
0,
};
uint32_t s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("bad lock size", s);
if (s != NTLSTATUS_INVALID_PARAMETER) fail_status("bad lock size", s);
lockReq.Size = sizeof(lockReq);
lockReq.Length = 0;
s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("zero lock length", s);
if (s != NTLSTATUS_INVALID_PARAMETER) fail_status("zero lock length", s);
SYS_UNLOCK_FILE_REQUEST unlockReq = {
sizeof(unlockReq),
@@ -297,12 +297,12 @@ static void test_validation(uint32_t file) {
LOCK_LENGTH,
};
s = SysUnlockFile(&unlockReq);
if (s != STATUS_NOT_FOUND) fail_status("unlock missing range", s);
if (s != NTLSTATUS_NOT_FOUND) fail_status("unlock missing range", s);
lockReq.Length = LOCK_LENGTH;
lockReq.FileHandle = 0xeeeeeeeeu;
s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_HANDLE) fail_status("bad lock handle", s);
if (s != NTLSTATUS_INVALID_HANDLE) fail_status("bad lock handle", s);
}
// Program entry point.
@@ -322,27 +322,27 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp_file(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s);
test_validation(file);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("exclusive parent lock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("exclusive parent lock", s);
run_child_expect_success("excl-blocked", path);
run_child_expect_success("excl-timeout", path);
run_child_expect_success("excl-nonoverlap", path);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("exclusive parent unlock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("exclusive parent unlock", s);
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("shared parent lock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("shared parent lock", s);
run_child_expect_success("shared-ok", path);
run_child_expect_success("excl-blocked", path);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("shared parent unlock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("shared parent unlock", s);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("blocking setup lock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("blocking setup lock", s);
SYS_PEB* selfPeb = SysGetPeb();
if (!selfPeb || !selfPeb->ImagePath.Data || selfPeb->ImagePath.Length == 0) fail("missing self image");
char command[CHILD_PREFIX_MAX + CHILD_PATH_MAX];
@@ -350,23 +350,23 @@ int main(void) {
uint32_t child = 0;
SYS_CREATE_PROCESS_REQUEST createReq = { selfPeb->ImagePath, SysSvFromCstr(command), &child };
s = SysCreateProcess(&createReq);
if (s != STATUS_SUCCESS) fail_status("create blocking child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create blocking child", s);
SysSleep(30);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("wake unlock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wake unlock", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait blocking child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait blocking child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query blocking child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query blocking child", s);
SysCloseHandle(child);
if (exitCode != 0) fail("blocking child failed");
run_child_expect_success("hold-exit", path);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("exit released lock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("exit released lock", s);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("exit release cleanup unlock", s);
if (s != NTLSTATUS_SUCCESS) fail_status("exit release cleanup unlock", s);
SysCloseHandle(file);
delete_path(path);
@@ -88,14 +88,14 @@ static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
*pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
// Creates a temporary directory on the first writable path that works.
@@ -111,13 +111,13 @@ static uint32_t create_temp_dir(const char** pathOut) {
remove_dir_path(paths[i]);
SYS_PATH_REQUEST req = { SysSvFromCstr(paths[i]) };
uint32_t s = SysCreateDir(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
// Fills the write pattern used by the test file.
@@ -131,11 +131,11 @@ static void write_test_file(uint32_t file) {
size_t done = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &done);
uint32_t s = SysWriteFile(&writeReq);
if (s != STATUS_SUCCESS) fail_status("write test file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("write test file", s);
if (done != sizeof(gPattern)) fail("short write");
s = SysFlushFile(file);
if (s != STATUS_SUCCESS) fail_status("flush test file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("flush test file", s);
}
// Checks one regular-file metadata result.
@@ -176,19 +176,19 @@ static void test_file_stat(uint32_t file, const char* path) {
SYS_FILE_STAT byHandle;
memset(&byHandle, 0xA5, sizeof(byHandle));
uint32_t s = SysGetFileStat(file, &byHandle);
if (s != STATUS_SUCCESS) fail_status("get file stat", s);
if (s != NTLSTATUS_SUCCESS) fail_status("get file stat", s);
check_regular_stat(&byHandle, sizeof(gPattern), "handle stat");
uint64_t size = 0;
s = SysGetFileSize(file, &size);
if (s != STATUS_SUCCESS) fail_status("get file size", s);
if (s != NTLSTATUS_SUCCESS) fail_status("get file size", s);
if (size != byHandle.FileSize) fail("file size and file stat disagree");
SYS_FILE_STAT byPath;
memset(&byPath, 0x5A, sizeof(byPath));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &byPath };
s = SysStatFile(&statReq);
if (s != STATUS_SUCCESS) fail_status("path stat", s);
if (s != NTLSTATUS_SUCCESS) fail_status("path stat", s);
check_regular_stat(&byPath, sizeof(gPattern), "path stat");
if (byHandle.VolumeId != 0 && byPath.VolumeId != 0 && byHandle.VolumeId != byPath.VolumeId)
@@ -203,7 +203,7 @@ static void test_directory_stat(const char* path) {
memset(&stat, 0xCC, sizeof(stat));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &stat };
uint32_t s = SysStatFile(&statReq);
if (s != STATUS_SUCCESS) fail_status("directory stat", s);
if (s != NTLSTATUS_SUCCESS) fail_status("directory stat", s);
check_directory_stat(&stat);
}
@@ -212,14 +212,14 @@ static void test_failure_paths(void) {
SYS_FILE_STAT stat;
SYS_STAT_FILE_REQUEST missingReq = { SysSvFromCstr("file_stat_smoke_missing.nope"), &stat };
uint32_t s = SysStatFile(&missingReq);
if (s != STATUS_NOT_FOUND) fail_status("missing path status", s);
if (s != NTLSTATUS_NOT_FOUND) fail_status("missing path status", s);
s = SysGetFileStat(0xFFFFFFFFu, &stat);
if (s != STATUS_INVALID_HANDLE) fail_status("invalid handle status", s);
if (s != NTLSTATUS_INVALID_HANDLE) fail_status("invalid handle status", s);
SYS_STAT_FILE_REQUEST badReq = { SysSvFromCstr("file_stat_smoke_missing.nope"), 0 };
s = SysStatFile(&badReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("null stat output status", s);
if (s != NTLSTATUS_INVALID_PARAMETER) fail_status("null stat output status", s);
}
// Runs the native file metadata smoke test.
@@ -231,7 +231,7 @@ int main(void) {
uint32_t file = 0;
const char* filePath = 0;
uint32_t s = create_temp_file(&file, &filePath);
if (s != STATUS_SUCCESS) fail_status("create temp file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp file", s);
write_test_file(file);
test_file_stat(file, filePath);
@@ -240,13 +240,13 @@ int main(void) {
const char* dirPath = 0;
s = create_temp_dir(&dirPath);
if (s != STATUS_SUCCESS) fail_status("create temp dir", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(dirPath) };
s = SysCreateDir(&dirReq);
if (s != STATUS_SUCCESS) fail_status("recreate temp dir", s);
if (s != NTLSTATUS_SUCCESS) fail_status("recreate temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
@@ -127,61 +127,61 @@ int main(void) {
uint32_t currentPid = SysGetCurrentPid();
uint32_t process = 0;
uint32_t s = SysOpenProcess(currentPid, &process);
if (s != STATUS_SUCCESS) fail_status("open current process", s);
if (s != NTLSTATUS_SUCCESS) fail_status("open current process", s);
uint32_t queue = 0;
s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create queue", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create queue", s);
uint32_t readOnly = 0;
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_NONE, &readOnly);
if (s != STATUS_SUCCESS) fail_status("duplicate read-only", s);
if (s != NTLSTATUS_SUCCESS) fail_status("duplicate read-only", s);
uint32_t writeOnly = 0;
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_WRITE, SYS_DUPLICATE_HANDLE_FLAG_NONE, &writeOnly);
if (s != STATUS_SUCCESS) fail_status("duplicate write-only", s);
if (s != NTLSTATUS_SUCCESS) fail_status("duplicate write-only", s);
uint32_t sameRights = 0;
s = duplicate_handle(queue, process, 0, SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS, &sameRights);
if (s != STATUS_SUCCESS) fail_status("duplicate same rights", s);
if (s != NTLSTATUS_SUCCESS) fail_status("duplicate same rights", s);
s = send_message(readOnly, 10, 100);
if (s == STATUS_SUCCESS) fail("read-only duplicate allowed send");
if (s == NTLSTATUS_SUCCESS) fail("read-only duplicate allowed send");
TEST_QUEUE_MESSAGE msg;
s = recv_message(writeOnly, &msg);
if (s == STATUS_SUCCESS) fail("write-only duplicate allowed receive");
if (s == NTLSTATUS_SUCCESS) fail("write-only duplicate allowed receive");
s = send_message(queue, 11, 101);
if (s != STATUS_SUCCESS) fail_status("send original", s);
if (s != NTLSTATUS_SUCCESS) fail_status("send original", s);
s = recv_message(readOnly, &msg);
if (s != STATUS_SUCCESS) fail_status("receive read-only", s);
if (s != NTLSTATUS_SUCCESS) fail_status("receive read-only", s);
check_message(&msg, 11, 101);
s = send_message(writeOnly, 12, 102);
if (s != STATUS_SUCCESS) fail_status("send write-only", s);
if (s != NTLSTATUS_SUCCESS) fail_status("send write-only", s);
s = recv_message(queue, &msg);
if (s != STATUS_SUCCESS) fail_status("receive original", s);
if (s != NTLSTATUS_SUCCESS) fail_status("receive original", s);
check_message(&msg, 12, 102);
s = send_message(sameRights, 13, 103);
if (s != STATUS_SUCCESS) fail_status("send same-rights", s);
if (s != NTLSTATUS_SUCCESS) fail_status("send same-rights", s);
s = recv_message(sameRights, &msg);
if (s != STATUS_SUCCESS) fail_status("receive same-rights", s);
if (s != NTLSTATUS_SUCCESS) fail_status("receive same-rights", s);
check_message(&msg, 13, 103);
uint32_t ignored = 0;
s = duplicate_handle(readOnly, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate non-duplicable handle", s, STATUS_ACCESS_DENIED);
expect_status("duplicate non-duplicable handle", s, NTLSTATUS_ACCESS_DENIED);
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ | 0x80000000u, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate unknown rights", s, STATUS_INVALID_PARAMETER);
expect_status("duplicate unknown rights", s, NTLSTATUS_INVALID_PARAMETER);
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS, &ignored);
expect_status("duplicate same-rights with desired rights", s, STATUS_INVALID_PARAMETER);
expect_status("duplicate same-rights with desired rights", s, NTLSTATUS_INVALID_PARAMETER);
s = duplicate_handle(queue, process, 0, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate zero rights", s, STATUS_INVALID_PARAMETER);
expect_status("duplicate zero rights", s, NTLSTATUS_INVALID_PARAMETER);
SYS_DUPLICATE_HANDLE_REQUEST badOut = {
queue,
@@ -191,7 +191,7 @@ int main(void) {
0,
};
s = SysDuplicateHandle(&badOut);
expect_status("duplicate null output", s, STATUS_INVALID_PARAMETER);
expect_status("duplicate null output", s, NTLSTATUS_INVALID_PARAMETER);
SysCloseHandle(sameRights);
SysCloseHandle(writeOnly);
@@ -62,21 +62,21 @@ static void expect_status(const char* what, uint32_t got, uint32_t expected) {
int main(void) {
print("input_api_smoke: start\n");
expect_status("poll keyboard null", SysPollKeyboard((INPUT_KEY*)0), STATUS_INVALID_PARAMETER);
expect_status("is key down null", SysIsKeyDown(INPUT_KEY_A, (bool*)0), STATUS_INVALID_PARAMETER);
expect_status("read key null", SysReadKey((INPUT_KEY*)0), STATUS_INVALID_PARAMETER);
expect_status("read char null", SysReadKeyboardChar((char*)0), STATUS_INVALID_PARAMETER);
expect_status("poll keyboard null", SysPollKeyboard((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("is key down null", SysIsKeyDown(INPUT_KEY_A, (bool*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read key null", SysReadKey((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read char null", SysReadKeyboardChar((char*)0), NTLSTATUS_INVALID_PARAMETER);
bool down = true;
uint32_t status = SysIsKeyDown(INPUT_KEY_A, &down);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("is key down", status);
INPUT_KEY key = INPUT_KEY_A;
status = SysPollKeyboard(&key);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("poll keyboard", status);
if (status == STATUS_WOULD_BLOCK && key != INPUT_KEY_NONE)
if (status == NTLSTATUS_WOULD_BLOCK && key != INPUT_KEY_NONE)
fail_status("poll keyboard would-block key", key);
INPUT_KEY_RECORD input;
@@ -87,33 +87,33 @@ int main(void) {
inputReq.TimeoutMilliseconds = 0;
inputReq.RecordOut = &input;
expect_status("peek input null", SysPeekInputEvent((const SYS_INPUT_EVENT_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("peek input null", SysPeekInputEvent((const SYS_INPUT_EVENT_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysPeekInputEvent(&inputReq);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek input", status);
INPUT_MOUSE_EVENT mouse;
expect_status("peek mouse null", SysPeekMouse((INPUT_MOUSE_EVENT*)0), STATUS_INVALID_PARAMETER);
expect_status("read mouse null", SysReadMouse((INPUT_MOUSE_EVENT*)0), STATUS_INVALID_PARAMETER);
expect_status("peek mouse null", SysPeekMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read mouse null", SysReadMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysPeekMouse(&mouse);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek mouse", status);
uint32_t readStatus = SysReadMouse(&mouse);
if (status == STATUS_SUCCESS) {
if (readStatus != STATUS_SUCCESS)
if (status == NTLSTATUS_SUCCESS) {
if (readStatus != NTLSTATUS_SUCCESS)
fail_status("read mouse after peek", readStatus);
} else if (readStatus != STATUS_WOULD_BLOCK) {
} else if (readStatus != NTLSTATUS_WOULD_BLOCK) {
fail_status("read mouse would-block", readStatus);
}
int x = 0;
int y = 0;
expect_status("get mouse pos null x", SysGetMousePos((int*)0, &y), STATUS_INVALID_PARAMETER);
expect_status("get mouse pos null y", SysGetMousePos(&x, (int*)0), STATUS_INVALID_PARAMETER);
expect_status("get mouse pos null x", SysGetMousePos((int*)0, &y), NTLSTATUS_INVALID_PARAMETER);
expect_status("get mouse pos null y", SysGetMousePos(&x, (int*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysGetMousePos(&x, &y);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("get mouse pos", status);
print("input_api_smoke: PASS\n");
@@ -62,11 +62,11 @@ int main(void) {
print("input_model_smoke: start\n");
uint32_t inputQueue = 0;
expect_status("open input queue null", SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, (uint32_t*)0), STATUS_INVALID_PARAMETER);
expect_status("open input queue bad flags", SysOpenInputQueue(1, &inputQueue), STATUS_INVALID_PARAMETER);
expect_status("open input queue null", SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, (uint32_t*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("open input queue bad flags", SysOpenInputQueue(1, &inputQueue), NTLSTATUS_INVALID_PARAMETER);
uint32_t status = SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, &inputQueue);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("open input queue", status);
INPUT_KEY_RECORD record;
@@ -79,31 +79,31 @@ int main(void) {
SYS_INPUT_EVENT_REQUEST badReq = req;
badReq.Size = 0;
expect_status("read input bad size", SysReadInputEvent(&badReq), STATUS_INVALID_PARAMETER);
expect_status("read input bad size", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER);
badReq = req;
badReq.RecordOut = (INPUT_KEY_RECORD*)0;
expect_status("peek input null out", SysPeekInputEvent(&badReq), STATUS_INVALID_PARAMETER);
expect_status("peek input null out", SysPeekInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER);
badReq = req;
badReq.QueueHandle = 0xdeadbeefu;
expect_status("read input bad handle", SysReadInputEvent(&badReq), STATUS_INVALID_HANDLE);
expect_status("read input bad handle", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_HANDLE);
status = SysPeekInputEvent(&req);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek input immediate", status);
uint32_t waitStatus = SysWaitSingle(inputQueue, SYS_WAIT_FLAG_NONE, 0);
if (status == STATUS_SUCCESS) {
if (waitStatus != STATUS_SUCCESS)
if (status == NTLSTATUS_SUCCESS) {
if (waitStatus != NTLSTATUS_SUCCESS)
fail_status("wait input after readable peek", waitStatus);
} else if (waitStatus != STATUS_TIMEOUT) {
} else if (waitStatus != NTLSTATUS_TIMEOUT) {
fail_status("wait empty input immediate", waitStatus);
}
req.TimeoutMilliseconds = 1;
status = SysReadInputEvent(&req);
if (status != STATUS_SUCCESS && status != STATUS_TIMEOUT)
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT)
fail_status("read input finite timeout", status);
SysCloseHandle(inputQueue);
+8 -8
View File
@@ -76,15 +76,15 @@ static uint32_t open_first_existing(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&file);
uint32_t s = SysOpenFile(&openReq);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = file;
if (pathOut)
*pathOut = candidates[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
int main(void) {
@@ -93,7 +93,7 @@ int main(void) {
uint32_t file = 0;
const char* openedPath = 0;
uint32_t s = open_first_existing(&file, &openedPath);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("open test file", s);
print("mmap_smoke: using ");
@@ -105,7 +105,7 @@ int main(void) {
SYS_FILE_IO_REQUEST ioReq = SysMakeFileIoRequest(file, gExpected, sizeof(gExpected), &got);
s = SysReadFile(&ioReq);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("read baseline", s);
if (got < 64)
fail_msg("baseline read was unexpectedly short");
@@ -118,7 +118,7 @@ int main(void) {
SYS_MAP_FLAG_NONE,
sizeof(gExpected),
&mapping);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("create file mapping", s);
print("mmap_smoke: mapping created\n");
@@ -141,7 +141,7 @@ int main(void) {
SysCloseHandle(mapping);
mapping = 0;
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("map file view", s);
if (!base)
fail_msg("map returned null base");
@@ -191,7 +191,7 @@ int main(void) {
print("mmap_smoke: EOF fill OK\n");
s = SysFreeVm(base);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("unmap", s);
print("mmap_smoke: PASS\n");
@@ -68,14 +68,14 @@ static void check_module_info(const SYS_MODULE_INFO* info) {
static int module_list_contains(uintptr_t base) {
size_t count = 0;
uint32_t status = SysGetModuleList(0, 0, &count);
if (status != STATUS_SUCCESS) fail_status("query module count", status);
if (status != NTLSTATUS_SUCCESS) fail_status("query module count", status);
if (count == 0) return 0;
if (count > 32) fail("module count unexpectedly large");
SYS_MODULE_INFO modules[32];
memset(modules, 0, sizeof(modules));
status = SysGetModuleList(modules, count, &count);
if (status != STATUS_SUCCESS) fail_status("query module list", status);
if (status != NTLSTATUS_SUCCESS) fail_status("query module list", status);
for (size_t i = 0; i < count; i++) {
if (modules[i].Base == base)
@@ -95,7 +95,7 @@ static void check_export_lookup(void) {
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) fail_status("load syscall.dll", status);
if (status != NTLSTATUS_SUCCESS) fail_status("load syscall.dll", status);
if (handle == 0) fail("syscall load returned null handle");
uintptr_t address = 0;
@@ -107,11 +107,11 @@ static void check_export_lookup(void) {
exportReq.AddressOut = &address;
status = SysGetModuleExport(&exportReq);
if (status != STATUS_SUCCESS) fail_status("lookup SysGetCurrentPid", status);
if (status != NTLSTATUS_SUCCESS) fail_status("lookup SysGetCurrentPid", status);
if (address == 0) fail("export address is zero");
status = SysUnloadModule(handle);
if (status != STATUS_SUCCESS) fail_status("unload syscall.dll handle", status);
if (status != NTLSTATUS_SUCCESS) fail_status("unload syscall.dll handle", status);
}
// Runs the public dynamic module loader smoke test.
@@ -131,7 +131,7 @@ int main(void) {
req.InfoOut = &info;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) fail_status("load posix.dll", status);
if (status != NTLSTATUS_SUCCESS) fail_status("load posix.dll", status);
if (handle == 0) fail("load returned null handle");
check_module_info(&info);
if (!module_list_contains(info.Base)) fail("loaded module absent from list");
@@ -139,16 +139,16 @@ int main(void) {
SYS_MODULE_INFO byHandle;
memset(&byHandle, 0, sizeof(byHandle));
status = SysGetModuleInfo(handle, &byHandle);
if (status != STATUS_SUCCESS) fail_status("get module info", status);
if (status != NTLSTATUS_SUCCESS) fail_status("get module info", status);
check_module_info(&byHandle);
if (byHandle.Base != info.Base) fail("module handle base mismatch");
status = SysUnloadModule(handle);
if (status != STATUS_SUCCESS) fail_status("unload module", status);
if (status != NTLSTATUS_SUCCESS) fail_status("unload module", status);
memset(&byHandle, 0, sizeof(byHandle));
status = SysGetModuleInfo(handle, &byHandle);
if (status != STATUS_INVALID_HANDLE) fail_status("query unloaded handle", status);
if (status != NTLSTATUS_INVALID_HANDLE) fail_status("query unloaded handle", status);
print("module_load_smoke: PASS\n");
exit_now(0);
@@ -0,0 +1,194 @@
// Native named queue lifetime smoke test.
// Verifies explicit name withdrawal and automatic withdrawal on owner handle close.
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_MESSAGE_TYPE 0x71756575u
#define TEST_MESSAGE_VALUE 0x123456789abcdef0ull
typedef struct TEST_QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6];
} TEST_QUEUE_MESSAGE;
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
];
// 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 to the kernel console.
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 test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("named_queue_lifetime_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("named_queue_lifetime_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("named_queue_lifetime_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Publishes one queue under a short test name.
static uint32_t publish_queue(const char* name, uint32_t queue) {
SYS_PUBLISH_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(name);
req.QueueHandle = queue;
return SysPublishQueue(&req);
}
// Withdraws one named queue publication.
static uint32_t unpublish_queue(const char* name) {
SYS_UNPUBLISH_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(name);
return SysUnpublishQueue(&req);
}
// Opens one named queue publication.
static uint32_t open_named_queue(const char* name, uint32_t* out) {
SYS_OPEN_NAMED_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(name);
req.HandleOut = out;
return SysOpenNamedQueue(&req);
}
// Sends one fixed-size queue message.
static uint32_t send_message(uint32_t queue, const TEST_QUEUE_MESSAGE* message) {
SYS_QUEUE_SEND_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.QueueHandle = queue;
req.Flags = SYS_QUEUE_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.Message = message;
return SysSendQueue(&req);
}
// Receives one fixed-size queue message.
static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* message) {
SYS_QUEUE_RECV_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.QueueHandle = queue;
req.Flags = SYS_QUEUE_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.MessageOut = message;
return SysRecvQueue(&req);
}
// Verifies explicit unpublish removes only the name, not existing handles.
static void check_explicit_unpublish(void) {
uint32_t queue = 0;
uint32_t opened = 0;
uint32_t status = SysCreateQueue(&queue);
if (status != NTLSTATUS_SUCCESS) fail_status("create queue", status);
status = publish_queue("ntl.queue.explicit", queue);
if (status != NTLSTATUS_SUCCESS) fail_status("publish queue", status);
status = open_named_queue("ntl.queue.explicit", &opened);
if (status != NTLSTATUS_SUCCESS) fail_status("open published queue", status);
if (!opened) fail("opened handle is zero");
status = unpublish_queue("ntl.queue.explicit");
if (status != NTLSTATUS_SUCCESS) fail_status("unpublish queue", status);
uint32_t missing = 0;
status = open_named_queue("ntl.queue.explicit", &missing);
expect_status("open withdrawn name", status, NTLSTATUS_NOT_FOUND);
TEST_QUEUE_MESSAGE out;
TEST_QUEUE_MESSAGE in;
memset(&out, 0, sizeof(out));
memset(&in, 0, sizeof(in));
out.Type = TEST_MESSAGE_TYPE;
out.Param[0] = TEST_MESSAGE_VALUE;
status = send_message(queue, &out);
if (status != NTLSTATUS_SUCCESS) fail_status("send after unpublish", status);
status = recv_message(opened, &in);
if (status != NTLSTATUS_SUCCESS) fail_status("recv after unpublish", status);
if (in.Type != TEST_MESSAGE_TYPE || in.Param[0] != TEST_MESSAGE_VALUE)
fail("message mismatch after unpublish");
SysCloseHandle(opened);
SysCloseHandle(queue);
}
// Verifies closing the publishing handle withdraws its names.
static void check_close_withdraws_name(void) {
uint32_t queue = 0;
uint32_t status = SysCreateQueue(&queue);
if (status != NTLSTATUS_SUCCESS) fail_status("create close queue", status);
status = publish_queue("ntl.queue.close", queue);
if (status != NTLSTATUS_SUCCESS) fail_status("publish close queue", status);
SysCloseHandle(queue);
uint32_t opened = 0;
status = open_named_queue("ntl.queue.close", &opened);
expect_status("open close-withdrawn name", status, NTLSTATUS_NOT_FOUND);
}
// Runs the named queue lifetime smoke test.
int main(void) {
print("named_queue_lifetime_smoke: start\n");
check_explicit_unpublish();
check_close_withdraws_name();
print("named_queue_lifetime_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -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;
}
@@ -0,0 +1,143 @@
// Native user-raised exception smoke test.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/exception.h>
#include <ntlite/syscall.h>
#define SMOKE_EXCEPTION_CODE 0xE1111001u
static volatile uint32_t gHandlerHits;
// 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_raise_exception_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_raise_exception_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_raise_exception_smoke: ");
print(what);
print(" expected=");
print_u64(expected);
print(" got=");
print_u64(got);
print("\n");
exit_now(1);
}
}
// Reads aggregate exception counters.
static void query_exception_stats(SYS_EXCEPTION_STATS* out) {
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, out, sizeof(*out), 0);
expect_status("query exception stats", status, NTLSTATUS_SUCCESS);
if (out->Size != sizeof(*out) || out->Version != SYS_STATS_VERSION)
fail("exception stats ABI mismatch");
}
// Handles the user-raised exception and verifies its native record payload.
static int smoke_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
if (userContext != (void*)(uintptr_t)0x111E)
fail("handler context mismatch");
if (!record || !context)
fail("handler received null records");
if (record->Code != SMOKE_EXCEPTION_CODE)
fail("handler received wrong exception code");
if ((record->Flags & SYS_EXCEPTION_FLAG_FIRST_CHANCE) == 0)
fail("handler missing first-chance flag");
if (record->Flags & SYS_EXCEPTION_FLAG_NONCONTINUABLE)
fail("handler received unexpected noncontinuable flag");
if (record->ParameterCount != 2)
fail("handler received wrong parameter count");
if (record->Parameters[0] != 0x12345678u || record->Parameters[1] != 0xABCDEF00u)
fail("handler received wrong parameters");
if (!context->Rip || !context->Rsp)
fail("handler received empty context");
gHandlerHits++;
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
int main(void) {
print("native_raise_exception_smoke: start\n");
SYS_EXCEPTION_STATS before;
SYS_EXCEPTION_STATS after;
query_exception_stats(&before);
void* handle = NtlAddExceptionHandler(1, smoke_handler, (void*)(uintptr_t)0x111E);
if (!handle)
fail("failed to add native exception handler");
uint64_t args[2] = { 0x12345678u, 0xABCDEF00u };
SYS_RAISE_EXCEPTION_REQUEST request;
request.Size = sizeof(request);
request.Flags = SYS_RAISE_EXCEPTION_FLAG_NONE;
request.Code = SMOKE_EXCEPTION_CODE;
request.ParameterCount = 2;
request.Parameters = args;
uint32_t status = SysRaiseException(&request);
expect_status("raise exception", status, NTLSTATUS_SUCCESS);
if (gHandlerHits != 1)
fail("handler was not called exactly once");
status = NtlRemoveExceptionHandler(handle);
expect_status("remove native exception handler", status, NTLSTATUS_SUCCESS);
query_exception_stats(&after);
if (after.Delivered < before.Delivered + 1 ||
after.UserRaised < before.UserRaised + 1 ||
after.Continued < before.Continued + 1)
fail("exception counters did not advance");
if (after.LastCode != SMOKE_EXCEPTION_CODE)
fail("last exception code was not updated");
print("native_raise_exception_smoke: PASS\n");
exit_now(0);
return 0;
}
+23 -23
View File
@@ -62,7 +62,7 @@ static SYS_NET_IRP_STATS query_stats(void) {
SYS_NET_IRP_STATS stats;
memset(&stats, 0, sizeof(stats));
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_NET_IRP, &stats, sizeof(stats), 0);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("query net irp stats", status);
if (stats.Size != sizeof(stats))
fail("net irp stats size mismatch");
@@ -93,20 +93,20 @@ static void set_u32_option(uint32_t socket, uint32_t option, uint32_t value) {
req.Value = &value;
req.ValueSize = sizeof(value);
uint32_t status = SysSetSocketOption(socket, &req);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("set socket option", status);
}
// Waits for an async socket request to publish a terminal IOSB status.
static void wait_for_async(uint32_t event, SYS_IO_STATUS_BLOCK* iosb, const char* what) {
for (uint32_t i = 0; i < 200u && iosb->Status == STATUS_PENDING; ++i) {
for (uint32_t i = 0; i < 200u && iosb->Status == NTLSTATUS_PENDING; ++i) {
uint32_t status = SysWait(event, 0);
if (status != STATUS_SUCCESS && status != STATUS_ALERTED &&
status != STATUS_KERNEL_APC && status != STATUS_USER_APC) {
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_ALERTED &&
status != NTLSTATUS_KERNEL_APC && status != NTLSTATUS_USER_APC) {
fail_status(what, status);
}
}
if (iosb->Status == STATUS_PENDING)
if (iosb->Status == NTLSTATUS_PENDING)
fail("async request stayed pending");
}
@@ -114,12 +114,12 @@ static void wait_for_async(uint32_t event, SYS_IO_STATUS_BLOCK* iosb, const char
static void check_nonblocking_receive(void) {
uint32_t udp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("create udp socket", status);
SYS_SOCKET_ADDRESS bindAddress = make_ipv4(0, 0, 0, 0, NET_IRP_SMOKE_UDP_PORT);
status = SysBindSocket(udp, &bindAddress, sizeof(bindAddress));
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("bind udp socket", status);
}
@@ -143,7 +143,7 @@ static void check_nonblocking_receive(void) {
status = SysRecvFromSocket(udp, &req);
SysCloseHandle(udp);
if (status != STATUS_WOULD_BLOCK)
if (status != NTLSTATUS_WOULD_BLOCK)
fail_status("nonblocking udp recvfrom", status);
}
@@ -151,19 +151,19 @@ static void check_nonblocking_receive(void) {
static void check_async_receive_cancel(void) {
uint32_t udp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("create cancel udp socket", status);
SYS_SOCKET_ADDRESS bindAddress = make_ipv4(0, 0, 0, 0, NET_IRP_SMOKE_UDP_PORT + 1u);
status = SysBindSocket(udp, &bindAddress, sizeof(bindAddress));
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("bind cancel udp socket", status);
}
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("create event", status);
}
@@ -193,7 +193,7 @@ static void check_async_receive_cancel(void) {
SYS_NET_IRP_STATS beforeSubmit = query_stats();
status = SysSocketIoAsync(&req);
if (status != STATUS_PENDING || requestHandle == 0) {
if (status != NTLSTATUS_PENDING || requestHandle == 0) {
SysCloseHandle(event);
SysCloseHandle(udp);
fail_status("submit async recvfrom", status);
@@ -213,17 +213,17 @@ static void check_async_receive_cancel(void) {
fail("async recvfrom did not enter the IRP path");
uint32_t cancelStatus = SysCancelIo(requestHandle);
if (cancelStatus != STATUS_SUCCESS && cancelStatus != STATUS_PENDING)
if (cancelStatus != NTLSTATUS_SUCCESS && cancelStatus != NTLSTATUS_PENDING)
fail_status("cancel async recvfrom", cancelStatus);
wait_for_async(event, &iosb, "wait async recvfrom cancel");
if (iosb.Status != STATUS_CANCELLED)
if (iosb.Status != NTLSTATUS_CANCELLED)
fail_status("async recvfrom iosb", iosb.Status);
if (iosb.Information != 0)
fail("async recvfrom cancel transferred bytes");
status = SysWaitSingle(requestHandle, 0, 0);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("wait cancelled request handle", status);
SysCloseHandle(requestHandle);
@@ -235,7 +235,7 @@ static void check_async_receive_cancel(void) {
static void try_http_async_success(void) {
uint32_t tcp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &tcp);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
print("net_irp_smoke: http success path skipped create status=");
print_u64(status);
print("\n");
@@ -244,7 +244,7 @@ static void try_http_async_success(void) {
SYS_SOCKET_ADDRESS address = make_ipv4(10, 0, 2, 2, NET_IRP_SMOKE_HTTP_PORT);
status = SysConnectSocket(tcp, &address, sizeof(address));
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
print("net_irp_smoke: http success path skipped connect status=");
print_u64(status);
print("\n");
@@ -254,7 +254,7 @@ static void try_http_async_success(void) {
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(tcp);
fail_status("create http event", status);
}
@@ -276,10 +276,10 @@ static void try_http_async_success(void) {
sendReq.RequestHandleOut = &sendRequest;
status = SysSocketIoAsync(&sendReq);
if (status != STATUS_PENDING || sendRequest == 0)
if (status != NTLSTATUS_PENDING || sendRequest == 0)
fail_status("submit async send", status);
wait_for_async(event, &sendIosb, "wait async send");
if (sendIosb.Status != STATUS_SUCCESS)
if (sendIosb.Status != NTLSTATUS_SUCCESS)
fail_status("async send iosb", sendIosb.Status);
if (sendIosb.Information != sizeof(requestText) - 1u)
fail("async send byte count mismatch");
@@ -303,10 +303,10 @@ static void try_http_async_success(void) {
recvReq.RequestHandleOut = &recvRequest;
status = SysSocketIoAsync(&recvReq);
if (status != STATUS_PENDING || recvRequest == 0)
if (status != NTLSTATUS_PENDING || recvRequest == 0)
fail_status("submit async recv", status);
wait_for_async(event, &recvIosb, "wait async recv");
if (recvIosb.Status != STATUS_SUCCESS)
if (recvIosb.Status != NTLSTATUS_SUCCESS)
fail_status("async recv iosb", recvIosb.Status);
if (recvIosb.Information == 0)
fail("async recv returned no bytes");
@@ -1,5 +1,5 @@
// Page-cache write/truncate invalidation regression test.
// Verifies cached clean pages are not reused after file write or truncate.
// Page-cache write/truncate coherency regression test.
// Verifies cached pages stay coherent after file write and cached tails are dropped after truncate.
#include <stdint.h>
#include <stddef.h>
@@ -79,14 +79,14 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
static void fill_patterns(void) {
@@ -100,7 +100,7 @@ static void checked_write(uint32_t file, const void* buffer, size_t size, const
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -109,7 +109,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
@@ -120,7 +120,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s);
print("pcache_invalidate: using ");
print(path ? path : "<unknown>");
@@ -129,7 +129,7 @@ int main(void) {
checked_write(file, gOriginal, sizeof(gOriginal), "write original");
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek fill", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek fill", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS afterFill;
@@ -137,23 +137,24 @@ int main(void) {
SYS_FILE_CACHE_STATS afterWrite;
SYS_FILE_CACHE_STATS afterRewriteRead;
SYS_FILE_CACHE_STATS afterTruncate;
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != NTLSTATUS_SUCCESS) fail("initial stats query failed");
size_t got = 0;
checked_read(file, gReadback, sizeof(gReadback), &got, "first read");
if (got != sizeof(gOriginal)) fail("first read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("first read data mismatch");
if (query_cache(&afterFill) != STATUS_SUCCESS) fail("fill stats query failed");
if (afterFill.Loads <= before.Loads) fail("initial read did not populate cache");
if (query_cache(&afterFill) != NTLSTATUS_SUCCESS) fail("fill stats query failed");
if (afterFill.Loads <= before.Loads && afterFill.Hits <= before.Hits)
fail("initial read did not touch cache");
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek second", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek second", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "second read");
if (got != sizeof(gOriginal)) fail("second read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("second read data mismatch");
if (query_cache(&afterSecond) != STATUS_SUCCESS) fail("second stats query failed");
if (query_cache(&afterSecond) != NTLSTATUS_SUCCESS) fail("second stats query failed");
// Do not require this second read to hit: the cache is intentionally
// bounded and pcache_pressure.exe may have left it at the soft limit, so
// the newly-populated temp pages are eligible for immediate pressure
@@ -161,20 +162,20 @@ int main(void) {
// is covered by pcache_mmap_read_reuse.exe.
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek rewrite", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek rewrite", s);
checked_write(file, gReplacement, sizeof(gReplacement), "rewrite first page");
if (query_cache(&afterWrite) != STATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.Invalidations <= afterSecond.Invalidations) fail("write did not invalidate cached page");
if (query_cache(&afterWrite) != NTLSTATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.DirtyMarks <= afterSecond.DirtyMarks) fail("rewrite did not dirty cached page");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gReplacement), &mapping);
if (s != STATUS_SUCCESS) fail_status("create mapping after rewrite", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create mapping after rewrite", s);
void* mappedBase = 0;
s = SysMapFile(mapping, 0, sizeof(gReplacement), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &mappedBase);
SysCloseHandle(mapping);
if (s != STATUS_SUCCESS) fail_status("map after rewrite", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map after rewrite", s);
if (!mappedBase) fail("map after rewrite returned null");
volatile uint8_t* mappedView = (volatile uint8_t*)mappedBase;
@@ -185,30 +186,30 @@ int main(void) {
fail("stale mmap data after write invalidation");
}
s = SysFreeVm(mappedBase);
if (s != STATUS_SUCCESS) fail_status("unmap after rewrite", s);
if (s != NTLSTATUS_SUCCESS) fail_status("unmap after rewrite", s);
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek verify rewrite", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek verify rewrite", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read after rewrite");
if (got != sizeof(gOriginal)) fail("rewrite read size mismatch");
if (memcmp(gReadback, gReplacement, sizeof(gReplacement)) != 0) fail("stale data after write invalidation");
if (memcmp(gReadback + sizeof(gReplacement), gOriginal + sizeof(gReplacement),
sizeof(gOriginal) - sizeof(gReplacement)) != 0) fail("unwritten page changed after rewrite");
if (query_cache(&afterRewriteRead) != STATUS_SUCCESS) fail("rewrite-read stats query failed");
if (query_cache(&afterRewriteRead) != NTLSTATUS_SUCCESS) fail("rewrite-read stats query failed");
s = SysTruncateFile(file, TRUNC_SIZE);
if (s != STATUS_SUCCESS) fail_status("truncate", s);
if (s != NTLSTATUS_SUCCESS) fail_status("truncate", s);
uint64_t truncatedSize = 0;
s = SysGetFileSize(file, &truncatedSize);
if (s != STATUS_SUCCESS) fail_status("get truncated size", s);
if (s != NTLSTATUS_SUCCESS) fail_status("get truncated size", s);
if (truncatedSize != TRUNC_SIZE) fail("truncate size mismatch");
if (query_cache(&afterTruncate) != STATUS_SUCCESS) fail("truncate stats query failed");
if (query_cache(&afterTruncate) != NTLSTATUS_SUCCESS) fail("truncate stats query failed");
if (afterTruncate.Invalidations <= afterRewriteRead.Invalidations) fail("truncate did not invalidate cached tail");
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek truncated", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek truncated", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read truncated");
if (got != TRUNC_SIZE) fail("truncated read length mismatch");
if (memcmp(gReadback, gReplacement, TRUNC_SIZE) != 0) fail("truncated data mismatch");
@@ -78,13 +78,13 @@ static uint32_t open_test_file(uint32_t* out, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*out = h;
if (pathOut) *pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
int main(void) {
@@ -94,16 +94,16 @@ int main(void) {
SYS_FILE_CACHE_STATS afterFault;
SYS_FILE_CACHE_STATS afterRead;
uint32_t s = query_cache(&before);
if (s != STATUS_SUCCESS) fail_status("query initial stats", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
const char* path = 0;
s = open_test_file(&file, &path);
if (s != STATUS_SUCCESS) fail_status("open test file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("open test file", s);
uint64_t fileSize = 0;
s = SysGetFileSize(file, &fileSize);
if (s != STATUS_SUCCESS) fail_status("get file size", s);
if (s != NTLSTATUS_SUCCESS) fail_status("get file size", s);
if (fileSize < 4096) fail("test file too small");
size_t mapSize = (fileSize < TEST_SIZE) ? (size_t)fileSize : TEST_SIZE;
@@ -115,12 +115,12 @@ int main(void) {
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, mapSize, &mapping);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create mapping", s);
void* base = 0;
s = SysMapFile(mapping, 0, mapSize, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != STATUS_SUCCESS) fail_status("map file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
@@ -129,14 +129,14 @@ int main(void) {
(void)view[mapSize - 1];
s = query_cache(&afterFault);
if (s != STATUS_SUCCESS) fail_status("query after mmap faults", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query after mmap faults", s);
if (afterFault.Loads <= before.Loads) {
SysFreeVm(base);
fail("mmap faults did not populate file cache");
}
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
SysFreeVm(base);
fail_status("seek", s);
}
@@ -145,7 +145,7 @@ int main(void) {
memset(gReadback, 0xCC, sizeof(gReadback));
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gReadback, mapSize, &got);
s = SysReadFile(&readReq);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
SysFreeVm(base);
fail_status("read", s);
}
@@ -159,7 +159,7 @@ int main(void) {
}
s = query_cache(&afterRead);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
SysFreeVm(base);
fail_status("query after read", s);
}
@@ -87,14 +87,14 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
static void fill_pattern(void) {
@@ -107,7 +107,7 @@ static uint32_t validate_mapping(const uint8_t* base) {
if (base[i] != gPattern[i])
return 0x10000000u | (uint32_t)(i & 0x0FFFFFFFu);
}
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
static void thread_main(uintptr_t arg) {
@@ -118,8 +118,8 @@ static void thread_main(uintptr_t arg) {
void* base = 0;
uint32_t s = SysMapFile(ctx->Mapping, 0, TEST_SIZE,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
if (s != STATUS_SUCCESS || !base) {
ctx->Status = s ? s : STATUS_INVALID_PARAMETER;
if (s != NTLSTATUS_SUCCESS || !base) {
ctx->Status = s ? s : NTLSTATUS_INVALID_PARAMETER;
SysExitThread(1);
}
@@ -130,18 +130,18 @@ static void thread_main(uintptr_t arg) {
s = validate_mapping((const uint8_t*)base);
uint32_t fs = SysFreeVm(base);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
ctx->Status = s;
SysExitThread(1);
}
if (fs != STATUS_SUCCESS) {
if (fs != NTLSTATUS_SUCCESS) {
ctx->Status = fs;
SysExitThread(1);
}
ctx->IterationsDone++;
}
ctx->Status = STATUS_SUCCESS;
ctx->Status = NTLSTATUS_SUCCESS;
SysExitThread(0);
}
@@ -152,21 +152,21 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s);
size_t written = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &written);
s = SysWriteFile(&writeReq);
if (s != STATUS_SUCCESS) fail_status("write pattern", s);
if (s != NTLSTATUS_SUCCESS) fail_status("write pattern", s);
if (written != sizeof(gPattern)) fail("short write");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create mapping", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != NTLSTATUS_SUCCESS) fail("initial stats query failed");
uint32_t handles[THREAD_COUNT];
uint32_t tids[THREAD_COUNT];
@@ -177,7 +177,7 @@ int main(void) {
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
gThreads[i].Index = i;
gThreads[i].Mapping = mapping;
gThreads[i].Status = STATUS_PENDING;
gThreads[i].Status = NTLSTATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)thread_main,
(uintptr_t)&gThreads[i],
@@ -187,17 +187,17 @@ int main(void) {
&tids[i]
};
s = SysCreateThread(&req);
if (s != STATUS_SUCCESS) fail_status("create thread", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait thread", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gThreads[i].Status != STATUS_SUCCESS) {
if (gThreads[i].Status != NTLSTATUS_SUCCESS) {
print("pcache_multithread: worker ");
print_u64(i);
print(" status=");
@@ -210,8 +210,9 @@ int main(void) {
if (gThreads[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch");
}
if (query_cache(&after) != STATUS_SUCCESS) fail("final stats query failed");
if (after.Loads <= before.Loads) fail("concurrent mmap did not load cache pages");
if (query_cache(&after) != NTLSTATUS_SUCCESS) fail("final stats query failed");
if (after.Loads <= before.Loads && after.Hits <= before.Hits)
fail("concurrent mmap did not touch cache pages");
if (after.Hits <= before.Hits) fail("concurrent mmap did not reuse cache pages");
SysCloseHandle(mapping);
@@ -77,12 +77,12 @@ static uint32_t open_first_large(uint32_t* fileOut, const char** pathOut, uint64
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
continue;
uint64_t size = 0;
s = SysGetFileSize(h, &size);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
SysCloseHandle(h);
continue;
}
@@ -90,13 +90,13 @@ static uint32_t open_first_large(uint32_t* fileOut, const char** pathOut, uint64
*fileOut = h;
*pathOut = candidates[i];
*sizeOut = size;
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
SysCloseHandle(h);
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
int main(void) {
@@ -104,14 +104,14 @@ int main(void) {
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != STATUS_SUCCESS)
if (query_cache(&before) != NTLSTATUS_SUCCESS)
fail("initial cache stats query failed");
uint32_t file = 0;
const char* path = 0;
uint64_t size = 0;
uint32_t s = open_first_large(&file, &path, &size);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("open large asset", s);
print("pcache_pressure: using ");
@@ -126,7 +126,7 @@ int main(void) {
size_t got = 0;
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gBuf, sizeof(gBuf), &got);
s = SysReadFile(&readReq);
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("read", s);
if (got == 0)
break;
@@ -138,7 +138,7 @@ int main(void) {
if (total < MIN_PRESSURE_BYTES)
fail("asset read was too short to pressure the cache");
if (query_cache(&after) != STATUS_SUCCESS)
if (query_cache(&after) != NTLSTATUS_SUCCESS)
fail("final cache stats query failed");
uint64_t loads = after.Loads - before.Loads;
+13 -13
View File
@@ -72,13 +72,13 @@ static uint32_t open_self(uint32_t* out) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*out = h;
print("pcache_smoke: using "); print(paths[i]); print("\n");
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
int main(void) {
@@ -86,52 +86,52 @@ int main(void) {
SYS_FILE_CACHE_STATS before, afterFirst, afterSecond, afterMap;
uint32_t s = query_cache(&before);
if (s != STATUS_SUCCESS) fail_status("query initial stats", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
s = open_self(&file);
if (s != STATUS_SUCCESS) fail_status("open test file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("open test file", s);
size_t gotA = 0;
memset(gA, 0xA5, sizeof(gA));
SYS_FILE_IO_REQUEST readReqA = SysMakeFileIoRequest(file, gA, sizeof(gA), &gotA);
s = SysReadFile(&readReqA);
if (s != STATUS_SUCCESS) fail_status("first read", s);
if (s != NTLSTATUS_SUCCESS) fail_status("first read", s);
if (gotA < 4096) fail("first read too short");
print("pcache_smoke: first read OK\n");
s = query_cache(&afterFirst);
if (s != STATUS_SUCCESS) fail_status("query after first read", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query after first read", s);
if (afterFirst.Loads <= before.Loads)
fail("first read did not load file-cache pages");
s = SysSeekFile(file, 0);
if (s != STATUS_SUCCESS) fail_status("seek", s);
if (s != NTLSTATUS_SUCCESS) fail_status("seek", s);
size_t gotB = 0;
memset(gB, 0x5A, sizeof(gB));
SYS_FILE_IO_REQUEST readReqB = SysMakeFileIoRequest(file, gB, sizeof(gB), &gotB);
s = SysReadFile(&readReqB);
if (s != STATUS_SUCCESS) fail_status("second read", s);
if (s != NTLSTATUS_SUCCESS) fail_status("second read", s);
if (gotB != gotA) fail("second read length mismatch");
if (memcmp(gA, gB, gotA) != 0) fail("second read data mismatch");
print("pcache_smoke: second read OK\n");
s = query_cache(&afterSecond);
if (s != STATUS_SUCCESS) fail_status("query after second read", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query after second read", s);
if (afterSecond.Hits <= afterFirst.Hits)
fail("second read did not hit file cache");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gA), &mapping);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create mapping", s);
SysCloseHandle(file);
file = 0;
void* base = 0;
s = SysMapFile(mapping, 0, sizeof(gA), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != STATUS_SUCCESS) fail_status("map file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
@@ -145,7 +145,7 @@ int main(void) {
}
s = query_cache(&afterMap);
if (s != STATUS_SUCCESS) fail_status("query after map", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query after map", s);
if (afterMap.Hits <= afterSecond.Hits)
fail("mmap faults did not hit already cached pages");
@@ -54,18 +54,18 @@ static void fail_status(const char* operation, uint32_t status) {
static void verify_protected_process(const SYS_PROCESS_INFO* process) {
uint32_t handle = 0;
uint32_t status = SysOpenProcess(process->ProcessId, &handle);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("open protected process", status);
status = SysKillProcess(handle, (int32_t)STATUS_PROCESS_KILLED);
if (status != STATUS_ACCESS_DENIED) {
status = SysKillProcess(handle, (int32_t)NTLSTATUS_PROCESS_KILLED);
if (status != NTLSTATUS_ACCESS_DENIED) {
SysCloseHandle(handle);
fail_status("kill protected process", status);
}
status = SysWaitSingle(handle, SYS_WAIT_FLAG_NONE, 0);
SysCloseHandle(handle);
if (status != STATUS_TIMEOUT)
if (status != NTLSTATUS_TIMEOUT)
fail_status("protected process remained signaled", status);
}
@@ -76,7 +76,7 @@ void main(void) {
SYS_PROCESS_INFO processes[MAX_PROCESSES];
size_t count = 0;
uint32_t status = SysGetProcessList(processes, MAX_PROCESSES, &count);
if (status != STATUS_SUCCESS)
if (status != NTLSTATUS_SUCCESS)
fail_status("query process list", status);
int sawIdle = 0;
@@ -75,7 +75,7 @@ static void get_current_drive_root(char* out, size_t cap) {
&required,
};
uint32_t status = SysRealPath(&req);
if (status != STATUS_SUCCESS) fail_status("query current path", status);
if (status != NTLSTATUS_SUCCESS) fail_status("query current path", status);
if (required < 4 || current[0] < '0' || current[0] > '7' || current[1] != ':' || current[2] != '/')
fail("current path is not native drive-rooted");
if (cap < 4) fail("drive root output too small");
@@ -98,7 +98,7 @@ static void expect_realpath(const char* input, const char* expected) {
&required,
};
uint32_t status = SysRealPath(&req);
if (status != STATUS_SUCCESS) fail_status("realpath", status);
if (status != NTLSTATUS_SUCCESS) fail_status("realpath", status);
if (required != strlen(expected) + 1) fail("required size mismatch");
if (strcmp(out, expected) != 0) fail("resolved path mismatch");
}
@@ -115,7 +115,7 @@ static void check_size_reporting(const char* input, const char* expected) {
&required,
};
uint32_t status = SysRealPath(&query);
if (status != STATUS_SUCCESS) fail_status("size query", status);
if (status != NTLSTATUS_SUCCESS) fail_status("size query", status);
if (required != strlen(expected) + 1) fail("size query required mismatch");
char small[4];
@@ -129,7 +129,7 @@ static void check_size_reporting(const char* input, const char* expected) {
&smallRequired,
};
status = SysRealPath(&smallReq);
if (status != STATUS_BUFFER_TOO_SMALL) fail_status("small buffer", status);
if (status != NTLSTATUS_BUFFER_TOO_SMALL) fail_status("small buffer", status);
if (smallRequired != required) fail("small required mismatch");
}
@@ -165,7 +165,7 @@ int main(void) {
(size_t*)0,
};
uint32_t status = SysRealPath(&currentReq);
if (status != STATUS_SUCCESS) fail_status("current realpath", status);
if (status != NTLSTATUS_SUCCESS) fail_status("current realpath", status);
memcpy(input, "missing_realpath_dir/../missing_file", sizeof("missing_realpath_dir/../missing_file"));
append(expected, sizeof(expected), expected[strlen(expected) - 1] == '/' ? "missing_file" : "/missing_file");
+19 -19
View File
@@ -79,13 +79,13 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return STATUS_SUCCESS;
return NTLSTATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
return NTLSTATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
@@ -96,21 +96,21 @@ static uint32_t open_path(const char* path, uint32_t* fileOut) {
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS)
if (s == NTLSTATUS_SUCCESS)
*fileOut = h;
return s;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
}
static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) {
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -119,7 +119,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (s != NTLSTATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
@@ -137,7 +137,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s);
print("shared_mmap: creating temporary file ");
print(path ? path : "<unknown>");
@@ -145,16 +145,16 @@ int main(void) {
checked_write(file, gInitial, TEST_SIZE, "initial write");
s = SysFlushFile(file);
if (s != STATUS_SUCCESS) fail_status("initial flush", s);
if (s != NTLSTATUS_SUCCESS) fail_status("initial flush", s);
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READWRITE, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create mapping", s);
uint8_t* view = 0;
s = SysMapFile(mapping, 0, TEST_SIZE, SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_WRITE,
SYS_MAP_FLAG_NONE, (void**)&view);
if (s != STATUS_SUCCESS) fail_status("map shared write", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map shared write", s);
for (size_t i = 128; i < 128 + 3000; ++i) {
view[i] = (uint8_t)(0xA0u + (uint8_t)i);
@@ -167,12 +167,12 @@ int main(void) {
SYS_FILE_CACHE_STATS beforeFlush;
SYS_FILE_CACHE_STATS afterFlush;
if (query_cache(&beforeFlush) != STATUS_SUCCESS) fail("pre-flush stats failed");
if (query_cache(&beforeFlush) != NTLSTATUS_SUCCESS) fail("pre-flush stats failed");
s = SysFlushFile(file);
if (s != STATUS_SUCCESS) fail_status("flush mapped writes", s);
if (s != NTLSTATUS_SUCCESS) fail_status("flush mapped writes", s);
if (query_cache(&afterFlush) != STATUS_SUCCESS) fail("post-flush stats failed");
if (query_cache(&afterFlush) != NTLSTATUS_SUCCESS) fail("post-flush stats failed");
if (afterFlush.MappedDirtyPages <= beforeFlush.MappedDirtyPages)
fail("mapped dirty pages were not harvested");
if (afterFlush.WritebackBytes <= beforeFlush.WritebackBytes)
@@ -191,18 +191,18 @@ int main(void) {
s = SysFreeVm(view);
view = 0;
if (s != STATUS_SUCCESS) fail_status("unmap", s);
if (s != NTLSTATUS_SUCCESS) fail_status("unmap", s);
s = SysCloseHandle(mapping);
mapping = 0;
if (s != STATUS_SUCCESS) fail_status("close mapping", s);
if (s != NTLSTATUS_SUCCESS) fail_status("close mapping", s);
s = SysCloseHandle(file);
file = 0;
if (s != STATUS_SUCCESS) fail_status("close file", s);
if (s != NTLSTATUS_SUCCESS) fail_status("close file", s);
s = open_path(path, &file);
if (s != STATUS_SUCCESS) fail_status("reopen", s);
if (s != NTLSTATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, TEST_SIZE, &got, "read after close");
if (got != TEST_SIZE) fail("read after close size mismatch");
@@ -210,7 +210,7 @@ int main(void) {
s = SysCloseHandle(file);
file = 0;
if (s != STATUS_SUCCESS) fail_status("close reopened", s);
if (s != NTLSTATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("shared_mmap: mapped-dirty-pages+=");
@@ -73,7 +73,7 @@ static void check_ipv4_resolve(void) {
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status != STATUS_SUCCESS) fail_status("resolve ipv4", status);
if (status != NTLSTATUS_SUCCESS) fail_status("resolve ipv4", status);
if (addressSize != sizeof(address)) fail("resolve ipv4 size mismatch");
if (address.Family != SYS_AF_INET || address.Size != sizeof(address)) fail("resolve ipv4 tag mismatch");
if (address.Address[0] != 127 || address.Address[1] != 0 || address.Address[2] != 0 || address.Address[3] != 1)
@@ -93,7 +93,7 @@ static void check_ipv6_resolve(void) {
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status != STATUS_SUCCESS) fail_status("resolve ipv6", status);
if (status != NTLSTATUS_SUCCESS) fail_status("resolve ipv6", status);
if (addressSize != sizeof(address)) fail("resolve ipv6 size mismatch");
if (address.Family != SYS_AF_INET6 || address.Size != sizeof(address)) fail("resolve ipv6 tag mismatch");
for (int i = 0; i < 15; i++) {
@@ -107,10 +107,10 @@ static void check_socket_address_validation(void) {
uint32_t ipv4 = 0;
uint32_t ipv6 = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv4);
if (status != STATUS_SUCCESS) fail_status("create ipv4 udp", status);
if (status != NTLSTATUS_SUCCESS) fail_status("create ipv4 udp", status);
status = SysCreateSocket(SYS_AF_INET6, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv6);
if (status != STATUS_SUCCESS) fail_status("create ipv6 udp", status);
if (status != NTLSTATUS_SUCCESS) fail_status("create ipv6 udp", status);
SYS_SOCKET_ADDRESS ipv4Address;
memset(&ipv4Address, 0, sizeof(ipv4Address));
@@ -128,10 +128,16 @@ static void check_socket_address_validation(void) {
SYS_SOCKET_ADDRESS badSize = ipv6Address;
badSize.Size = sizeof(badSize) - 1;
status = SysBindSocket(ipv6, &badSize, sizeof(badSize));
if (status == STATUS_SUCCESS) fail("bind accepted wrong ipv6 size");
if (status != NTLSTATUS_INVALID_PARAMETER) fail_status("bind wrong struct size", status);
status = SysBindSocket(ipv6, &ipv6Address, sizeof(ipv6Address) - 1);
if (status != NTLSTATUS_BUFFER_TOO_SMALL) fail_status("bind short buffer size", status);
status = SysBindSocket(ipv6, &ipv6Address, sizeof(ipv6Address) + 1);
if (status != NTLSTATUS_INVALID_PARAMETER) fail_status("bind oversized buffer size", status);
status = SysConnectSocket(ipv4, &ipv6Address, sizeof(ipv6Address));
if (status == STATUS_SUCCESS) fail("connect accepted wrong address family");
if (status == NTLSTATUS_SUCCESS) fail("connect accepted wrong address family");
SysCloseHandle(ipv4);
SysCloseHandle(ipv6);
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
@@ -70,50 +71,63 @@ static void expect_status(const char* what, uint32_t got, uint32_t expected) {
static void check_fixed_stats(void) {
size_t required = 0;
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, 0, 0, &required);
if (status != STATUS_SUCCESS) fail_status("scheduler size", status);
if (status != NTLSTATUS_SUCCESS) fail_status("scheduler size", status);
if (required != sizeof(SYS_SCHEDULER_STATS)) fail("scheduler required size mismatch");
uint8_t small[sizeof(SYS_SCHEDULER_STATS) - 1u];
size_t smallRequired = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, small, sizeof(small), &smallRequired);
expect_status("scheduler small buffer", status, STATUS_BUFFER_TOO_SMALL);
expect_status("scheduler small buffer", status, NTLSTATUS_BUFFER_TOO_SMALL);
if (smallRequired != sizeof(SYS_SCHEDULER_STATS)) fail("scheduler small required mismatch");
SYS_SCHEDULER_STATS scheduler;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &scheduler, sizeof(scheduler), 0);
if (status != STATUS_SUCCESS) fail_status("scheduler exact", status);
if (status != NTLSTATUS_SUCCESS) fail_status("scheduler exact", status);
if (scheduler.Size != sizeof(scheduler)) fail("scheduler size field mismatch");
if (scheduler.Version != SYS_STATS_VERSION) fail("scheduler version mismatch");
SYS_FILESYSTEM_STATS filesystem;
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILESYSTEM, &filesystem, sizeof(filesystem), 0);
if (status != STATUS_SUCCESS) fail_status("filesystem exact", status);
if (status != NTLSTATUS_SUCCESS) fail_status("filesystem exact", status);
if (filesystem.Size != sizeof(filesystem)) fail("filesystem size field mismatch");
if (filesystem.Version != SYS_STATS_VERSION) fail("filesystem version mismatch");
SYS_EXCEPTION_STATS exceptionStats;
status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, &exceptionStats, sizeof(exceptionStats), 0);
if (status != NTLSTATUS_SUCCESS) fail_status("exception exact", status);
if (exceptionStats.Size != sizeof(exceptionStats)) fail("exception size field mismatch");
if (exceptionStats.Version != SYS_STATS_VERSION) fail("exception version mismatch");
}
// Verifies CPU stats use a size-reported trailing-record layout.
static void check_cpu_stats(void) {
size_t required = 0;
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &required);
if (status != STATUS_SUCCESS) fail_status("cpu size", status);
if (status != NTLSTATUS_SUCCESS) fail_status("cpu size", status);
if (required < sizeof(SYS_CPU_STATS) + sizeof(SYS_CPU_STAT)) fail("cpu required size too small");
SYS_CPU_STATS headerOnly;
size_t smallRequired = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, &headerOnly, sizeof(headerOnly), &smallRequired);
expect_status("cpu header-only buffer", status, STATUS_BUFFER_TOO_SMALL);
expect_status("cpu header-only buffer", status, NTLSTATUS_BUFFER_TOO_SMALL);
if (smallRequired != required) fail("cpu small required mismatch");
void* buffer = 0;
size_t pages = (required + 4095u) / 4096u;
if (pages == 0) pages = 1;
status = SysAllocVm(0, pages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &buffer);
if (status != STATUS_SUCCESS || !buffer) fail_status("cpu buffer alloc", status);
SYS_ALLOC_VM_REQUEST allocReq;
memset(&allocReq, 0, sizeof(allocReq));
allocReq.Size = sizeof(allocReq);
allocReq.PageCount = pages;
allocReq.Protect = SYS_VM_READWRITE;
allocReq.Flags = SYS_VM_FLAG_NONE;
allocReq.BaseOut = &buffer;
status = SysAllocVm(&allocReq);
if (status != NTLSTATUS_SUCCESS || !buffer) fail_status("cpu buffer alloc", status);
SYS_CPU_STATS* cpu = (SYS_CPU_STATS*)buffer;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, cpu, pages * 4096u, 0);
if (status != STATUS_SUCCESS) {
if (status != NTLSTATUS_SUCCESS) {
SysFreeVm(buffer);
fail_status("cpu exact", status);
}
@@ -65,38 +65,49 @@ int main(void) {
print("surface_invalid_rect: start\n");
uint32_t bad = 0;
uint32_t s = SysCreateSurface(0, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("zero width", s, STATUS_INVALID_PARAMETER);
SYS_CREATE_SURFACE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Width = 0;
req.Height = SURFACE_H;
req.Format = SYS_SURFACE_FORMAT_XRGB8888;
req.Flags = SYS_SURFACE_FLAG_NONE;
req.HandleOut = &bad;
uint32_t s = SysCreateSurface(&req);
expect_status("zero width", s, NTLSTATUS_INVALID_PARAMETER);
s = SysCreateSurface(SURFACE_W, SURFACE_H, 0x9999u, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("bad format", s, STATUS_NOT_SUPPORTED);
req.Width = SURFACE_W;
req.Format = 0x9999u;
s = SysCreateSurface(&req);
expect_status("bad format", s, NTLSTATUS_NOT_SUPPORTED);
uint32_t surface = 0;
s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
req.Format = SYS_SURFACE_FORMAT_XRGB8888;
req.HandleOut = &surface;
s = SysCreateSurface(&req);
if (s != NTLSTATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
s = SysPresentSurface(surface, 0, 1);
expect_status("null rect array with nonzero count", s, STATUS_INVALID_PARAMETER);
expect_status("null rect array with nonzero count", s, NTLSTATUS_INVALID_PARAMETER);
DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS + 1];
memset(rects, 0, sizeof(rects));
s = SysPresentSurface(surface, rects, SYS_SURFACE_MAX_RECTS + 1);
expect_status("too many rects", s, STATUS_INVALID_PARAMETER);
expect_status("too many rects", s, NTLSTATUS_INVALID_PARAMETER);
DISPLAY_RECT clipped = { -8, -8, 24, 24 };
s = SysPresentSurface(surface, &clipped, 1);
if (s != STATUS_SUCCESS) fail_status("present clipped rect", s);
if (s != NTLSTATUS_SUCCESS) fail_status("present clipped rect", s);
DISPLAY_RECT empty = { 10000, 10000, 16, 16 };
s = SysPresentSurface(surface, &empty, 1);
if (s != STATUS_SUCCESS) fail_status("present empty clipped rect", s);
if (s != NTLSTATUS_SUCCESS) fail_status("present empty clipped rect", s);
SysCloseHandle(surface);
print("surface_invalid_rect: PASS\n");
@@ -80,7 +80,7 @@ static void worker_main(uintptr_t arg) {
DISPLAY_RECT rect = { 0, (int32_t)y0, (int32_t)SURFACE_W, (int32_t)h };
uint32_t s = SysPresentSurface(st->Surface, &rect, 1);
if (s != STATUS_SUCCESS) {
if (s != NTLSTATUS_SUCCESS) {
st->Status = s;
SysExitThread(1);
}
@@ -88,7 +88,7 @@ static void worker_main(uintptr_t arg) {
SysYield();
}
st->Status = STATUS_SUCCESS;
st->Status = NTLSTATUS_SUCCESS;
SysExitThread(0);
}
@@ -96,14 +96,21 @@ int main(void) {
print("surface_multithread_present: start\n");
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
SYS_CREATE_SURFACE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Width = SURFACE_W;
req.Height = SURFACE_H;
req.Format = SYS_SURFACE_FORMAT_XRGB8888;
req.Flags = SYS_SURFACE_FLAG_NONE;
req.HandleOut = &surface;
uint32_t s = SysCreateSurface(&req);
if (s != NTLSTATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
uint32_t handles[THREAD_COUNT];
@@ -117,7 +124,7 @@ int main(void) {
gWorkers[i].Surface = surface;
gWorkers[i].Base = (uint8_t*)base;
gWorkers[i].Pitch = pitch;
gWorkers[i].Status = STATUS_PENDING;
gWorkers[i].Status = NTLSTATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)worker_main,
(uintptr_t)&gWorkers[i],
@@ -127,17 +134,17 @@ int main(void) {
&tids[i]
};
s = SysCreateThread(&req);
if (s != STATUS_SUCCESS) fail_status("create thread", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait thread", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gWorkers[i].Status != STATUS_SUCCESS) {
if (gWorkers[i].Status != NTLSTATUS_SUCCESS) {
print("surface_multithread_present: worker ");
print_u64(i);
print(" status=");
+14 -7
View File
@@ -53,21 +53,28 @@ int main(void) {
print("surface_smoke: start\n");
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
SYS_CREATE_SURFACE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Width = SURFACE_W;
req.Height = SURFACE_H;
req.Format = SYS_SURFACE_FORMAT_XRGB8888;
req.Flags = SYS_SURFACE_FLAG_NONE;
req.HandleOut = &surface;
uint32_t s = SysCreateSurface(&req);
if (s != NTLSTATUS_SUCCESS) fail_status("create surface", s);
if (!surface) fail("surface handle is zero");
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
SYS_SURFACE_DESC desc;
memset(&desc, 0, sizeof(desc));
s = SysQuerySurface(surface, &desc);
if (s != STATUS_SUCCESS) fail_status("query surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query surface", s);
if (desc.Width != SURFACE_W || desc.Height != SURFACE_H) fail("query dimensions mismatch");
if (desc.PitchBytes != pitch || desc.Format != SYS_SURFACE_FORMAT_XRGB8888) fail("query metadata mismatch");
if (desc.Base != base) fail("query base mismatch");
@@ -81,10 +88,10 @@ int main(void) {
DISPLAY_RECT rect = { 0, 0, (int32_t)SURFACE_W, (int32_t)SURFACE_H };
s = SysPresentSurface(surface, &rect, 1);
if (s != STATUS_SUCCESS) fail_status("present surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("present surface", s);
s = SysPresentSurface(surface, 0, 0);
if (s != STATUS_SUCCESS) fail_status("present full surface", s);
if (s != NTLSTATUS_SUCCESS) fail_status("present full surface", s);
SysCloseHandle(surface);
print("surface_smoke: PASS\n");
@@ -70,12 +70,12 @@ static void fail_status(const char* what, uint32_t status) {
static void check_tick_contract(void) {
uint64_t frequency = 0;
uint32_t s = SysGetTickFrequency(&frequency);
if (s != STATUS_SUCCESS) fail_status("query tick frequency", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query tick frequency", s);
if (!frequency) fail("tick frequency was zero");
uint64_t t0 = SysGetTick();
s = SysSleep(1);
if (s != STATUS_SUCCESS) fail_status("sleep", s);
if (s != NTLSTATUS_SUCCESS) fail_status("sleep", s);
uint64_t t1 = SysGetTick();
if (t1 < t0) fail("tick moved backwards across sleep");
@@ -96,14 +96,14 @@ static void check_exit_code_contract(void) {
};
uint32_t s = SysCreateProcess(&req);
if (s != STATUS_SUCCESS) fail_status("create child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create child", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait child", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query child exit code", s);
if (s != NTLSTATUS_SUCCESS) fail_status("query child exit code", s);
if (exitCode != CHILD_EXIT_CODE) fail("child exit code mismatch");
SysCloseHandle(child);
+31 -31
View File
@@ -72,14 +72,14 @@ static uint32_t set_timer(uint32_t timer, uint64_t dueMs, uint64_t periodMs) {
// Verifies timer request validation paths.
static void test_validation(void) {
uint32_t timer = 0;
expect_status("create timer null", SysCreateTimer((uint32_t*)0), STATUS_INVALID_PARAMETER);
expect_status("create timer null", SysCreateTimer((uint32_t*)0), NTLSTATUS_INVALID_PARAMETER);
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create validation timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create validation timer", s);
expect_status("set timer null", SysSetTimer((const SYS_SET_TIMER_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("cancel invalid", SysCancelTimer(0xdeadbeefu), STATUS_INVALID_HANDLE);
expect_status("wait invalid", SysWaitSingle(0xdeadbeefu, SYS_WAIT_FLAG_NONE, 0), STATUS_INVALID_HANDLE);
expect_status("set timer null", SysSetTimer((const SYS_SET_TIMER_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("cancel invalid", SysCancelTimer(0xdeadbeefu), NTLSTATUS_INVALID_HANDLE);
expect_status("wait invalid", SysWaitSingle(0xdeadbeefu, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_INVALID_HANDLE);
SYS_SET_TIMER_REQUEST req = {
0,
@@ -88,11 +88,11 @@ static void test_validation(void) {
1,
0,
};
expect_status("set bad size", SysSetTimer(&req), STATUS_INVALID_PARAMETER);
expect_status("set bad size", SysSetTimer(&req), NTLSTATUS_INVALID_PARAMETER);
req.Size = sizeof(req);
req.Flags = 1;
expect_status("set bad flags", SysSetTimer(&req), STATUS_INVALID_PARAMETER);
expect_status("set bad flags", SysSetTimer(&req), NTLSTATUS_INVALID_PARAMETER);
SysCloseHandle(timer);
}
@@ -101,16 +101,16 @@ static void test_validation(void) {
static void test_one_shot(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create one-shot timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create one-shot timer", s);
expect_status("unset immediate wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
expect_status("unset immediate wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_TIMEOUT);
s = set_timer(timer, 20, 0);
if (s != STATUS_SUCCESS) fail_status("set one-shot", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set one-shot", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, SYS_WAIT_TIMEOUT_INFINITE);
if (s != STATUS_SUCCESS) fail_status("wait one-shot", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait one-shot", s);
expect_status("one-shot consumed", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
expect_status("one-shot consumed", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_TIMEOUT);
SysCloseHandle(timer);
}
@@ -118,24 +118,24 @@ static void test_one_shot(void) {
static void test_cancel(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create cancel timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create cancel timer", s);
s = set_timer(timer, 100, 0);
if (s != STATUS_SUCCESS) fail_status("set cancel timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set cancel timer", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("cancel timer", s);
expect_status("cancelled timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), STATUS_TIMEOUT);
expect_status("cancelled timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), NTLSTATUS_TIMEOUT);
s = set_timer(timer, 0, 0);
if (s != STATUS_SUCCESS) fail_status("set immediate", s);
expect_status("immediate timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_SUCCESS);
if (s != NTLSTATUS_SUCCESS) fail_status("set immediate", s);
expect_status("immediate timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_SUCCESS);
s = set_timer(timer, 0, 0);
if (s != STATUS_SUCCESS) fail_status("set immediate before cancel", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set immediate before cancel", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel signaled", s);
expect_status("cancel clears signal", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
if (s != NTLSTATUS_SUCCESS) fail_status("cancel signaled", s);
expect_status("cancel clears signal", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_TIMEOUT);
SysCloseHandle(timer);
}
@@ -144,15 +144,15 @@ static void test_cancel(void) {
static void test_reset_changes_deadline(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create reset timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create reset timer", s);
s = set_timer(timer, 100, 0);
if (s != STATUS_SUCCESS) fail_status("set reset timer long", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set reset timer long", s);
s = set_timer(timer, 20, 0);
if (s != STATUS_SUCCESS) fail_status("set reset timer short", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set reset timer short", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait reset timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait reset timer", s);
SysCloseHandle(timer);
}
@@ -161,18 +161,18 @@ static void test_reset_changes_deadline(void) {
static void test_periodic(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create periodic timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create periodic timer", s);
s = set_timer(timer, 10, 20);
if (s != STATUS_SUCCESS) fail_status("set periodic timer", s);
if (s != NTLSTATUS_SUCCESS) fail_status("set periodic timer", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait periodic first", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait periodic first", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait periodic second", s);
if (s != NTLSTATUS_SUCCESS) fail_status("wait periodic second", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel periodic", s);
expect_status("periodic cancelled wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), STATUS_TIMEOUT);
if (s != NTLSTATUS_SUCCESS) fail_status("cancel periodic", s);
expect_status("periodic cancelled wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), NTLSTATUS_TIMEOUT);
SysCloseHandle(timer);
}
@@ -0,0 +1,186 @@
// Win32 vectored exception smoke test. This file verifies Win32 VEH policy over the native exception runtime.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
static volatile uint32_t gFirstHits;
static volatile uint32_t gTailHits;
static volatile uint32_t gContinueHits;
static volatile uint32_t gOnlyHits;
static volatile uint32_t gRaisedHits;
// 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("win32_exception_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Fails the test with one integer value.
static void fail_value(const char* message, uint64_t value) {
print("win32_exception_smoke: ");
print(message);
print(" value=");
print_u64(value);
print("\n");
exit_now(1);
}
// Verifies the Win32 exception pointers contain a breakpoint and editable context.
static void verify_breakpoint(EXCEPTION_POINTERS* info) {
if (!info || !info->ExceptionRecord || !info->ContextRecord)
fail("handler received null exception pointers");
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT)
fail_value("handler received unexpected exception code", info->ExceptionRecord->ExceptionCode);
if (!info->ExceptionRecord->ExceptionAddress)
fail("handler received null exception address");
if (!info->ContextRecord->Rip || !info->ContextRecord->Rsp)
fail("handler received empty context");
}
// Handles the first breakpoint before later exception handlers can observe it.
static LONG first_continue_handler(EXCEPTION_POINTERS* info) {
verify_breakpoint(info);
gFirstHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Records an exception pass and asks Win32 policy to keep searching.
static LONG tail_search_handler(EXCEPTION_POINTERS* info) {
verify_breakpoint(info);
gTailHits++;
return EXCEPTION_CONTINUE_SEARCH;
}
// Continues a breakpoint after the exception-handler list declines it.
static LONG continue_handler(EXCEPTION_POINTERS* info) {
verify_breakpoint(info);
gContinueHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Handles a breakpoint after earlier handlers have been removed.
static LONG only_continue_handler(EXCEPTION_POINTERS* info) {
verify_breakpoint(info);
gOnlyHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Handles a user-raised Win32 exception and verifies parameter translation.
static LONG raised_continue_handler(EXCEPTION_POINTERS* info) {
if (!info || !info->ExceptionRecord || !info->ContextRecord)
fail("raised handler received null exception pointers");
if (info->ExceptionRecord->ExceptionCode != 0xE1111002u)
fail_value("raised handler received unexpected exception code", info->ExceptionRecord->ExceptionCode);
if (info->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
fail("raised handler received unexpected noncontinuable flag");
if (info->ExceptionRecord->NumberParameters != 2)
fail_value("raised handler received wrong parameter count", info->ExceptionRecord->NumberParameters);
if (info->ExceptionRecord->ExceptionInformation[0] != 0x1122u ||
info->ExceptionRecord->ExceptionInformation[1] != 0x3344u)
fail("raised handler received wrong parameters");
gRaisedHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Verifies one successful remove operation.
static void expect_remove(const char* what, unsigned long removed) {
if (!removed)
fail(what);
}
// Runs the Win32 vectored exception smoke test.
int main(void) {
print("win32_exception_smoke: start\n");
if (AddVectoredExceptionHandler(0, 0))
fail("null exception handler was accepted");
if (GetLastError() != 87)
fail_value("null handler did not set ERROR_INVALID_PARAMETER", GetLastError());
void* tail = AddVectoredExceptionHandler(0, tail_search_handler);
void* first = AddVectoredExceptionHandler(1, first_continue_handler);
if (!tail || !first)
fail("exception handler registration failed");
__debugbreak();
if (gFirstHits != 1 || gTailHits != 0)
fail("first vectored handler did not run before tail handler");
expect_remove("remove first failed", RemoveVectoredExceptionHandler(first));
void* cont = AddVectoredContinueHandler(0, continue_handler);
if (!cont)
fail("continue handler registration failed");
__debugbreak();
if (gTailHits != 1 || gContinueHits != 1)
fail("continue handler did not run after exception search");
expect_remove("remove tail failed", RemoveVectoredExceptionHandler(tail));
expect_remove("remove continue failed", RemoveVectoredContinueHandler(cont));
if (RemoveVectoredContinueHandler(cont))
fail("second continue-handler removal succeeded unexpectedly");
if (GetLastError() != 6)
fail_value("second removal did not set ERROR_INVALID_HANDLE", GetLastError());
void* only = AddVectoredExceptionHandler(0, only_continue_handler);
if (!only)
fail("single handler registration failed");
__debugbreak();
if (gOnlyHits != 1)
fail("single vectored exception handler did not continue execution");
expect_remove("remove single handler failed", RemoveVectoredExceptionHandler(only));
void* raised = AddVectoredExceptionHandler(1, raised_continue_handler);
if (!raised)
fail("raised handler registration failed");
ULONG_PTR args[2] = { 0x1122u, 0x3344u };
RaiseException(0xE1111002u, 0, 2, args);
if (gRaisedHits != 1)
fail("raised vectored handler did not continue execution");
expect_remove("remove raised handler failed", RemoveVectoredExceptionHandler(raised));
print("win32_exception_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,157 @@
// Win32 runtime SEH smoke test. This file verifies explicit runtime frames before compiler SEH exists.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
typedef LONG (*PRTL_EXCEPTION_FRAME_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo, PVOID UserContext);
typedef struct _RTL_EXCEPTION_FRAME {
DWORD Size;
DWORD Flags;
struct _RTL_EXCEPTION_FRAME* Previous;
PRTL_EXCEPTION_FRAME_HANDLER Handler;
PVOID UserContext;
} RTL_EXCEPTION_FRAME;
PVOID RtlPushExceptionFrame(RTL_EXCEPTION_FRAME* Frame,
PRTL_EXCEPTION_FRAME_HANDLER Handler,
PVOID UserContext);
BOOL RtlPopExceptionFrame(RTL_EXCEPTION_FRAME* Frame);
static volatile uint32_t gTopHits;
static volatile uint32_t gBottomHits;
static volatile uint32_t gVehHits;
// 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("win32_seh_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Fails the test with one integer value.
static void fail_value(const char* message, uint64_t value) {
print("win32_seh_smoke: ");
print(message);
print(" value=");
print_u64(value);
print("\n");
exit_now(1);
}
// Verifies a breakpoint exception and editable context.
static void verify_breakpoint(EXCEPTION_POINTERS* info) {
if (!info || !info->ExceptionRecord || !info->ContextRecord)
fail("handler received null exception pointers");
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT)
fail_value("handler received unexpected exception code", info->ExceptionRecord->ExceptionCode);
if (!info->ContextRecord->Rip || !info->ContextRecord->Rsp)
fail("handler received empty context");
}
// Handles the first breakpoint from the newest runtime SEH frame.
static LONG top_frame_handler(EXCEPTION_POINTERS* info, void* userContext) {
verify_breakpoint(info);
if (userContext != (void*)0x1111u)
fail("top frame context was not preserved");
gTopHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Handles a breakpoint after the top frame is removed.
static LONG bottom_frame_handler(EXCEPTION_POINTERS* info, void* userContext) {
verify_breakpoint(info);
if (userContext != (void*)0x2222u)
fail("bottom frame context was not preserved");
gBottomHits++;
return EXCEPTION_CONTINUE_EXECUTION;
}
// Proves VEH still sees exceptions before the runtime SEH chain.
static LONG veh_search_handler(EXCEPTION_POINTERS* info) {
verify_breakpoint(info);
gVehHits++;
return EXCEPTION_CONTINUE_SEARCH;
}
// Runs the runtime-registered Win32 SEH smoke test.
int main(void) {
print("win32_seh_smoke: start\n");
if (RtlPushExceptionFrame(NULL, top_frame_handler, NULL))
fail("null frame was accepted");
if (GetLastError() != 87)
fail_value("null frame did not set ERROR_INVALID_PARAMETER", GetLastError());
RTL_EXCEPTION_FRAME bottom;
RTL_EXCEPTION_FRAME top;
if (!RtlPushExceptionFrame(&bottom, bottom_frame_handler, (void*)0x2222u))
fail("bottom SEH frame registration failed");
if (!RtlPushExceptionFrame(&top, top_frame_handler, (void*)0x1111u))
fail("top SEH frame registration failed");
if (RtlPopExceptionFrame(&bottom))
fail("non-top SEH frame pop succeeded unexpectedly");
if (GetLastError() != 87)
fail_value("non-top pop did not set ERROR_INVALID_PARAMETER", GetLastError());
__debugbreak();
if (gTopHits != 1 || gBottomHits != 0)
fail("top SEH frame did not run before bottom frame");
if (!RtlPopExceptionFrame(&top))
fail("top SEH frame pop failed");
void* veh = AddVectoredExceptionHandler(0, veh_search_handler);
if (!veh)
fail("VEH registration failed");
__debugbreak();
if (gVehHits != 1 || gBottomHits != 1)
fail("VEH did not search before SEH handled the exception");
if (!RemoveVectoredExceptionHandler(veh))
fail("VEH removal failed");
if (!RtlPopExceptionFrame(&bottom))
fail("bottom SEH frame pop failed");
print("win32_seh_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -119,7 +119,7 @@ static void sender_thread(uintptr_t arg) {
msg.Type = TEST_MESSAGE_WAKE;
msg.Param[0] = TEST_WAKE_VALUE;
ctx->Status = send_queue(ctx->Queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
SysExitThread(ctx->Status == STATUS_SUCCESS ? 0 : 1);
SysExitThread(ctx->Status == NTLSTATUS_SUCCESS ? 0 : 1);
}
// Fills one queue with immediate sends until the queue reports would-block.
@@ -131,9 +131,9 @@ static void fill_until_would_block(uint32_t queue) {
for (; sends < TEST_FILL_LIMIT; sends++) {
msg.Param[0] = sends;
uint32_t s = send_queue(queue, &msg, 0);
if (s == STATUS_WOULD_BLOCK)
if (s == NTLSTATUS_WOULD_BLOCK)
break;
if (s != STATUS_SUCCESS)
if (s != NTLSTATUS_SUCCESS)
fail_status("fill send", s);
}
@@ -145,20 +145,20 @@ static void fill_until_would_block(uint32_t queue) {
static void test_queue_empty_and_full(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create empty queue", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create empty queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("empty nonblocking receive", recv_queue(queue, &msg, 0), STATUS_WOULD_BLOCK);
expect_status("empty finite receive", recv_queue(queue, &msg, 20), STATUS_TIMEOUT);
expect_status("empty nonblocking receive", recv_queue(queue, &msg, 0), NTLSTATUS_WOULD_BLOCK);
expect_status("empty finite receive", recv_queue(queue, &msg, 20), NTLSTATUS_TIMEOUT);
SysCloseHandle(queue);
queue = 0;
s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create full queue", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create full queue", s);
fill_until_would_block(queue);
expect_status("full nonblocking send", send_queue(queue, &msg, 0), STATUS_WOULD_BLOCK);
expect_status("full finite send", send_queue(queue, &msg, 20), STATUS_TIMEOUT);
expect_status("full nonblocking send", send_queue(queue, &msg, 0), NTLSTATUS_WOULD_BLOCK);
expect_status("full finite send", send_queue(queue, &msg, 20), NTLSTATUS_TIMEOUT);
SysCloseHandle(queue);
}
@@ -166,9 +166,9 @@ static void test_queue_empty_and_full(void) {
static void test_queue_infinite_receive_wakeup(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create wake queue", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create wake queue", s);
TEST_SENDER_CONTEXT ctx = { queue, STATUS_PENDING };
TEST_SENDER_CONTEXT ctx = { queue, NTLSTATUS_PENDING };
uint32_t threadHandle = 0;
uint32_t threadId = 0;
SYS_CREATE_THREAD_REQUEST req = {
@@ -180,17 +180,17 @@ static void test_queue_infinite_receive_wakeup(void) {
&threadId
};
s = SysCreateThread(&req);
if (s != STATUS_SUCCESS) fail_status("create sender", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create sender", s);
TEST_QUEUE_MESSAGE msg = {0};
s = recv_queue(queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
if (s != STATUS_SUCCESS) fail_status("infinite receive", s);
if (s != NTLSTATUS_SUCCESS) fail_status("infinite receive", s);
if (msg.Type != TEST_MESSAGE_WAKE || msg.Param[0] != TEST_WAKE_VALUE)
fail("wake message payload mismatch");
s = SysWait(threadHandle, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait sender", s);
if (ctx.Status != STATUS_SUCCESS) fail_status("sender status", ctx.Status);
if (s != NTLSTATUS_SUCCESS) fail_status("wait sender", s);
if (ctx.Status != NTLSTATUS_SUCCESS) fail_status("sender status", ctx.Status);
SysCloseHandle(threadHandle);
SysCloseHandle(queue);
@@ -200,11 +200,11 @@ static void test_queue_infinite_receive_wakeup(void) {
static void test_queue_request_validation(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create validation queue", s);
if (s != NTLSTATUS_SUCCESS) fail_status("create validation queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("null send request", SysSendQueue((const SYS_QUEUE_SEND_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("null recv request", SysRecvQueue((const SYS_QUEUE_RECV_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("null send request", SysSendQueue((const SYS_QUEUE_SEND_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("null recv request", SysRecvQueue((const SYS_QUEUE_RECV_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
SYS_QUEUE_SEND_REQUEST sendReq = {
0,
@@ -213,15 +213,15 @@ static void test_queue_request_validation(void) {
0,
&msg,
};
expect_status("bad send size", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
expect_status("bad send size", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
sendReq.Size = sizeof(sendReq);
sendReq.Flags = 1;
expect_status("bad send flags", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
expect_status("bad send flags", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
sendReq.Flags = SYS_QUEUE_FLAG_NONE;
sendReq.Message = (const void*)0;
expect_status("bad send message", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
expect_status("bad send message", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
SYS_QUEUE_RECV_REQUEST recvReq = {
sizeof(recvReq),
@@ -230,7 +230,7 @@ static void test_queue_request_validation(void) {
0,
(void*)0,
};
expect_status("bad recv message", SysRecvQueue(&recvReq), STATUS_INVALID_PARAMETER);
expect_status("bad recv message", SysRecvQueue(&recvReq), NTLSTATUS_INVALID_PARAMETER);
SysCloseHandle(queue);
}