126 lines
3.5 KiB
C
126 lines
3.5 KiB
C
// Native syscall contract smoke test.
|
|
// Verifies public tick units and process exit-code round trips.
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
#define CHILD_EXIT_CODE ((int32_t)-1234567)
|
|
|
|
// 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(int32_t code) {
|
|
while (true) SysExitProcess(code);
|
|
}
|
|
|
|
// Checks whether a string view equals a NUL-terminated string.
|
|
static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) {
|
|
size_t n = 0;
|
|
while (text[n]) n++;
|
|
if (view.Length != n || (!view.Data && n != 0)) return 0;
|
|
for (size_t i = 0; i < n; i++) {
|
|
if (view.Data[i] != text[i]) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Fails the test with one plain message.
|
|
static void fail(const char* msg) {
|
|
print("syscall_contract_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("syscall_contract_smoke: ");
|
|
print(what);
|
|
print(" status=");
|
|
print_u64(status);
|
|
print("\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
// Verifies the documented tick unit without relying on wall-clock precision.
|
|
static void check_tick_contract(void) {
|
|
uint64_t frequency = 0;
|
|
uint32_t s = SysGetTickFrequency(&frequency);
|
|
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 != NTLSTATUS_SUCCESS) fail_status("sleep", s);
|
|
|
|
uint64_t t1 = SysGetTick();
|
|
if (t1 < t0) fail("tick moved backwards across sleep");
|
|
if (SysTicksToMicroseconds(t1 - t0, frequency) < 1000ULL)
|
|
fail("tick delta was smaller than one millisecond");
|
|
}
|
|
|
|
// Verifies process exit-code storage, wait, and query agree on int32_t.
|
|
static void check_exit_code_contract(void) {
|
|
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 req = {
|
|
peb->ImagePath,
|
|
SysSvFromCstr("child"),
|
|
&child,
|
|
};
|
|
|
|
uint32_t s = SysCreateProcess(&req);
|
|
if (s != NTLSTATUS_SUCCESS) fail_status("create child", s);
|
|
|
|
s = SysWait(child, SYS_WAIT_FLAG_NONE);
|
|
if (s != NTLSTATUS_SUCCESS) fail_status("wait child", s);
|
|
|
|
int32_t exitCode = 0;
|
|
s = SysGetProcessExitCode(child, &exitCode);
|
|
if (s != NTLSTATUS_SUCCESS) fail_status("query child exit code", s);
|
|
if (exitCode != CHILD_EXIT_CODE) fail("child exit code mismatch");
|
|
|
|
SysCloseHandle(child);
|
|
}
|
|
|
|
// Runs the syscall ABI contract smoke test.
|
|
int main(void) {
|
|
SYS_PEB* peb = SysGetPeb();
|
|
if (peb && view_equals_cstr(peb->CommandLine, "child")) {
|
|
exit_now(CHILD_EXIT_CODE);
|
|
}
|
|
|
|
print("syscall_contract_smoke: start\n");
|
|
check_tick_contract();
|
|
check_exit_code_contract();
|
|
print("syscall_contract_smoke: PASS\n");
|
|
exit_now(0);
|
|
return 0;
|
|
}
|