move POSIX spawn, exec and child tracking into posixsrv, keep process transactions alive across reconnects and preserve stable PIDs across image replacement, flesh out spawn with ordered file actions, process-group selection and signal inheritance, implement execve with bootstrap validation and rollback before retiring the old process, add vfork children that share the native process while keeping separate POSIX descriptor and signal state, replace local spawn bookkeeping with child reaping, nonblocking and process-group waits and orphan cleanup, add signal dispositions, per-thread masks, pending delivery, process/group signalling and alarms, translate native exceptions into POSIX signals and inherit masks when creating threads, make pipe waits interruptible and raise SIGPIPE for broken pipes, pass arguments and environment into main and add getenv/environ, add direct user APC notifications that can run without an alertable wait, add current-thread capabilities and resume-and-wait, let accepted IPC endpoints open restricted peer process capabilities and preserve caller security during spawn and exec, make process preparation reusable and improve DLL lookup across current, executable and fallback directories, move keyboard repeat into RSSRV so PS/2 and USB share one repeat policy, fix video presentation, report launch failure stages and status codes, mooaaar tests.

This commit is contained in:
maksw committed 2026-09-23 09:39:42 +02:00
1 parent 73abd6f90d
commit 899225f551
57 files changed
+8817 -1216

No files matched your search

