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

+251 -76
View File
@@ -13,7 +13,7 @@
#define NTL_MODULE_CONTEXT_MAGIC 0x4D4F444Cu
#define NTL_MODULE_PAGE_SIZE 4096u
#define NTL_LOADER_DATA_MAGIC 0x30444C4Eu
#define NTL_LOADER_DATA_VERSION 3u
#define NTL_LOADER_DATA_VERSION 4u
typedef struct NTL_LOADER_IMAGE_RECORD {
uintptr_t Base;
@@ -22,6 +22,11 @@ typedef struct NTL_LOADER_IMAGE_RECORD {
char Name[NTL_MODULE_NAME_MAX];
} NTL_LOADER_IMAGE_RECORD;
typedef struct NTL_LOADER_STRING_RECORD {
uint32_t Offset;
uint32_t Length;
} NTL_LOADER_STRING_RECORD;
typedef struct NTL_LOADER_DATA {
uint32_t Magic;
uint32_t Version;
@@ -31,6 +36,10 @@ typedef struct NTL_LOADER_DATA {
uint32_t CurrentDirectoryLength;
uint32_t StartupDataOffset;
uint32_t StartupDataLength;
uint32_t ArgumentVectorOffset;
uint32_t ArgumentCount;
uint32_t EnvironmentVectorOffset;
uint32_t EnvironmentCount;
NTL_LOADER_IMAGE_RECORD Images[];
} NTL_LOADER_DATA;
@@ -48,8 +57,12 @@ typedef struct NTL_MODULE_ENTRY {
struct NTL_MODULE_CONTEXT {
uint32_t Magic;
SYS_HANDLE ProcessHandle;
SYS_HANDLE ModuleRootHandle;
SYS_STRING_VIEW ModuleDirectory;
SYS_HANDLE PrimaryModuleRootHandle;
SYS_STRING_VIEW PrimaryModuleDirectory;
SYS_HANDLE SecondaryModuleRootHandle;
SYS_STRING_VIEW SecondaryModuleDirectory;
SYS_HANDLE FallbackModuleRootHandle;
SYS_STRING_VIEW FallbackModuleDirectory;
NTL_MODULE_NAME_RESOLVER Resolver;
void* ResolverContext;
NTL_MODULE_ENTRY* Head;
@@ -202,27 +215,6 @@ static KSTATUS NtlpCanonicalName(NTL_MODULE_CONTEXT* context,
return NtlpCopyName(*canonicalOut, scratch);
}
// Builds the capability-relative dependency path beneath the configured module directory.
static KSTATUS NtlpBuildModulePath(NTL_MODULE_CONTEXT* context,
SYS_STRING_VIEW canonical,
char out[SYS_PATH_MAX]) {
if (!context || !out) return NTLSTATUS_INVALID_PARAMETER;
char name[NTL_MODULE_NAME_MAX];
KSTATUS status = NtlpCopyName(canonical, name);
if (!KSUCCESS(status)) return status;
size_t dirLength = context->ModuleDirectory.Length;
if (!SysSvIsValid(context->ModuleDirectory) || SysSvContainsByte(context->ModuleDirectory, '\0'))
return NTLSTATUS_INVALID_PARAMETER;
size_t total = dirLength + (dirLength ? 1u : 0u) + canonical.Length;
if (total + 1u > SYS_PATH_MAX) return NTLSTATUS_BUFFER_TOO_SMALL;
if (dirLength) memcpy(out, context->ModuleDirectory.Data, dirLength);
size_t pos = dirLength;
if (dirLength) out[pos++] = '/';
memcpy(out + pos, canonical.Data, canonical.Length);
out[total] = '\0';
return NTLSTATUS_SUCCESS;
}
// Replaces the final component of an explicit module path with its canonical policy name.
static KSTATUS NtlpBuildCanonicalPath(SYS_STRING_VIEW path,
SYS_STRING_VIEW canonical,
@@ -246,23 +238,54 @@ static KSTATUS NtlpBuildCanonicalPath(SYS_STRING_VIEW path,
return NTLSTATUS_SUCCESS;
}
// Opens one canonical dependency through the caller-supplied module-root capability.
// Opens one canonical dependency from a single capability-relative search location.
static KSTATUS NtlpOpenDependencyAt(SYS_HANDLE rootHandle,
SYS_STRING_VIEW directory,
SYS_STRING_VIEW canonical,
SYS_HANDLE* fileOut) {
if (rootHandle == SYS_HANDLE_INVALID || !fileOut || !SysSvIsValid(directory) ||
SysSvContainsByte(directory, '\0'))
return NTLSTATUS_INVALID_PARAMETER;
*fileOut = SYS_HANDLE_INVALID;
char path[SYS_PATH_MAX];
char name[NTL_MODULE_NAME_MAX];
KSTATUS status = NtlpCopyName(canonical, name);
if (!KSUCCESS(status)) return status;
size_t dirLength = directory.Length;
size_t total = dirLength + (dirLength ? 1u : 0u) + canonical.Length;
if (total + 1u > sizeof(path)) return NTLSTATUS_BUFFER_TOO_SMALL;
if (dirLength) memcpy(path, directory.Data, dirLength);
size_t pos = dirLength;
if (dirLength) path[pos++] = '/';
memcpy(path + pos, canonical.Data, canonical.Length);
path[total] = '\0';
SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(rootHandle, SysSvFromCstr(path),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE, fileOut);
return SysOpenFileAt(&open);
}
// Searches the available current-directory root, then image-private and configured fallback roots.
static KSTATUS NtlpOpenDependency(NTL_MODULE_CONTEXT* context,
SYS_STRING_VIEW canonical,
SYS_HANDLE* fileOut) {
if (!context || context->ModuleRootHandle == SYS_HANDLE_INVALID || !fileOut)
return NTLSTATUS_INVALID_PARAMETER;
*fileOut = SYS_HANDLE_INVALID;
char path[SYS_PATH_MAX];
KSTATUS status = NtlpBuildModulePath(context, canonical, path);
if (!KSUCCESS(status)) return status;
SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(context->ModuleRootHandle,
SysSvFromCstr(path),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
fileOut);
return SysOpenFileAt(&open);
if (!context || !fileOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = NtlpOpenDependencyAt(context->PrimaryModuleRootHandle,
context->PrimaryModuleDirectory, canonical, fileOut);
if (status != NTLSTATUS_NOT_FOUND) return status;
if (context->SecondaryModuleRootHandle != SYS_HANDLE_INVALID) {
status = NtlpOpenDependencyAt(context->SecondaryModuleRootHandle,
context->SecondaryModuleDirectory, canonical, fileOut);
if (status != NTLSTATUS_NOT_FOUND) return status;
}
if (context->FallbackModuleRootHandle == SYS_HANDLE_INVALID) return NTLSTATUS_NOT_FOUND;
return NtlpOpenDependencyAt(context->FallbackModuleRootHandle,
context->FallbackModuleDirectory, canonical, fileOut);
}
// Publishes a mapped image before import binding so cyclic dependencies can resolve it.
@@ -356,14 +379,22 @@ static KSTATUS NtlpResolveImport(void* opaque,
// Creates loader state for a child process or seeds the current process from PEB loader data.
KSTATUS NtlpCreateModuleContext(SYS_HANDLE processHandle,
SYS_HANDLE moduleRootHandle,
SYS_STRING_VIEW moduleDirectory,
SYS_HANDLE primaryModuleRootHandle,
SYS_STRING_VIEW primaryModuleDirectory,
SYS_HANDLE secondaryModuleRootHandle,
SYS_STRING_VIEW secondaryModuleDirectory,
SYS_HANDLE fallbackModuleRootHandle,
SYS_STRING_VIEW fallbackModuleDirectory,
NTL_MODULE_NAME_RESOLVER resolver,
void* resolverContext,
int seedCurrentProcess,
NTL_MODULE_CONTEXT** contextOut) {
if (!contextOut || moduleRootHandle == SYS_HANDLE_INVALID || !SysSvIsValid(moduleDirectory) ||
SysSvContainsByte(moduleDirectory, '\0'))
if (!contextOut || primaryModuleRootHandle == SYS_HANDLE_INVALID ||
!SysSvIsValid(primaryModuleDirectory) || SysSvContainsByte(primaryModuleDirectory, '\0') ||
(secondaryModuleRootHandle != SYS_HANDLE_INVALID &&
(!SysSvIsValid(secondaryModuleDirectory) || SysSvContainsByte(secondaryModuleDirectory, '\0'))) ||
(fallbackModuleRootHandle != SYS_HANDLE_INVALID &&
(!SysSvIsValid(fallbackModuleDirectory) || SysSvContainsByte(fallbackModuleDirectory, '\0'))))
return NTLSTATUS_INVALID_PARAMETER;
*contextOut = 0;
@@ -372,8 +403,12 @@ KSTATUS NtlpCreateModuleContext(SYS_HANDLE processHandle,
if (!KSUCCESS(status)) return status;
context->Magic = NTL_MODULE_CONTEXT_MAGIC;
context->ProcessHandle = processHandle;
context->ModuleRootHandle = moduleRootHandle;
context->ModuleDirectory = moduleDirectory;
context->PrimaryModuleRootHandle = primaryModuleRootHandle;
context->PrimaryModuleDirectory = primaryModuleDirectory;
context->SecondaryModuleRootHandle = secondaryModuleRootHandle;
context->SecondaryModuleDirectory = secondaryModuleDirectory;
context->FallbackModuleRootHandle = fallbackModuleRootHandle;
context->FallbackModuleDirectory = fallbackModuleDirectory;
context->Resolver = resolver;
context->ResolverContext = resolverContext;
context->OwnsStorage = 1u;
@@ -495,9 +530,26 @@ KSTATUS NtlpLoadMainImage(NTL_MODULE_CONTEXT* context,
return NtlLoadPeImage(&load);
}
// Validates one caller-owned process string vector and returns its serialized string bytes.
static KSTATUS NtlpMeasureProcessVector(NTL_PROCESS_STRING_VECTOR vector, size_t* bytesOut) {
if (!bytesOut || (vector.Count && !vector.Entries)) return NTLSTATUS_INVALID_PARAMETER;
size_t bytes = 0;
for (uint32_t i = 0; i < vector.Count; ++i) {
SYS_STRING_VIEW entry = vector.Entries[i];
if (!SysSvIsValid(entry) || SysSvContainsByte(entry, '\0') || entry.Length > UINT32_MAX)
return NTLSTATUS_INVALID_PARAMETER;
if (entry.Length > SIZE_MAX - bytes - 1u) return NTLSTATUS_NO_MEMORY;
bytes += entry.Length + 1u;
}
*bytesOut = bytes;
return NTLSTATUS_SUCCESS;
}
// Serializes startup dependency metadata into a read-only child-owned VAD.
KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
SYS_STRING_VIEW currentDirectory,
NTL_PROCESS_STRING_VECTOR arguments,
NTL_PROCESS_STRING_VECTOR environment,
const void* startupData,
size_t startupDataSize,
void** loaderDataOut) {
@@ -510,10 +562,28 @@ KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
return NTLSTATUS_INVALID_PARAMETER;
*loaderDataOut = 0;
size_t argumentStringBytes = 0;
size_t environmentStringBytes = 0;
KSTATUS status = NtlpMeasureProcessVector(arguments, &argumentStringBytes);
if (!KSUCCESS(status)) return status;
status = NtlpMeasureProcessVector(environment, &environmentStringBytes);
if (!KSUCCESS(status)) return status;
size_t recordsBytes = (size_t)context->Count * sizeof(NTL_LOADER_IMAGE_RECORD);
if (context->Count && recordsBytes / sizeof(NTL_LOADER_IMAGE_RECORD) != context->Count)
return NTLSTATUS_NO_MEMORY;
size_t bytes = sizeof(NTL_LOADER_DATA) + recordsBytes;
size_t argumentRecordsBytes = (size_t)arguments.Count * sizeof(NTL_LOADER_STRING_RECORD);
size_t environmentRecordsBytes = (size_t)environment.Count * sizeof(NTL_LOADER_STRING_RECORD);
if ((arguments.Count && argumentRecordsBytes / sizeof(NTL_LOADER_STRING_RECORD) != arguments.Count) ||
(environment.Count && environmentRecordsBytes / sizeof(NTL_LOADER_STRING_RECORD) != environment.Count) ||
argumentRecordsBytes > SIZE_MAX - bytes)
return NTLSTATUS_NO_MEMORY;
size_t argumentVectorOffset = arguments.Count ? bytes : 0u;
bytes += argumentRecordsBytes;
if (environmentRecordsBytes > SIZE_MAX - bytes) return NTLSTATUS_NO_MEMORY;
size_t environmentVectorOffset = environment.Count ? bytes : 0u;
bytes += environmentRecordsBytes;
if (bytes < sizeof(NTL_LOADER_DATA) || currentDirectory.Length > SIZE_MAX - bytes - 1u)
return NTLSTATUS_NO_MEMORY;
size_t currentDirectoryOffset = bytes;
@@ -524,6 +594,12 @@ KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
startupDataOffset = bytes;
bytes += startupDataSize;
}
if (argumentStringBytes > SIZE_MAX - bytes) return NTLSTATUS_NO_MEMORY;
size_t argumentStringsOffset = bytes;
bytes += argumentStringBytes;
if (environmentStringBytes > SIZE_MAX - bytes) return NTLSTATUS_NO_MEMORY;
size_t environmentStringsOffset = bytes;
bytes += environmentStringBytes;
if (bytes > UINT32_MAX) return NTLSTATUS_NO_MEMORY;
if (bytes > SIZE_MAX - (NTL_MODULE_PAGE_SIZE - 1u)) return NTLSTATUS_NO_MEMORY;
size_t pages = (bytes + NTL_MODULE_PAGE_SIZE - 1u) / NTL_MODULE_PAGE_SIZE;
@@ -537,7 +613,7 @@ KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
alloc.Flags = SYS_VM_FLAG_NONE;
alloc.PageCount = pages;
alloc.BaseOut = &base;
KSTATUS status = SysAllocProcessVm(&alloc);
status = SysAllocProcessVm(&alloc);
if (!KSUCCESS(status)) return status;
NTL_LOADER_DATA* data = (NTL_LOADER_DATA*)base;
@@ -550,6 +626,10 @@ KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
data->CurrentDirectoryLength = (uint32_t)currentDirectory.Length;
data->StartupDataOffset = startupDataSize ? (uint32_t)startupDataOffset : 0u;
data->StartupDataLength = (uint32_t)startupDataSize;
data->ArgumentVectorOffset = arguments.Count ? (uint32_t)argumentVectorOffset : 0u;
data->ArgumentCount = arguments.Count;
data->EnvironmentVectorOffset = environment.Count ? (uint32_t)environmentVectorOffset : 0u;
data->EnvironmentCount = environment.Count;
uint32_t i = 0;
for (NTL_MODULE_ENTRY* entry = context->Head; entry; entry = entry->Next) {
NTL_LOADER_IMAGE_RECORD* image = &data->Images[i++];
@@ -564,16 +644,40 @@ KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
if (startupDataSize)
memcpy((char*)data + startupDataOffset, startupData, startupDataSize);
NTL_LOADER_STRING_RECORD* argumentRecords = arguments.Count
? (NTL_LOADER_STRING_RECORD*)((char*)data + argumentVectorOffset) : 0;
size_t stringOffset = argumentStringsOffset;
for (uint32_t argumentIndex = 0; argumentIndex < arguments.Count; ++argumentIndex) {
SYS_STRING_VIEW entry = arguments.Entries[argumentIndex];
argumentRecords[argumentIndex].Offset = (uint32_t)stringOffset;
argumentRecords[argumentIndex].Length = (uint32_t)entry.Length;
if (entry.Length) memcpy((char*)data + stringOffset, entry.Data, entry.Length);
((char*)data)[stringOffset + entry.Length] = '\0';
stringOffset += entry.Length + 1u;
}
NTL_LOADER_STRING_RECORD* environmentRecords = environment.Count
? (NTL_LOADER_STRING_RECORD*)((char*)data + environmentVectorOffset) : 0;
stringOffset = environmentStringsOffset;
for (uint32_t environmentIndex = 0; environmentIndex < environment.Count; ++environmentIndex) {
SYS_STRING_VIEW entry = environment.Entries[environmentIndex];
environmentRecords[environmentIndex].Offset = (uint32_t)stringOffset;
environmentRecords[environmentIndex].Length = (uint32_t)entry.Length;
if (entry.Length) memcpy((char*)data + stringOffset, entry.Data, entry.Length);
((char*)data)[stringOffset + entry.Length] = '\0';
stringOffset += entry.Length + 1u;
}
status = SysProtectProcessVm(context->ProcessHandle, base, pages, SYS_VM_READ);
if (!KSUCCESS(status)) return status;
*loaderDataOut = base;
return NTLSTATUS_SUCCESS;
}
// Copies the opaque userspace bootstrap payload from the current process loader block.
KSTATUS NtlGetProcessStartupData(void* buffer, size_t bufferSize, size_t* sizeOut) {
if (!sizeOut || (bufferSize && !buffer)) return NTLSTATUS_INVALID_PARAMETER;
*sizeOut = 0;
// Validates and returns the immutable loader block for the current process.
static KSTATUS NtlpGetCurrentLoaderData(const NTL_LOADER_DATA** dataOut) {
if (!dataOut) return NTLSTATUS_INVALID_PARAMETER;
*dataOut = 0;
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION || !peb->LoaderData)
@@ -595,8 +699,21 @@ KSTATUS NtlGetProcessStartupData(void* buffer, size_t bufferSize, size_t* sizeOu
const NTL_LOADER_DATA* data = (const NTL_LOADER_DATA*)peb->LoaderData;
if (data->Magic != NTL_LOADER_DATA_MAGIC || data->Version != NTL_LOADER_DATA_VERSION ||
data->Size < sizeof(*data) || data->Size > regionBytes - (dataBase - regionBase) ||
data->StartupDataLength > NTL_PROCESS_STARTUP_DATA_MAX ||
data->Size < sizeof(*data) || data->Size > regionBytes - (dataBase - regionBase))
return NTLSTATUS_FAILURE;
*dataOut = data;
return NTLSTATUS_SUCCESS;
}
// Copies the opaque userspace bootstrap payload from the current process loader block.
KSTATUS NtlGetProcessStartupData(void* buffer, size_t bufferSize, size_t* sizeOut) {
if (!sizeOut || (bufferSize && !buffer)) return NTLSTATUS_INVALID_PARAMETER;
*sizeOut = 0;
const NTL_LOADER_DATA* data = 0;
KSTATUS status = NtlpGetCurrentLoaderData(&data);
if (!KSUCCESS(status)) return status;
if (data->StartupDataLength > NTL_PROCESS_STARTUP_DATA_MAX ||
(data->StartupDataLength == 0 && data->StartupDataOffset != 0) ||
(data->StartupDataLength != 0 &&
(data->StartupDataOffset < sizeof(*data) || data->StartupDataOffset > data->Size ||
@@ -612,33 +729,85 @@ KSTATUS NtlGetProcessStartupData(void* buffer, size_t bufferSize, size_t* sizeOu
return NTLSTATUS_SUCCESS;
}
// Returns one loader string vector count after validating its descriptor table.
static KSTATUS NtlpGetProcessVectorCount(int environment, uint32_t* countOut) {
if (!countOut) return NTLSTATUS_INVALID_PARAMETER;
*countOut = 0;
const NTL_LOADER_DATA* data = 0;
KSTATUS status = NtlpGetCurrentLoaderData(&data);
if (!KSUCCESS(status)) return status;
uint32_t count = environment ? data->EnvironmentCount : data->ArgumentCount;
uint32_t offset = environment ? data->EnvironmentVectorOffset : data->ArgumentVectorOffset;
if ((count == 0 && offset != 0) || (count != 0 && offset == 0)) return NTLSTATUS_FAILURE;
size_t recordsBytes = (size_t)count * sizeof(NTL_LOADER_STRING_RECORD);
if (count && recordsBytes / sizeof(NTL_LOADER_STRING_RECORD) != count)
return NTLSTATUS_FAILURE;
if (offset > data->Size || recordsBytes > data->Size - offset)
return NTLSTATUS_FAILURE;
*countOut = count;
return NTLSTATUS_SUCCESS;
}
// Returns one immutable loader string vector entry.
static KSTATUS NtlpGetProcessVectorEntry(int environment, uint32_t index, SYS_STRING_VIEW* entryOut) {
if (!entryOut) return NTLSTATUS_INVALID_PARAMETER;
*entryOut = SYS_SV_NULL;
const NTL_LOADER_DATA* data = 0;
KSTATUS status = NtlpGetCurrentLoaderData(&data);
if (!KSUCCESS(status)) return status;
uint32_t count = environment ? data->EnvironmentCount : data->ArgumentCount;
uint32_t offset = environment ? data->EnvironmentVectorOffset : data->ArgumentVectorOffset;
if (index >= count || !count || !offset) return NTLSTATUS_NOT_FOUND;
size_t recordsBytes = (size_t)count * sizeof(NTL_LOADER_STRING_RECORD);
if (recordsBytes / sizeof(NTL_LOADER_STRING_RECORD) != count ||
offset > data->Size || recordsBytes > data->Size - offset)
return NTLSTATUS_FAILURE;
const NTL_LOADER_STRING_RECORD* records =
(const NTL_LOADER_STRING_RECORD*)((const char*)data + offset);
NTL_LOADER_STRING_RECORD record = records[index];
if (record.Offset >= data->Size || record.Length > data->Size - record.Offset ||
record.Length == data->Size - record.Offset)
return NTLSTATUS_FAILURE;
const char* string = (const char*)data + record.Offset;
SYS_STRING_VIEW view = SysSvFromParts(string, record.Length);
if (string[record.Length] != '\0' || SysSvContainsByte(view, '\0'))
return NTLSTATUS_FAILURE;
*entryOut = view;
return NTLSTATUS_SUCCESS;
}
// Returns the number of serialized process arguments.
KSTATUS NtlGetProcessArgumentCount(uint32_t* countOut) {
return NtlpGetProcessVectorCount(0, countOut);
}
// Returns one serialized process argument.
KSTATUS NtlGetProcessArgument(uint32_t index, SYS_STRING_VIEW* argumentOut) {
return NtlpGetProcessVectorEntry(0, index, argumentOut);
}
// Returns the number of serialized environment entries.
KSTATUS NtlGetProcessEnvironmentCount(uint32_t* countOut) {
return NtlpGetProcessVectorCount(1, countOut);
}
// Returns one serialized environment entry.
KSTATUS NtlGetProcessEnvironment(uint32_t index, SYS_STRING_VIEW* entryOut) {
return NtlpGetProcessVectorEntry(1, index, entryOut);
}
// Returns the canonical inherited resource CWD stored in this process's loader data.
KSTATUS NtlpGetLoaderCurrentDirectory(SYS_STRING_VIEW* currentDirectoryOut) {
if (!currentDirectoryOut) return NTLSTATUS_INVALID_PARAMETER;
*currentDirectoryOut = SysSvFromParts((const char*)0, 0);
*currentDirectoryOut = SYS_SV_NULL;
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION || !peb->LoaderData)
return NTLSTATUS_NOT_FOUND;
SYS_VM_REGION_INFO region;
memset(&region, 0, sizeof(region));
KSTATUS status = SysQueryVm((void*)peb->LoaderData, &region);
if (!KSUCCESS(status) || !region.Base || !region.PageCount ||
region.PageCount > SIZE_MAX / NTL_MODULE_PAGE_SIZE)
return NTLSTATUS_FAILURE;
size_t regionBytes = region.PageCount * NTL_MODULE_PAGE_SIZE;
uintptr_t regionBase = (uintptr_t)region.Base;
uintptr_t dataBase = (uintptr_t)peb->LoaderData;
if (dataBase < regionBase || dataBase - regionBase > regionBytes ||
regionBytes - (dataBase - regionBase) < sizeof(NTL_LOADER_DATA))
return NTLSTATUS_FAILURE;
const NTL_LOADER_DATA* data = (const NTL_LOADER_DATA*)peb->LoaderData;
if (data->Magic != NTL_LOADER_DATA_MAGIC || data->Version != NTL_LOADER_DATA_VERSION ||
data->Size < sizeof(*data) || data->Size > regionBytes - (dataBase - regionBase) ||
data->CurrentDirectoryOffset < sizeof(*data) ||
const NTL_LOADER_DATA* data = 0;
KSTATUS status = NtlpGetCurrentLoaderData(&data);
if (!KSUCCESS(status)) return status;
if (data->CurrentDirectoryOffset < sizeof(*data) ||
data->CurrentDirectoryLength == 0 ||
data->CurrentDirectoryOffset > data->Size ||
data->CurrentDirectoryLength > data->Size - data->CurrentDirectoryOffset ||
@@ -664,6 +833,8 @@ static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot,
if (_InterlockedCompareExchange(&gCurrentContextState, 1, 0) == 0) {
NTL_MODULE_CONTEXT* temporary = 0;
KSTATUS status = NtlpCreateModuleContext(SYS_HANDLE_INVALID, moduleRoot, moduleDirectory,
SYS_HANDLE_INVALID, SysSvFromParts((const char*)0, 0),
SYS_HANDLE_INVALID, SysSvFromParts((const char*)0, 0),
resolver, resolverContext, 1, &temporary);
if (!KSUCCESS(status)) {
_InterlockedExchange(&gCurrentContextState, 0);
@@ -679,8 +850,12 @@ static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot,
while (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) == 1) SysYield();
if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2)
return NTLSTATUS_FAILURE;
gCurrentContext.ModuleRootHandle = moduleRoot;
gCurrentContext.ModuleDirectory = moduleDirectory;
gCurrentContext.PrimaryModuleRootHandle = moduleRoot;
gCurrentContext.PrimaryModuleDirectory = moduleDirectory;
gCurrentContext.SecondaryModuleRootHandle = SYS_HANDLE_INVALID;
gCurrentContext.SecondaryModuleDirectory = SysSvFromParts((const char*)0, 0);
gCurrentContext.FallbackModuleRootHandle = SYS_HANDLE_INVALID;
gCurrentContext.FallbackModuleDirectory = SysSvFromParts((const char*)0, 0);
gCurrentContext.Resolver = resolver;
gCurrentContext.ResolverContext = resolverContext;
}