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

@@ -147,33 +147,52 @@ static NTL_POSIX_REQUEST make_request(uint32_t operation) {
return request;
}
// Registers the current native process by transferring a restricted self process capability.
// Registers the current native process through the accepted Port peer capability.
static KSTATUS register_current_process(SYS_HANDLE endpoint, NTL_POSIX_REPLY* replyOut) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_REGISTER_PROCESS);
size_t cwdRequired = 0;
KSTATUS status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), &cwdRequired);
if (!KSUCCESS(status)) return status;
request.PathLength = (uint32_t)strlen(request.Path);
return call_posixsrv(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyOut);
}
// Provides a valid user APC target for protocol-level thread registration tests.
static void __stdcall signal_probe_apc(void* context, void* argument1, void* argument2) {
(void)context;
(void)argument1;
(void)argument2;
}
// Registers the current thread by transferring a restricted self capability.
static KSTATUS register_current_thread(SYS_HANDLE endpoint, NTL_POSIX_REPLY* replyOut) {
SYS_HANDLE self = SYS_HANDLE_INVALID;
KSTATUS status = SysOpenProcess(SysGetCurrentPid(),
SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_TRANSFER,
&self);
KSTATUS status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY,
&self);
if (!KSUCCESS(status)) return status;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = self;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_THREAD_RIGHT_NOTIFY;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_REGISTER_PROCESS);
size_t cwdRequired = 0;
status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), &cwdRequired);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(self);
return status;
}
request.PathLength = (uint32_t)strlen(request.Path);
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_REGISTER_THREAD);
request.SignalApcRoutine = (uint64_t)(uintptr_t)signal_probe_apc;
status = call_posixsrv(endpoint, &request, &transfer, 1u, replyOut);
(void)SysCloseHandle(self);
return status;
}
// Unregisters the calling thread using the Port sender identity.
static KSTATUS unregister_current_thread(SYS_HANDLE endpoint, NTL_POSIX_REPLY* replyOut) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_UNREGISTER_THREAD);
KSTATUS status = call_posixsrv(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
replyOut);
if (!KSUCCESS(status)) return status;
return replyOut->Status;
}
// Creates one handle-less server-owned description and returns its stable identity.
static uint64_t create_synthetic_description(SYS_HANDLE endpoint,
uint32_t type,
@@ -200,6 +219,146 @@ static KSTATUS release_description(SYS_HANDLE endpoint, uint64_t descriptionId,
return replyOut->Status;
}
typedef struct VFORK_SUBSTRATE_CONTEXT {
SYS_HANDLE Endpoint;
uint32_t ParentPid;
uint32_t ChildPid;
uint64_t Token;
uint64_t FdToken;
uint64_t DescriptionId;
volatile uint32_t Result;
} VFORK_SUBSTRATE_CONTEXT;
// Attaches one native thread to a borrowed logical process and verifies routed FD ownership.
static void vfork_substrate_thread(uintptr_t argument) {
VFORK_SUBSTRATE_CONTEXT* context = (VFORK_SUBSTRATE_CONTEXT*)argument;
if (!context) SysExitThread(1);
SYS_HANDLE self = SYS_HANDLE_INVALID;
KSTATUS status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY, &self);
if (!KSUCCESS(status)) { context->Result = 1u; SysExitThread(1); }
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = self;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
SYS_THREAD_RIGHT_NOTIFY;
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_VFORK_ATTACH);
request.TargetPosixPid = context->ChildPid;
request.VforkToken = context->Token;
request.SignalApcRoutine = (uint64_t)(uintptr_t)signal_probe_apc;
NTL_POSIX_REPLY reply;
status = call_posixsrv(context->Endpoint, &request, &transfer, 1u, &reply);
(void)SysCloseHandle(self);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status) || reply.PosixPid != context->ChildPid ||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
reply.VforkToken != context->Token) {
context->Result = 2u;
SysExitThread(2);
}
request = make_request(NTL_POSIX_OP_GET_PROCESS_ATTRIBUTES);
status = call_posixsrv(context->Endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status) || reply.PosixPid != context->ChildPid ||
reply.ParentPosixPid != context->ParentPid ||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED) {
context->Result = 3u;
SysExitThread(3);
}
request = make_request(NTL_POSIX_OP_IMPORT_FD_INHERITANCE);
request.FdInheritanceToken = context->FdToken;
request.FdInheritanceIndex = 0;
status = call_posixsrv_exchange(context->Endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0,
(SYS_HANDLE*)0, 0, 0, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status) || reply.FdInheritanceCount != 1u ||
reply.FdDescriptionTotal != 1u || reply.FdDescriptionCount != 1u ||
reply.FdManifest[0].Descriptor != 7 ||
reply.FdManifest[0].OpenDescriptionId != context->DescriptionId ||
reply.FdDescriptions[0].OpenDescriptionId != context->DescriptionId) {
context->Result = 4u;
SysExitThread(4);
}
request = make_request(NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION);
request.OpenDescriptionId = context->DescriptionId;
status = call_posixsrv(context->Endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
context->Result = 5u;
SysExitThread(5);
}
context->Result = 0u;
SysExitThread(0);
}
// Exercises the server-owned borrowed-process record and sender-TID logical routing.
static void test_vfork_substrate(SYS_HANDLE endpoint,
uint32_t parentPid,
uint64_t descriptionId,
uint32_t baselineProcesses,
uint32_t baselineThreads) {
NTL_POSIX_REQUEST request = make_request(NTL_POSIX_OP_CREATE_FD_INHERITANCE);
request.FdInheritanceCount = 1u;
request.FdManifest[0].Descriptor = 7;
request.FdManifest[0].OpenDescriptionId = descriptionId;
NTL_POSIX_REPLY reply;
if (!KSUCCESS(call_posixsrv(endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.FdInheritanceToken == 0)
fail("vfork descriptor snapshot failed");
uint64_t fdToken = reply.FdInheritanceToken;
request = make_request(NTL_POSIX_OP_VFORK_BEGIN);
request.FdInheritanceToken = fdToken;
if (!KSUCCESS(call_posixsrv(endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.SpawnedPosixPid == 0 || reply.VforkToken == 0 ||
reply.RegisteredProcesses != baselineProcesses + 1u)
fail("vfork borrowed child creation failed");
VFORK_SUBSTRATE_CONTEXT context;
memset(&context, 0, sizeof(context));
context.Endpoint = endpoint;
context.ParentPid = parentPid;
context.ChildPid = reply.SpawnedPosixPid;
context.Token = reply.VforkToken;
context.FdToken = fdToken;
context.DescriptionId = descriptionId;
context.Result = UINT32_MAX;
SYS_HANDLE thread = SYS_HANDLE_INVALID;
uint32_t tid = 0;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)vfork_substrate_thread;
create.Argument = (uintptr_t)&context;
create.Priority = 1u;
create.ThreadHandleOut = &thread;
create.ThreadIdOut = &tid;
if (!KSUCCESS(SysCreateThread(&create)) || thread == SYS_HANDLE_INVALID || tid == 0)
fail("vfork borrowed native thread creation failed");
if (!KSUCCESS(SysWait(thread, SYS_WAIT_FLAG_NONE)))
fail("vfork borrowed native thread wait failed");
(void)SysCloseHandle(thread);
if (context.Result != 0u)
fail("vfork borrowed thread routing failed");
request = make_request(NTL_POSIX_OP_VFORK_CANCEL);
request.TargetPosixPid = context.ChildPid;
request.VforkToken = context.Token;
if (!KSUCCESS(call_posixsrv(endpoint, &request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.RegisteredProcesses != baselineProcesses ||
reply.RegisteredThreads != baselineThreads)
fail("vfork borrowed child cleanup failed");
}
// Waits for the process monitor to retire one process and all of its shared holders.
static void wait_registry_counts(SYS_HANDLE endpoint,
uint32_t expectedProcesses,
@@ -354,7 +513,22 @@ int main(void) {
strcmp(reply.CurrentDirectory, registeredCwd) != 0)
fail("registered cwd mismatch");
uint32_t baselineProcesses = reply.RegisteredProcesses;
uint32_t baselineDescriptions = reply.RegisteredOpenDescriptions;
uint32_t baselineThreads = reply.RegisteredThreads;
if (!KSUCCESS(register_current_thread(first, &reply)) || !KSUCCESS(reply.Status) ||
reply.RegisteredThreads != baselineThreads + 1u)
fail("thread registration failed");
if (!KSUCCESS(register_current_thread(first, &reply)) || !KSUCCESS(reply.Status) ||
reply.RegisteredThreads != baselineThreads + 1u)
fail("duplicate thread registration was not idempotent");
if (!KSUCCESS(unregister_current_thread(first, &reply)) ||
reply.RegisteredThreads != baselineThreads)
fail("thread unregister failed");
if (unregister_current_thread(first, &reply) != NTLSTATUS_NOT_FOUND)
fail("duplicate thread unregister was accepted");
NTL_POSIX_REPLY descriptionReply;
uint64_t descriptionId = create_synthetic_description(
first, NTL_POSIX_OPEN_TYPE_DEV_NULL, NTL_POSIX_OPEN_STATUS_WRONLY,
@@ -381,6 +555,9 @@ int main(void) {
(uint32_t)shared->StatusFlags != request.OpenStatusFlags)
fail("server-serialized status update failed");
test_vfork_substrate(first, stablePid, descriptionId,
baselineProcesses, baselineThreads);
if (!KSUCCESS(release_description(first, descriptionId, &descriptionReply)) ||
descriptionReply.RegisteredOpenDescriptions != baselineDescriptions)
fail("shared description release failed");
@@ -469,8 +646,8 @@ int main(void) {
request = make_request(NTL_POSIX_OP_QUERY);
if (!KSUCCESS(call_posixsrv(second, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.ActiveTransactions != 0 || reply.PosixPid != stablePid)
fail("connection transaction isolation failed");
!KSUCCESS(reply.Status) || reply.ActiveTransactions != 1 || reply.PosixPid != stablePid)
fail("process transaction sharing failed");
request = make_request(NTL_POSIX_OP_QUERY);
request.Version = NTL_POSIX_PROTOCOL_VERSION + 1u;
@@ -480,9 +657,9 @@ int main(void) {
request = make_request(NTL_POSIX_OP_CANCEL_TRANSACTION);
request.TransactionId = transactionId;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
if (!KSUCCESS(call_posixsrv(second, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.TransactionId != transactionId || reply.ActiveTransactions != 0)
fail("transaction cancel failed");
fail("cross-connection transaction cancel failed");
request = make_request(NTL_POSIX_OP_CANCEL_TRANSACTION);
request.TransactionId = transactionId;
@@ -493,8 +670,9 @@ int main(void) {
request = make_request(NTL_POSIX_OP_BEGIN_TRANSACTION);
request.TransactionKind = NTL_POSIX_TRANSACTION_EXEC;
if (!KSUCCESS(call_posixsrv(first, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status))
!KSUCCESS(reply.Status) || reply.TransactionId == 0 || reply.ActiveTransactions != 1)
fail("exec transaction begin failed");
uint32_t reconnectTransactionId = reply.TransactionId;
(void)SysDisconnectPort(first);
(void)SysCloseHandle(first);
@@ -504,8 +682,13 @@ int main(void) {
SYS_HANDLE third = SYS_HANDLE_INVALID;
if (!KSUCCESS(connect_posixsrv(&third))) fail("reconnect failed");
if (!KSUCCESS(register_current_process(third, &reply)) || !KSUCCESS(reply.Status) ||
reply.PosixPid != stablePid || reply.NativeGeneration != 1u || reply.ActiveTransactions != 0)
fail("live process lost identity after disconnect");
reply.PosixPid != stablePid || reply.NativeGeneration != 1u || reply.ActiveTransactions != 1)
fail("live process lost process-owned transaction after disconnect");
request = make_request(NTL_POSIX_OP_CANCEL_TRANSACTION);
request.TransactionId = reconnectTransactionId;
if (!KSUCCESS(call_posixsrv(third, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply)) ||
!KSUCCESS(reply.Status) || reply.ActiveTransactions != 0)
fail("reconnected transaction cancel failed");
(void)SysDisconnectPort(third);
(void)SysCloseHandle(third);