@@ -1,139 +1,157 @@
// Process-construction smoke test. The same import-free PE is loaded a second time entirely by userspace.
// Userspace process-builder smoke test for inert construction inputs and child bootstrap vectors.
#include <ntlite/syscall.h>
#include <ntlite/path.h>
#include <ntlite/pe_loader.h>
#include <ntlite/process_loader.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#define TEST_STARTUP_EVENT_TAG 0x50425445u
#define TEST_STARTUP_WORD 0x31503550u
// Exits without relying on a hosted runtime.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Writes one string through the native console syscall.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Writes one 32-bit value as hexadecimal without requiring a formatting runtime.
static void print_hex32(uint32_t value) {
static const char digits[] = "0123456789ABCDEF";
char buffer[11] = "0x00000000";
for (uint32_t i = 0; i < 8; ++i)
buffer[2 + i] = digits[(value >> ((7u - i) * 4u)) & 0xFu];
print(buffer);
// Compares one native string view with a C string.
static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) {
size_t length = strlen(text);
return view.Length == length && (!length || memcmp(view.Data, text, length) == 0);
}
// Detects the child instance through the diagnostic command line.
static int is_child(void) {
SYS_PEB* peb = SysGetPeb();
static const char marker[] = "process-builder-child";
return peb && peb->CommandLine.Length == sizeof(marker) - 1 &&
memcmp(peb->CommandLine.Data, marker, sizeof(marker) - 1) == 0;
return peb && peb->CommandLine.Length == sizeof(marker) - 1u &&
memcmp(peb->CommandLine.Data, marker, sizeof(marker) - 1u) == 0;
}
static SYS_HANDLE find_startup(uint32_t tag) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
return SYS_HANDLE_INVALID;
for (uint32_t i = 0; i < peb->StartupCapabilityCount; ++i) {
if (peb->StartupCapabilities[i].Tag == tag)
return peb->StartupCapabilities[i].Handle;
}
return SYS_HANDLE_INVALID;
// Verifies the serialized generic argv/envp vectors and opaque startup data.
static void verify_child_bootstrap(void) {
uint32_t count = 0;
SYS_STRING_VIEW entry;
if (NtlGetProcessArgumentCount(&count) != NTLSTATUS_SUCCESS || count != 3u) exit_now(20);
if (NtlGetProcessArgument(0, &entry) != NTLSTATUS_SUCCESS ||
!view_equals_cstr(entry, "process_builder_smoke")) exit_now(21);
if (NtlGetProcessArgument(1, &entry) != NTLSTATUS_SUCCESS ||
!view_equals_cstr(entry, "vector-child")) exit_now(22);
if (NtlGetProcessArgument(2, &entry) != NTLSTATUS_SUCCESS ||
!view_equals_cstr(entry, "arg two")) exit_now(23);
if (NtlGetProcessEnvironmentCount(&count) != NTLSTATUS_SUCCESS || count != 2u) exit_now(24);
if (NtlGetProcessEnvironment(0, &entry) != NTLSTATUS_SUCCESS ||
!view_equals_cstr(entry, "P5_TEST=builder")) exit_now(25);
if (NtlGetProcessEnvironment(1, &entry) != NTLSTATUS_SUCCESS ||
!view_equals_cstr(entry, "EMPTY=")) exit_now(26);
uint32_t startupWord = 0;
size_t startupSize = 0;
if (NtlGetProcessStartupData(&startupWord, sizeof(startupWord), &startupSize) != NTLSTATUS_SUCCESS ||
startupSize != sizeof(startupWord) || startupWord != TEST_STARTUP_WORD)
exit_now(27);
}
int main(void) {
if (is_child()) {
SYS_HANDLE event = find_startup(TEST_STARTUP_EVENT_TAG);
if (event == SYS_HANDLE_INVALID) exit_now(10);
if (SysSignalEvent(event) != NTLSTATUS_SUCCESS) exit_now(11);
exit_now(0);
}
// Runs the child half and signals the inherited parent event after validation.
static void run_child(void) {
SYS_HANDLE event = SysGetStartupCapability(TEST_STARTUP_EVENT_TAG);
if (event == SYS_HANDLE_INVALID) exit_now(10);
verify_child_bootstrap();
if (SysSignalEvent(event) != NTLSTATUS_SUCCESS) exit_now(11);
exit_now(0);
}
// Runs the parent half through the reusable userspace process builder wrapper.
static int run_parent(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
exit_now(1);
return 1;
SYS_HANDLE file = SYS_HANDLE_INVALID;
KSTATUS s = NtlOpenFile(peb->ImagePath,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&file);
if (!KSUCCESS(s)) exit_now(2);
SYS_HANDLE child = SYS_HANDLE_INVALID;
SYS_CREATE_PROCESS_OBJECT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = SYS_CREATE_PROCESS_OBJECT_FLAG_NONE;
create.ImagePath = peb->ImagePath;
create.CommandLine = SYS_SV_LITERAL("process-builder-child");
create.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
create.ProcessHandleOut = &child;
s = SysCreateProcessObject(&create);
if (!KSUCCESS(s)) exit_now(3);
NTL_PE_IMAGE image;
memset(&image, 0, sizeof(image));
NTL_PE_LOAD_REQUEST load;
memset(&load, 0, sizeof(load));
load.Size = sizeof(load);
load.ProcessHandle = child;
load.FileHandle = file;
load.Flags = NTL_PE_LOAD_FLAG_NONE;
load.ImageOut = &image;
s = NtlLoadPeImage(&load);
SysCloseHandle(file);
if (!KSUCCESS(s) || !image.Base || !image.EntryPoint) {
print("process_builder_smoke: NtlLoadPeImage failed status=");
print_hex32(s);
print("\n");
exit_now(4);
}
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
KSTATUS status = NtlOpenFile(peb->ImagePath,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&imageFile);
if (!KSUCCESS(status)) return 2;
SYS_HANDLE event = SYS_HANDLE_INVALID;
s = SysCreateEvent(0, 0, &event);
if (!KSUCCESS(s)) exit_now(5);
status = SysCreateEvent(0, 0, &event);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(imageFile);
return 3;
}
SYS_HANDLE childEvent = SYS_HANDLE_INVALID;
SYS_DUPLICATE_HANDLE_REQUEST duplicate;
memset(&duplicate, 0, sizeof(duplicate));
duplicate.Size = sizeof(duplicate);
duplicate.SourceHandle = event;
duplicate.TargetProcessHandle = child;
duplicate.DesiredRights = SYS_EVENT_RIGHT_SIGNAL | SYS_RIGHT_QUERY;
duplicate.Flags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
duplicate.TargetHandleOut = &childEvent;
s = SysDuplicateHandle(&duplicate);
if (!KSUCCESS(s)) exit_now(6);
NTL_PROCESS_STARTUP_CAPABILITY startupCapability;
memset(&startupCapability, 0, sizeof(startupCapability));
startupCapability.Tag = TEST_STARTUP_EVENT_TAG;
startupCapability.SourceHandle = event;
startupCapability.DesiredRights = SYS_EVENT_RIGHT_SIGNAL | SYS_RIGHT_QUERY;
startupCapability.DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
SYS_STARTUP_CAPABILITY startup = { TEST_STARTUP_EVENT_TAG, childEvent };
SYS_START_PROCESS_REQUEST start;
memset(&start, 0, sizeof(start));
start.Size = sizeof(start);
start.ProcessHandle = child;
start.Flags = SYS_START_PROCESS_FLAG_NONE;
start.MainImageBase = image.Base;
start.MainImageSize = image.Size;
start.EntryPoint = image.EntryPoint;
start.StartupCapabilities = &startup;
start.StartupCapabilityCount = 1;
s = SysStartProcess(&start);
if (!KSUCCESS(s)) exit_now(7);
SYS_STRING_VIEW arguments[] = {
SYS_SV_LITERAL("process_builder_smoke"),
SYS_SV_LITERAL("vector-child"),
SYS_SV_LITERAL("arg two")
};
SYS_STRING_VIEW environment[] = {
SYS_SV_LITERAL("P5_TEST=builder"),
SYS_SV_LITERAL("EMPTY=")
};
uint32_t startupWord = TEST_STARTUP_WORD;
SYS_HANDLE child = SYS_HANDLE_INVALID;
NTL_CREATE_SYSTEM_PROCESS_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
request.ImageFile = imageFile;
request.Path = peb->ImagePath;
request.CommandLine = SYS_SV_LITERAL("process-builder-child");
request.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
request.ResolveModuleName = NtlResolveSystemModuleName;
request.StartupCapabilities = &startupCapability;
request.StartupCapabilityCount = 1u;
request.Arguments.Entries = arguments;
request.Arguments.Count = (uint32_t)(sizeof(arguments) / sizeof(arguments[0]));
request.Environment.Entries = environment;
request.Environment.Count = (uint32_t)(sizeof(environment) / sizeof(environment[0]));
request.StartupData = &startupWord;
request.StartupDataSize = sizeof(startupWord);
request.ProcessHandleOut = &child;
status = NtlCreateSystemProcess(&request);
(void)SysCloseHandle(imageFile);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(event);
return 4;
}
status = SysWait(event, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) {
(void)SysKillProcess(child, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(child);
(void)SysCloseHandle(event);
return 5;
}
status = SysWait(child, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(child);
(void)SysCloseHandle(event);
return 6;
}
s = SysWait(event, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(s)) exit_now(8);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(s)) exit_now(9);
int32_t exitCode = -1;
s = SysGetProcessExitCode(child, &exitCode);
SysCloseHandle(event);
SysCloseHandle(child);
if (!KSUCCESS(s) || exitCode != 0) exit_now(12);
status = SysGetProcessExitCode(child, &exitCode);
(void)SysCloseHandle(child);
(void)SysCloseHandle(event);
if (!KSUCCESS(status) || exitCode != 0) return 7;
return 0;
}
// Exercises generic userspace process construction in parent and child roles.
int main(void) {
if (is_child()) run_child();
return run_parent();
}