clean up and standardize types across the codebase, unify atomic operations, improve UTF-8 handling throughout the system, remove obsolete input and named queue interfaces, redesign process preparation and creation, tighten IPC and service boundaries, improve error handling and reliability, minor documentation cleanup, fix doom surfaces, fix panic in ini parsing.
This commit is contained in:
1 parent
899225f551
commit
c0ad63c27f
226 files changed
+6562
-4147
No files matched your search
@@ -1,5 +1,6 @@
|
||||
// Native userspace process construction built on capability-relative file open and PE materialization.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ntlite/process_loader.h>
|
||||
#include <ntlite/path.h>
|
||||
#include <ntlite/resource.h>
|
||||
@@ -15,26 +16,42 @@ static void NtlDiscardChild(SYS_HANDLE processHandle) {
|
||||
(void)SysCloseHandle(processHandle);
|
||||
}
|
||||
|
||||
#define NTL_PROCESS_BUILDER_MAGIC 0x3142504Eu
|
||||
#define NTL_PREPARATION_PAGE_SIZE 4096u
|
||||
|
||||
typedef struct NTL_PROCESS_BUILDER_INTERNAL {
|
||||
uint32_t Magic;
|
||||
// The preparation is allocated by the loader; its private layout is not ABI.
|
||||
struct NTL_PROCESS_PREPARATION {
|
||||
uint32_t StartupCapabilityCount;
|
||||
SYS_HANDLE ProcessHandle;
|
||||
NTL_MODULE_CONTEXT* Modules;
|
||||
NTL_PE_IMAGE Image;
|
||||
void* LoaderData;
|
||||
SYS_STARTUP_CAPABILITY ChildCapabilities[SYS_STARTUP_CAPABILITY_MAX];
|
||||
} NTL_PROCESS_BUILDER_INTERNAL;
|
||||
};
|
||||
|
||||
typedef char NTL_PROCESS_BUILDER_SIZE_CHECK[
|
||||
sizeof(NTL_PROCESS_BUILDER_INTERNAL) <= sizeof(NTL_PROCESS_BUILDER) ? 1 : -1];
|
||||
|
||||
static NTL_PROCESS_BUILDER_INTERNAL* NtlBuilderInternal(NTL_PROCESS_BUILDER* builder) {
|
||||
return (NTL_PROCESS_BUILDER_INTERNAL*)(void*)builder;
|
||||
// Allocates zero-initialized page-backed preparation state in user space.
|
||||
static KSTATUS NtlAllocatePreparation(NTL_PROCESS_PREPARATION** preparationOut) {
|
||||
if (!preparationOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*preparationOut = 0;
|
||||
if (sizeof(NTL_PROCESS_PREPARATION) > SIZE_MAX - (NTL_PREPARATION_PAGE_SIZE - 1u))
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
size_t pages = (sizeof(NTL_PROCESS_PREPARATION) + NTL_PREPARATION_PAGE_SIZE - 1u) /
|
||||
NTL_PREPARATION_PAGE_SIZE;
|
||||
SYS_ALLOC_VM_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Protect = SYS_VM_READWRITE;
|
||||
request.Flags = SYS_VM_FLAG_NONE;
|
||||
request.PageCount = pages;
|
||||
request.BaseOut = (void**)preparationOut;
|
||||
KSTATUS status = SysAllocVm(&request);
|
||||
if (KSUCCESS(status)) {
|
||||
memset(*preparationOut, 0, pages * NTL_PREPARATION_PAGE_SIZE);
|
||||
(*preparationOut)->ProcessHandle = SYS_HANDLE_INVALID;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int NtlBuilderRequestValid(const NTL_PROCESS_BUILDER_REQUEST* request) {
|
||||
static bool NtlPreparationRequestValid(const NTL_PREPARE_PROCESS_REQUEST* request) {
|
||||
return request && request->Size == sizeof(*request) &&
|
||||
request->Flags == NTL_PROCESS_LOAD_FLAG_NONE &&
|
||||
request->RootHandle != SYS_HANDLE_INVALID &&
|
||||
@@ -49,25 +66,22 @@ static int NtlBuilderRequestValid(const NTL_PROCESS_BUILDER_REQUEST* request) {
|
||||
(!request->StartupDataSize || request->StartupData);
|
||||
}
|
||||
|
||||
static void NtlBuilderReset(NTL_PROCESS_BUILDER* builder) {
|
||||
if (builder) memset(builder, 0, sizeof(*builder));
|
||||
// Disposes private preparation state and optionally discards the inert child.
|
||||
static void NtlDestroyPreparation(NTL_PROCESS_PREPARATION* preparation, bool discardChild) {
|
||||
if (!preparation) return;
|
||||
if (preparation->Modules) NtlpDestroyModuleContext(preparation->Modules);
|
||||
if (discardChild && preparation->ProcessHandle != SYS_HANDLE_INVALID)
|
||||
NtlDiscardChild(preparation->ProcessHandle);
|
||||
(void)SysFreeVm(preparation);
|
||||
}
|
||||
|
||||
// Aborts one prepared inert child. This is deliberately safe on an empty builder.
|
||||
void NtlProcessBuilderAbort(NTL_PROCESS_BUILDER* builder) {
|
||||
if (!builder) return;
|
||||
NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder);
|
||||
if (state->Magic != NTL_PROCESS_BUILDER_MAGIC) {
|
||||
NtlBuilderReset(builder);
|
||||
return;
|
||||
}
|
||||
if (state->Modules) NtlpDestroyModuleContext(state->Modules);
|
||||
if (state->ProcessHandle != SYS_HANDLE_INVALID) NtlDiscardChild(state->ProcessHandle);
|
||||
NtlBuilderReset(builder);
|
||||
// Aborts and consumes an inert child; NULL requires no action.
|
||||
void NtlAbortPreparedProcess(NTL_PROCESS_PREPARATION* preparation) {
|
||||
NtlDestroyPreparation(preparation, true);
|
||||
}
|
||||
|
||||
// Opens the executable's containing directory as the private dependency-search root.
|
||||
static KSTATUS NtlOpenImageDirectory(const NTL_PROCESS_BUILDER_REQUEST* request,
|
||||
static KSTATUS NtlOpenImageDirectory(const NTL_PREPARE_PROCESS_REQUEST* request,
|
||||
SYS_HANDLE* directoryOut, int* borrowedRootOut) {
|
||||
if (!request || !directoryOut || !borrowedRootOut || !SysSvIsValid(request->Path) ||
|
||||
request->Path.Length == 0 || SysSvContainsByte(request->Path, '\0'))
|
||||
@@ -101,13 +115,15 @@ static KSTATUS NtlOpenImageDirectory(const NTL_PROCESS_BUILDER_REQUEST* request,
|
||||
|
||||
// Prepares an inert child: process object, image/dependencies, inherited startup
|
||||
// capabilities, CWD, and loader/bootstrap block are all complete before return.
|
||||
KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder,
|
||||
const NTL_PROCESS_BUILDER_REQUEST* request,
|
||||
SYS_HANDLE imageFile) {
|
||||
if (!builder || !NtlBuilderRequestValid(request)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NtlBuilderReset(builder);
|
||||
NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder);
|
||||
state->ProcessHandle = SYS_HANDLE_INVALID;
|
||||
KSTATUS NtlPrepareProcess(const NTL_PREPARE_PROCESS_REQUEST* request,
|
||||
SYS_HANDLE imageFile,
|
||||
NTL_PROCESS_PREPARATION** preparationOut) {
|
||||
if (!preparationOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*preparationOut = 0;
|
||||
if (!NtlPreparationRequestValid(request)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTL_PROCESS_PREPARATION* state = 0;
|
||||
KSTATUS status = NtlAllocatePreparation(&state);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE openedImage = SYS_HANDLE_INVALID;
|
||||
if (imageFile == SYS_HANDLE_INVALID) {
|
||||
@@ -121,7 +137,10 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder,
|
||||
open.Flags = SYS_FILE_FLAG_NONE;
|
||||
open.HandleOut = &openedImage;
|
||||
KSTATUS openStatus = SysOpenFileAt(&open);
|
||||
if (!KSUCCESS(openStatus)) return openStatus;
|
||||
if (!KSUCCESS(openStatus)) {
|
||||
NtlDestroyPreparation(state, true);
|
||||
return openStatus;
|
||||
}
|
||||
imageFile = openedImage;
|
||||
}
|
||||
|
||||
@@ -133,8 +152,8 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder,
|
||||
create.CommandLine = request->CommandLine;
|
||||
create.SecurityContext = request->SecurityContext;
|
||||
create.ProcessHandleOut = &state->ProcessHandle;
|
||||
KSTATUS status = SysCreateProcessObject(&create);
|
||||
if (!KSUCCESS(status)) goto done;
|
||||
status = SysCreateProcessObject(&create);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
|
||||
char currentDirectoryBuffer[SYS_PATH_MAX + 1u];
|
||||
SYS_STRING_VIEW currentDirectory;
|
||||
@@ -231,99 +250,91 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder,
|
||||
request->StartupDataSize, &state->LoaderData);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
|
||||
state->Magic = NTL_PROCESS_BUILDER_MAGIC;
|
||||
*preparationOut = state;
|
||||
status = NTLSTATUS_SUCCESS;
|
||||
goto done;
|
||||
|
||||
fail:
|
||||
if (state->Modules) NtlpDestroyModuleContext(state->Modules);
|
||||
if (state->ProcessHandle != SYS_HANDLE_INVALID) NtlDiscardChild(state->ProcessHandle);
|
||||
NtlBuilderReset(builder);
|
||||
NtlDestroyPreparation(state, true);
|
||||
done:
|
||||
if (openedImage != SYS_HANDLE_INVALID) (void)SysCloseHandle(openedImage);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Returns the process capability owned by one prepared builder without transferring it.
|
||||
KSTATUS NtlProcessBuilderGetProcessHandle(NTL_PROCESS_BUILDER* builder,
|
||||
SYS_HANDLE* processHandleOut) {
|
||||
if (!builder || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
// Returns the borrowed process capability of a prepared child.
|
||||
KSTATUS NtlPreparedProcessGetHandle(const NTL_PROCESS_PREPARATION* preparation,
|
||||
SYS_HANDLE* processHandleOut) {
|
||||
if (!processHandleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*processHandleOut = SYS_HANDLE_INVALID;
|
||||
NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder);
|
||||
if (state->Magic != NTL_PROCESS_BUILDER_MAGIC ||
|
||||
state->ProcessHandle == SYS_HANDLE_INVALID || !state->Modules || !state->LoaderData)
|
||||
if (!preparation || preparation->ProcessHandle == SYS_HANDLE_INVALID ||
|
||||
!preparation->Modules || !preparation->LoaderData)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*processHandleOut = state->ProcessHandle;
|
||||
*processHandleOut = preparation->ProcessHandle;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Starts one prepared child. On success ownership of the retained native process
|
||||
// capability moves to the caller; on failure the child is destroyed.
|
||||
KSTATUS NtlProcessBuilderStart(NTL_PROCESS_BUILDER* builder, SYS_HANDLE* processHandleOut) {
|
||||
if (!builder || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
// Starts and consumes one prepared child, including on ordinary start failure.
|
||||
KSTATUS NtlStartPreparedProcess(NTL_PROCESS_PREPARATION* preparation,
|
||||
SYS_HANDLE* processHandleOut) {
|
||||
if (!preparation || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*processHandleOut = SYS_HANDLE_INVALID;
|
||||
NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder);
|
||||
if (state->Magic != NTL_PROCESS_BUILDER_MAGIC ||
|
||||
state->ProcessHandle == SYS_HANDLE_INVALID || !state->Modules || !state->LoaderData)
|
||||
if (preparation->ProcessHandle == SYS_HANDLE_INVALID ||
|
||||
!preparation->Modules || !preparation->LoaderData)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
SYS_START_PROCESS_REQUEST startRequest;
|
||||
memset(&startRequest, 0, sizeof(startRequest));
|
||||
startRequest.Size = sizeof(startRequest);
|
||||
startRequest.ProcessHandle = state->ProcessHandle;
|
||||
startRequest.ProcessHandle = preparation->ProcessHandle;
|
||||
startRequest.Flags = SYS_START_PROCESS_FLAG_NONE;
|
||||
startRequest.MainImageBase = state->Image.Base;
|
||||
startRequest.MainImageSize = state->Image.Size;
|
||||
startRequest.EntryPoint = state->Image.EntryPoint;
|
||||
startRequest.StartupCapabilities = state->ChildCapabilities;
|
||||
startRequest.StartupCapabilityCount = state->StartupCapabilityCount;
|
||||
startRequest.LoaderData = state->LoaderData;
|
||||
startRequest.MainImageBase = preparation->Image.Base;
|
||||
startRequest.MainImageSize = preparation->Image.Size;
|
||||
startRequest.EntryPoint = preparation->Image.EntryPoint;
|
||||
startRequest.StartupCapabilities = preparation->ChildCapabilities;
|
||||
startRequest.StartupCapabilityCount = preparation->StartupCapabilityCount;
|
||||
startRequest.LoaderData = preparation->LoaderData;
|
||||
KSTATUS status = SysStartProcess(&startRequest);
|
||||
NtlpDestroyModuleContext(state->Modules);
|
||||
state->Modules = 0;
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlDiscardChild(state->ProcessHandle);
|
||||
NtlBuilderReset(builder);
|
||||
NtlDestroyPreparation(preparation, true);
|
||||
return status;
|
||||
}
|
||||
|
||||
*processHandleOut = state->ProcessHandle;
|
||||
NtlBuilderReset(builder);
|
||||
// Transfer the native handle before releasing loader-owned metadata.
|
||||
*processHandleOut = preparation->ProcessHandle;
|
||||
preparation->ProcessHandle = SYS_HANDLE_INVALID;
|
||||
NtlDestroyPreparation(preparation, false);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void NtlBuilderRequestFromCreate(const NTL_CREATE_PROCESS_AT_REQUEST* create,
|
||||
NTL_PROCESS_BUILDER_REQUEST* builder) {
|
||||
memset(builder, 0, sizeof(*builder));
|
||||
builder->Size = sizeof(*builder);
|
||||
builder->Flags = create->Flags;
|
||||
builder->RootHandle = create->RootHandle;
|
||||
builder->Path = create->Path;
|
||||
builder->CommandLine = create->CommandLine;
|
||||
builder->CurrentDirectory = create->CurrentDirectory;
|
||||
builder->SecurityContext = create->SecurityContext;
|
||||
builder->ModuleRootHandle = create->ModuleRootHandle;
|
||||
builder->ModuleDirectory = create->ModuleDirectory;
|
||||
builder->ResolveModuleName = create->ResolveModuleName;
|
||||
builder->ResolveContext = create->ResolveContext;
|
||||
builder->StartupCapabilities = create->StartupCapabilities;
|
||||
builder->StartupCapabilityCount = create->StartupCapabilityCount;
|
||||
builder->Arguments = create->Arguments;
|
||||
builder->Environment = create->Environment;
|
||||
builder->StartupData = create->StartupData;
|
||||
builder->StartupDataSize = create->StartupDataSize;
|
||||
static void NtlPreparationRequestFromCreate(const NTL_CREATE_PROCESS_AT_REQUEST* create,
|
||||
NTL_PREPARE_PROCESS_REQUEST* preparedRequest) {
|
||||
memset(preparedRequest, 0, sizeof(*preparedRequest));
|
||||
preparedRequest->Size = sizeof(*preparedRequest);
|
||||
preparedRequest->Flags = create->Flags;
|
||||
preparedRequest->RootHandle = create->RootHandle;
|
||||
preparedRequest->Path = create->Path;
|
||||
preparedRequest->CommandLine = create->CommandLine;
|
||||
preparedRequest->CurrentDirectory = create->CurrentDirectory;
|
||||
preparedRequest->SecurityContext = create->SecurityContext;
|
||||
preparedRequest->ModuleRootHandle = create->ModuleRootHandle;
|
||||
preparedRequest->ModuleDirectory = create->ModuleDirectory;
|
||||
preparedRequest->ResolveModuleName = create->ResolveModuleName;
|
||||
preparedRequest->ResolveContext = create->ResolveContext;
|
||||
preparedRequest->StartupCapabilities = create->StartupCapabilities;
|
||||
preparedRequest->StartupCapabilityCount = create->StartupCapabilityCount;
|
||||
preparedRequest->Arguments = create->Arguments;
|
||||
preparedRequest->Environment = create->Environment;
|
||||
preparedRequest->StartupData = create->StartupData;
|
||||
preparedRequest->StartupDataSize = create->StartupDataSize;
|
||||
}
|
||||
|
||||
static KSTATUS NtlCreateProcessFile(const NTL_CREATE_PROCESS_AT_REQUEST* request, SYS_HANDLE fileHandle) {
|
||||
NTL_PROCESS_BUILDER_REQUEST builderRequest;
|
||||
NtlBuilderRequestFromCreate(request, &builderRequest);
|
||||
NTL_PROCESS_BUILDER builder;
|
||||
memset(&builder, 0, sizeof(builder));
|
||||
KSTATUS status = NtlProcessBuilderPrepare(&builder, &builderRequest, fileHandle);
|
||||
NTL_PREPARE_PROCESS_REQUEST preparationRequest;
|
||||
NtlPreparationRequestFromCreate(request, &preparationRequest);
|
||||
NTL_PROCESS_PREPARATION* preparation = 0;
|
||||
KSTATUS status = NtlPrepareProcess(&preparationRequest, fileHandle, &preparation);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = NtlProcessBuilderStart(&builder, request->ProcessHandleOut);
|
||||
if (!KSUCCESS(status)) NtlProcessBuilderAbort(&builder);
|
||||
return status;
|
||||
return NtlStartPreparedProcess(preparation, request->ProcessHandleOut);
|
||||
}
|
||||
|
||||
// Creates and starts one child from a PE file beneath a capability-relative root.
|
||||
|
||||
Reference in new issue
Block a user