replace ad hoc driver loading with a proper driver manager, new DDK, new PnP manager with device matching, asynchronous startup, resource ownership and removal, add shared PCI capability helpers, replace typed, rigid services with a proper service manager, implement proper system shutdown and restart, shutting down unloads drivers and shuts down services, add simple power button, remove heavyweight boot self-tests and SMP stress runs, add monotonic timestamps to kernel debug output, fruitless USB and PS/2 fixes, make test builds opt in.

This commit is contained in:
maksw committed 2026-10-26 10:08:03 +01:00
1 parent c0ad63c27f
commit d36f19509c
160 files changed
+6563 -7366

No files matched your search

+11 -10
View File
@@ -29,6 +29,7 @@ int main(void) {
NTL_CONSOLE_INFO console;
SYS_MEMORY_STATUS mem;
char metricsBuf[128];
size_t previousLength = 0;
while (true) {
if (NtlConsoleQueryInfo(SYS_HANDLE_INVALID, &console) == NTLSTATUS_SUCCESS &&
@@ -52,22 +53,22 @@ int main(void) {
strcat(metricsBuf, "K ]");
size_t textLen = strlen(metricsBuf);
// Erase any trailing characters left by a longer previous sample.
if (previousLength > textLen && previousLength < sizeof(metricsBuf)) {
memset(metricsBuf + textLen, ' ', previousLength - textLen);
textLen = previousLength;
}
int targetX = columns - (int)textLen;
int targetY = 0;
if (targetX >= 0) {
size_t written = 0;
// Loop through the string and use the console's dedicated text filling API
// This utilizes the console's native font configuration instead of drawing raw screen bits
for (size_t i = 0; i < textLen; i++) {
(void)NtlConsoleFillCharacter(SYS_HANDLE_INVALID, metricsBuf[i],
targetX + (int)i, targetY, 1, &written);
}
if (NtlConsoleWriteAt(SYS_HANDLE_INVALID, targetX, 0,
metricsBuf, textLen, &written) == NTLSTATUS_SUCCESS &&
written == textLen)
previousLength = textLen;
}
}
SysSleep(500);
SysSleep(2000);
}
return 0;
}
@@ -863,6 +863,8 @@ static void cmd_help() {
" del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n"
" clear - Clear the screen\n"
" shutdown - Request system shutdown\n"
" reboot - Request system restart\n"
" exit - Close the shell\n");
}
@@ -2203,6 +2205,13 @@ static KSTATUS waitForegroundProcess(uint32_t hProc) {
// Run one command.
static bool runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return false;
if (strcmp(cmd, "shutdown") == 0 || strcmp(cmd, "reboot") == 0) {
uint32_t action = strcmp(cmd, "reboot") == 0
? SYS_POWER_ACTION_RESTART : SYS_POWER_ACTION_SHUTDOWN;
KSTATUS st = SysRequestSystemPower(action);
if (!KSUCCESS(st)) print("power: request failed\n");
return false;
}
if (strcmp(cmd, "exit") == 0) return true;
if (strcmp(cmd, "help") == 0) { cmd_help(); return false; }
if (strcmp(cmd, "clear") == 0) { (void)NtlConsoleClear(SYS_HANDLE_INVALID, 0x012456); return false; }
+10
View File
@@ -11,6 +11,7 @@
#include <spawn.h>
#include <sys/wait.h>
#include <ntlite/posix_extensions.h>
#include <ntlite/syscall.h>
#define MAX_CMD 256
@@ -80,6 +81,8 @@ static void cmd_help() {
" del <file> - Delete a file\n"
" write <file> <txt> - Create a file with text\n"
" clear - Clear the screen\n"
" shutdown - Request system shutdown\n"
" reboot - Request system restart\n"
" exit - Close the shell\n");
}
@@ -196,6 +199,13 @@ static void cmd_type(const char* filename) {
// Runs command.
static bool runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return false;
if (strcmp(cmd, "shutdown") == 0 || strcmp(cmd, "reboot") == 0) {
uint32_t action = strcmp(cmd, "reboot") == 0
? SYS_POWER_ACTION_RESTART : SYS_POWER_ACTION_SHUTDOWN;
KSTATUS st = SysRequestSystemPower(action);
if (!KSUCCESS(st)) print("power: request failed\n");
return false;
}
if (strcmp(cmd, "exit") == 0) return true;
if (strcmp(cmd, "help") == 0) { cmd_help(); return false; }
if (strcmp(cmd, "clear") == 0) { clearscreen(); return false; }
+10
View File
@@ -1,6 +1,7 @@
// User application entry points for shell_win32.
#include <windows.h>
#include <ntlite/syscall.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
@@ -76,6 +77,8 @@ static void cmd_help() {
" del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n"
" clear - Clear the screen\n"
" shutdown - Request system shutdown\n"
" reboot - Request system restart\n"
" exit - Close the shell\n");
}
@@ -211,6 +214,13 @@ static void cmd_type(const char* filename) {
// Runs command.
static bool runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return false;
if (strcmp(cmd, "shutdown") == 0 || strcmp(cmd, "reboot") == 0) {
uint32_t action = strcmp(cmd, "reboot") == 0
? SYS_POWER_ACTION_RESTART : SYS_POWER_ACTION_SHUTDOWN;
KSTATUS st = SysRequestSystemPower(action);
if (!KSUCCESS(st)) print("power: request failed\n");
return false;
}
if (strcmp(cmd, "exit") == 0) return true;
if (strcmp(cmd, "help") == 0) { cmd_help(); return false; }
if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return false; }
+1 -4
View File
@@ -247,8 +247,7 @@ static KSTATUS PosixProcessDeriveSecurityContext(SYS_HANDLE* contextOut) {
info.Size = sizeof(info);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info);
if (!KSUCCESS(status)) return status;
if ((info.EffectivePrivileges & ~info.DelegablePrivileges) != 0 ||
(info.ServiceClaims & ~info.DelegableServiceClaims) != 0)
if ((info.EffectivePrivileges & ~info.DelegablePrivileges) != 0)
return NTLSTATUS_ACCESS_DENIED;
SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive;
@@ -257,8 +256,6 @@ static KSTATUS PosixProcessDeriveSecurityContext(SYS_HANDLE* contextOut) {
derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
derive.EffectivePrivileges = info.EffectivePrivileges;
derive.DelegablePrivileges = info.DelegablePrivileges;
derive.ServiceClaims = info.ServiceClaims;
derive.DelegableServiceClaims = info.DelegableServiceClaims;
derive.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
derive.ContextHandleOut = contextOut;
return SysDeriveSecurityContext(&derive);
+21
View File
@@ -152,6 +152,27 @@ static KSTATUS NtlConsoleSimpleCall(SYS_HANDLE session, NTL_CONSOLE_REQUEST* req
return status;
}
// Sends one positioned text span in a single console request.
KSTATUS NtlConsoleWriteAt(SYS_HANDLE session, int32_t x, int32_t y,
const char* text, size_t length, size_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
if (!text || length > NTL_CONSOLE_WRITE_MAX - 8u || x < 0 || y < 0)
return NTLSTATUS_INVALID_PARAMETER;
NTL_CONSOLE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_CONSOLE_PROTOCOL_VERSION;
request.Operation = NTL_CONSOLE_OP_WRITE_AT;
request.Data.WriteAt.X = x;
request.Data.WriteAt.Y = y;
request.Data.WriteAt.Length = (uint32_t)length;
memcpy(request.Data.WriteAt.Data, text, length);
NTL_CONSOLE_REPLY reply;
KSTATUS status = NtlConsoleCall(session, &request, &reply);
if (writtenOut) *writtenOut = reply.Written;
return status;
}
// Updates the logical console cursor.
KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int32_t x, int32_t y) {
NTL_CONSOLE_REQUEST request;
+3 -3
View File
@@ -219,12 +219,12 @@ KSTATUS NtlPrepareProcess(const NTL_PREPARE_PROCESS_REQUEST* request,
memset(state->ChildCapabilities, 0, sizeof(state->ChildCapabilities));
for (uint32_t i = 0; i < request->StartupCapabilityCount; ++i) {
const NTL_PROCESS_STARTUP_CAPABILITY* source = &request->StartupCapabilities[i];
if (!source->Tag || source->SourceHandle == SYS_HANDLE_INVALID) {
if (!SysStartupCapabilityNameValid(source->Name) || source->SourceHandle == SYS_HANDLE_INVALID) {
status = NTLSTATUS_INVALID_PARAMETER;
goto fail;
}
for (uint32_t j = 0; j < i; ++j) {
if (state->ChildCapabilities[j].Tag == source->Tag) {
if (strcmp(state->ChildCapabilities[j].Name, source->Name) == 0) {
status = NTLSTATUS_INVALID_PARAMETER;
goto fail;
}
@@ -240,7 +240,7 @@ KSTATUS NtlPrepareProcess(const NTL_PREPARE_PROCESS_REQUEST* request,
duplicate.TargetHandleOut = &childHandle;
status = SysDuplicateHandle(&duplicate);
if (!KSUCCESS(status)) goto fail;
state->ChildCapabilities[i].Tag = source->Tag;
memcpy(state->ChildCapabilities[i].Name, source->Name, sizeof(source->Name));
state->ChildCapabilities[i].Handle = childHandle;
}
state->StartupCapabilityCount = request->StartupCapabilityCount;
+27
View File
@@ -160,6 +160,33 @@ KSTATUS NtlResourceConnectListener(SYS_HANDLE listenerHandle, SYS_HANDLE* sessio
return SysConnectPort(&request);
}
// Delegates the authority to bind or unbind exactly one resource path.
KSTATUS NtlResourceCreatePublisher(SYS_HANDLE adminSession, SYS_STRING_VIEW path,
SYS_HANDLE* listenerOut) {
if (!listenerOut) return NTLSTATUS_INVALID_PARAMETER;
*listenerOut = SYS_HANDLE_INVALID;
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
uint32_t length = 0;
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload),
NTL_RESOURCE_OP_CREATE_PUBLISHER, path,
SysSvFromParts((const char*)0, 0), 0, 0,
NTL_RESOURCE_KIND_INVALID, &length);
if (!KSUCCESS(status)) return status;
NTL_RESOURCE_REPLY reply;
SYS_HANDLE handle = SYS_HANDLE_INVALID;
status = NtlCallResource(adminSession, payload, length, (const SYS_PORT_HANDLE_TRANSFER*)0,
0, &reply, &handle);
if (!KSUCCESS(status)) return status;
if (reply.Operation != NTL_RESOURCE_OP_CREATE_PUBLISHER ||
!KSUCCESS(reply.Status) || handle == SYS_HANDLE_INVALID) {
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
return reply.Operation == NTL_RESOURCE_OP_CREATE_PUBLISHER && !KSUCCESS(reply.Status)
? reply.Status : NTLSTATUS_FAILURE;
}
*listenerOut = handle;
return NTLSTATUS_SUCCESS;
}
// Looks up one resource and requests an attenuated alias of its bound capability.
KSTATUS NtlResourceLookup(SYS_HANDLE session,
SYS_STRING_VIEW path,
+6 -1
View File
@@ -186,4 +186,9 @@ SYS_SET_SOCKET_OPTION EQU 159
SYS_GET_SOCKET_OPTION EQU 160
SYS_POLL EQU 161
SYS_MAX EQU 162
SYS_OPEN_POWER_EVENT EQU 162
SYS_REQUEST_SYSTEM_POWER EQU 163
SYS_QUERY_SYSTEM_POWER EQU 164
SYS_COMPLETE_SYSTEM_POWER EQU 165
SYS_MAX EQU 166
+4
View File
@@ -38,6 +38,10 @@ ENDM
; ---------------------------------------------------------------------------
; Process & thread lifecycle
; ---------------------------------------------------------------------------
SYSCALL_STUB OpenPowerEvent, SYS_OPEN_POWER_EVENT
SYSCALL_STUB RequestSystemPower, SYS_REQUEST_SYSTEM_POWER
SYSCALL_STUB QuerySystemPower, SYS_QUERY_SYSTEM_POWER
SYSCALL_STUB CompleteSystemPower, SYS_COMPLETE_SYSTEM_POWER
SYSCALL_STUB ExitProcess, SYS_EXIT_PROCESS
SYSCALL_STUB ExitThread, SYS_EXIT_THREAD
SYSCALL_STUB CreateProcessObject, SYS_CREATE_PROCESS_OBJECT
+22 -9
View File
@@ -1,10 +1,12 @@
// Native Session 0 desktop/window manager. DWM owns window policy, composition, and visible cursor presentation.
#include <stdint.h>
#include <ntlite/bootstrap_capabilities.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <ntlite/desktop.h>
#include <ntlite/resource.h>
#include <ntlite/exception.h>
#include <ntlite/internal/session_control.h>
#include <ntlite/port.h>
@@ -1156,10 +1158,11 @@ static KSTATUS DwmSendReady(SYS_HANDLE endpoint, SYS_HANDLE desktopListener) {
message.Operation = NTL_SERVICE_CONTROL_READY;
message.ExportCount = 1u;
message.Status = NTLSTATUS_SUCCESS;
if (!NtlServiceControlNameExport(&message, 0u, "desktop")) return NTLSTATUS_FAILURE;
SYS_PORT_HANDLE_TRANSFER transfer;
transfer.Handle = desktopListener;
transfer.DesiredRights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.DesiredRights = SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_SEND_PORT_REQUEST send;
@@ -1196,8 +1199,7 @@ static void DwmControlLoop(SYS_HANDLE endpoint) {
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) return;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(message) || message.Size != sizeof(message) ||
message.Version != NTL_SERVICE_CONTROL_VERSION || message.ExportCount != 0 ||
message.Reserved[0] != 0 || message.Reserved[1] != 0 || message.Reserved[2] != 0)
!NtlServiceControlValid(&message, NTL_SERVICE_CONTROL_STOP, 0u))
continue;
if (message.Operation == NTL_SERVICE_CONTROL_STOP) return;
}
@@ -1255,15 +1257,15 @@ void _start(void) {
memset(&security, 0, sizeof(security));
security.Size = sizeof(security);
status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security);
if (!KSUCCESS(status) || security.EffectivePrivileges != 0 || security.DelegablePrivileges != 0 ||
security.ServiceClaims != 0 || security.DelegableServiceClaims != 0)
if (!KSUCCESS(status) || security.EffectivePrivileges != 0 || security.DelegablePrivileges != 0)
DwmExit(2);
SYS_HANDLE controlListener = SysGetStartupCapability(SYS_STARTUP_CAP_SERVICE_CONTROL);
SYS_HANDLE displayManager = SysGetStartupCapability(SYS_STARTUP_CAP_DISPLAY_MANAGER);
SYS_HANDLE sessionListener = SysGetStartupCapability(SYS_STARTUP_CAP_SESSION_CONTROL);
SYS_HANDLE controlListener = SysGetStartupCapability("service.control");
SYS_HANDLE displayManager = SysGetStartupCapability(NTL_BOOT_CAP_DISPLAY_PRESENT);
SYS_HANDLE sessionListener = SysGetStartupCapability("rssrv.session-control");
SYS_HANDLE publicationListener = SysGetStartupCapability("rssrv.desktop-publisher");
if (controlListener == SYS_HANDLE_INVALID || displayManager == SYS_HANDLE_INVALID ||
sessionListener == SYS_HANDLE_INVALID)
sessionListener == SYS_HANDLE_INVALID || publicationListener == SYS_HANDLE_INVALID)
DwmExit(3);
memset(&gDwm, 0, sizeof(gDwm));
@@ -1322,10 +1324,21 @@ void _start(void) {
DwmUnlock();
}
SYS_HANDLE publication = SYS_HANDLE_INVALID;
status = NtlResourceConnectListener(publicationListener, &publication);
(void)SysCloseHandle(publicationListener);
if (!KSUCCESS(status)) DwmExit(8);
status = NtlResourceBind(publication, SYS_SV_LITERAL(NTL_DESKTOP_SERVICE_PATH),
NTL_RESOURCE_KIND_CAPABILITY, gDwm.DesktopListener,
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) DwmExit(8);
status = DwmSendReady(controlEndpoint, gDwm.DesktopListener);
if (!KSUCCESS(status)) DwmExit(8);
DwmControlLoop(controlEndpoint);
(void)NtlResourceUnbind(publication, SYS_SV_LITERAL(NTL_DESKTOP_SERVICE_PATH));
(void)NtlResourceClose(publication);
gDwm.Running = 0u;
(void)SysSignalEvent(gDwm.DirtyEvent);
(void)SysDisconnectPort(gDwm.SessionEndpoint);
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
// Service restart budget: three automatic attempts in any sliding five-minute interval.
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef struct INIT_RESTART_BUDGET {
uint64_t Attempts[3];
uint32_t Count;
} INIT_RESTART_BUDGET;
static inline bool InitConsumeRestartBudget(INIT_RESTART_BUDGET* budget,
uint64_t now, uint64_t frequency) {
if (!budget || !frequency || budget->Count > 3u ||
frequency > UINT64_MAX / 300u) return false;
uint64_t window = frequency * 300u;
uint32_t keep = 0;
for (uint32_t i = 0; i < budget->Count; ++i) {
uint64_t started = budget->Attempts[i];
if (now >= started && now - started < window)
budget->Attempts[keep++] = started;
}
budget->Count = keep;
if (keep == 3u) return false;
budget->Attempts[budget->Count++] = now;
return true;
}
+128 -61
View File
@@ -1,12 +1,12 @@
// Init service-configuration parser. Malformed or ambiguous service policy is rejected at boot.
#include "service_config.h"
#include <ntlite/bootstrap_capabilities.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/status.h>
#define INIT_SERVICE_KEY_TYPE 0x00000001u
#define INIT_SERVICE_KEY_IMAGE 0x00000002u
#define INIT_SERVICE_KEY_START 0x00000004u
#define INIT_SERVICE_KEY_CRITICAL 0x00000008u
@@ -14,7 +14,9 @@
#define INIT_SERVICE_KEY_READY_TIMEOUT 0x00000020u
#define INIT_SERVICE_KEY_STOP_TIMEOUT 0x00000040u
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
#define INIT_SERVICE_REQUIRED_KEYS 0x000000FFu
#define INIT_SERVICE_KEY_GRANTS 0x00000100u
#define INIT_SERVICE_KEY_EXPORTS 0x00000200u
#define INIT_SERVICE_REQUIRED_KEYS 0x000003FEu
typedef struct INIT_PARSED_SERVICE {
INIT_SERVICE_DESCRIPTOR Descriptor;
@@ -155,17 +157,65 @@ static bool parse_requires(char* value, INIT_PARSED_SERVICE* service) {
return true;
}
// Parses explicit capability-source names without associating them with service roles.
static bool parse_grants(char* value, INIT_PARSED_SERVICE* service) {
service->Descriptor.GrantCount = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* name = trim_left(cursor);
trim_right(name);
size_t length = strlen(name);
if (length == 0 || length > INIT_SERVICE_GRANT_NAME_MAX ||
service->Descriptor.GrantCount >= INIT_SERVICE_GRANT_MAX) return false;
for (size_t i = 0; i < length; ++i)
if (!is_service_id_char(name[i])) return false;
if (!strchr(name, '.') || name[0] == '.' || name[length - 1u] == '.') return false;
if (strcmp(name, "service.control") == 0 || strncmp(name, "publish.", 8u) == 0)
return false;
for (uint32_t i = 0; i < service->Descriptor.GrantCount; ++i)
if (strcmp(service->Descriptor.Grants[i], name) == 0) return false;
if (!copy_bounded(service->Descriptor.Grants[service->Descriptor.GrantCount],
sizeof(service->Descriptor.Grants[0]), name)) return false;
++service->Descriptor.GrantCount;
if (!comma) break;
cursor = comma + 1;
if (!*cursor) return false;
}
return true;
}
// Reads the declared READY export names, which are an exact startup contract.
static bool parse_exports(char* value, INIT_PARSED_SERVICE* service) {
service->Descriptor.ExportCount = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* name = trim_left(cursor);
trim_right(name);
if (!NtlServiceExportNameValid(name) ||
service->Descriptor.ExportCount >= INIT_SERVICE_EXPORT_MAX) return false;
for (uint32_t i = 0; i < service->Descriptor.ExportCount; ++i)
if (strcmp(name, service->Descriptor.Exports[i]) == 0) return false;
if (!copy_bounded(service->Descriptor.Exports[service->Descriptor.ExportCount],
sizeof(service->Descriptor.Exports[0]), name)) return false;
++service->Descriptor.ExportCount;
if (!comma) break;
cursor = comma + 1;
if (!*cursor) return false;
}
return true;
}
// Applies one known key to the current service and rejects duplicates or invalid values.
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
uint32_t bit = 0;
if (strcmp(key, "type") == 0) {
bit = INIT_SERVICE_KEY_TYPE;
if (strcmp(value, "resource") == 0) service->Descriptor.Type = InitServiceTypeResource;
else if (strcmp(value, "dwm") == 0) service->Descriptor.Type = InitServiceTypeDwm;
else if (strcmp(value, "generic") == 0) service->Descriptor.Type = InitServiceTypeGeneric;
else return false;
} else if (strcmp(key, "image") == 0) {
if (strcmp(key, "image") == 0) {
bit = INIT_SERVICE_KEY_IMAGE;
if (!validate_image_path(value) ||
!copy_bounded(service->Descriptor.ImagePath, sizeof(service->Descriptor.ImagePath), value))
@@ -192,6 +242,12 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
} else if (strcmp(key, "requires") == 0) {
bit = INIT_SERVICE_KEY_REQUIRES;
if (!parse_requires(value, service)) return false;
} else if (strcmp(key, "grants") == 0) {
bit = INIT_SERVICE_KEY_GRANTS;
if (!parse_grants(value, service)) return false;
} else if (strcmp(key, "exports") == 0) {
bit = INIT_SERVICE_KEY_EXPORTS;
if (!parse_exports(value, service)) return false;
} else {
return false;
}
@@ -270,7 +326,8 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
line = saved ? end + 1u : end;
}
if (serviceCount == 0) return NTLSTATUS_INVALID_PARAMETER;
if (serviceCount == 0)
return NTLSTATUS_INVALID_PARAMETER;
*serviceCountOut = serviceCount;
return NTLSTATUS_SUCCESS;
}
@@ -279,14 +336,59 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
static KSTATUS validate_services(uint32_t serviceCount,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut) {
uint32_t resourceCount = 0;
uint16_t resourceIndex = 0;
uint32_t dwmCount = 0;
uint16_t dwmIndex = UINT16_MAX;
memset(configOut, 0, sizeof(*configOut));
configOut->ResourceServiceIndex = UINT16_MAX;
configOut->DwmServiceIndex = UINT16_MAX;
static const char* const bootGrants[] = {
NTL_BOOT_CAP_NAMESPACE_ROOT, NTL_BOOT_CAP_VOLUME_MANAGER, NTL_BOOT_CAP_INPUT_MANAGER,
NTL_BOOT_CAP_DISPLAY_POLICY, NTL_BOOT_CAP_DISPLAY_PRESENT, NTL_BOOT_CAP_RESOURCE_REGISTRATION
};
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* descriptor = &gParsedServices[i].Descriptor;
for (uint32_t g = 0; g < descriptor->GrantCount; ++g) {
const char* name = descriptor->Grants[g];
bool known = false;
for (uint32_t b = 0; b < sizeof(bootGrants) / sizeof(bootGrants[0]); ++b)
if (strcmp(name, bootGrants[b]) == 0) known = true;
if (known) continue;
if (strncmp(name, "boot.", 5u) == 0) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
// Service export sources require an explicit dependency; the longest
// matching service ID owns a qualified export name.
int32_t provider = -1;
size_t prefixLength = 0;
for (uint32_t j = 0; j < serviceCount; ++j) {
const char* id = gParsedServices[j].Descriptor.Id;
size_t length = strlen(id);
if (length > prefixLength && strncmp(name, id, length) == 0 &&
name[length] == '.' && name[length + 1u] != '\0') {
provider = (int32_t)j;
prefixLength = length;
}
}
bool declaredDependency = false;
if (provider >= 0 && (uint32_t)provider != i &&
gParsedServices[provider].Descriptor.StartPolicy == InitServiceStartAuto) {
for (uint32_t d = 0; d < gParsedServices[i].DependencyCount; ++d)
if (strcmp(gParsedServices[i].DependencyIds[d],
gParsedServices[provider].Descriptor.Id) == 0)
declaredDependency = true;
}
if (declaredDependency) {
const char* exportName = name + prefixLength + 1u;
bool declaredExport = false;
const INIT_SERVICE_DESCRIPTOR* source = &gParsedServices[provider].Descriptor;
for (uint32_t e = 0; e < source->ExportCount; ++e)
if (strcmp(source->Exports[e], exportName) == 0) declaredExport = true;
if (!declaredExport) declaredDependency = false;
}
if (!declaredDependency) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
}
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_PARSED_SERVICE* parsed = &gParsedServices[i];
@@ -297,6 +399,13 @@ static KSTATUS validate_services(uint32_t serviceCount,
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
*descriptor = parsed->Descriptor;
for (uint32_t e = 0; e < descriptor->ExportCount; ++e) {
if (strlen(descriptor->Id) + 1u + strlen(descriptor->Exports[e]) >
INIT_SERVICE_GRANT_NAME_MAX) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_NAME_TOO_LONG;
}
}
descriptor->DependencyCount = parsed->DependencyCount;
for (uint32_t d = 0; d < parsed->DependencyCount; ++d) {
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->DependencyIds[d]);
@@ -306,48 +415,6 @@ static KSTATUS validate_services(uint32_t serviceCount,
}
descriptor->Dependencies[d] = (uint16_t)dependency;
}
if (descriptor->Type == InitServiceTypeResource) {
++resourceCount;
resourceIndex = (uint16_t)i;
} else if (descriptor->Type == InitServiceTypeDwm) {
++dwmCount;
dwmIndex = (uint16_t)i;
}
}
if (resourceCount != 1u || dwmCount > 1u) return NTLSTATUS_INVALID_PARAMETER;
INIT_SERVICE_DESCRIPTOR* resource = &configOut->Services[resourceIndex];
if (resource->StartPolicy != InitServiceStartAuto || !resource->Critical ||
resource->RestartPolicy != InitServiceRestartNever || resource->DependencyCount != 0u) {
if (errorLineOut) *errorLineOut = gParsedServices[resourceIndex].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
if (dwmCount == 1u) {
INIT_SERVICE_DESCRIPTOR* dwm = &configOut->Services[dwmIndex];
bool requiresResource = false;
for (uint32_t d = 0; d < dwm->DependencyCount; ++d)
if (dwm->Dependencies[d] == resourceIndex) requiresResource = true;
if (dwm->StartPolicy == InitServiceStartAuto &&
(dwm->Critical || dwm->RestartPolicy != InitServiceRestartNever || !requiresResource)) {
if (errorLineOut) *errorLineOut = gParsedServices[dwmIndex].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
// The generic adapter publishes READY listeners through RSSRV, so automatic generic services require it.
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* generic = &configOut->Services[i];
if (generic->Type != InitServiceTypeGeneric || generic->StartPolicy != InitServiceStartAuto) continue;
bool requiresResource = false;
for (uint32_t d = 0; d < generic->DependencyCount; ++d)
if (generic->Dependencies[d] == resourceIndex) requiresResource = true;
if (generic->Critical || generic->RestartPolicy != InitServiceRestartNever || !requiresResource) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
for (uint32_t i = 0; i < serviceCount; ++i) {
@@ -406,8 +473,8 @@ static KSTATUS validate_services(uint32_t serviceCount,
}
configOut->ServiceCount = serviceCount;
configOut->ResourceServiceIndex = resourceIndex;
configOut->DwmServiceIndex = dwmIndex;
if (configOut->StartupCount + 2u > SYS_WAIT_MANY_MAX_HANDLES)
return NTLSTATUS_LIMIT_REACHED;
return NTLSTATUS_SUCCESS;
}
+8 -9
View File
@@ -5,18 +5,16 @@
#include <stdbool.h>
#include <stdint.h>
#include <ntlite/syscall.h>
#include <ntlite/service_control.h>
#define INIT_SERVICES_PATH "system/services.ini"
#define INIT_SERVICES_MAX_FILE_BYTES (64u * 1024u)
#define INIT_SERVICE_MAX_COUNT 32u
#define INIT_SERVICE_ID_MAX 31u
#define INIT_SERVICE_DEPENDENCY_MAX 8u
typedef enum INIT_SERVICE_TYPE {
InitServiceTypeResource = 0,
InitServiceTypeDwm,
InitServiceTypeGeneric,
} INIT_SERVICE_TYPE;
#define INIT_SERVICE_GRANT_MAX 8u
#define INIT_SERVICE_GRANT_NAME_MAX SYS_STARTUP_CAPABILITY_NAME_MAX
#define INIT_SERVICE_EXPORT_MAX 8u
typedef enum INIT_SERVICE_START_POLICY {
InitServiceStartAuto = 0,
@@ -31,7 +29,6 @@ typedef enum INIT_SERVICE_RESTART_POLICY {
typedef struct INIT_SERVICE_DESCRIPTOR {
char Id[INIT_SERVICE_ID_MAX + 1u];
char ImagePath[SYS_PATH_MAX];
INIT_SERVICE_TYPE Type;
INIT_SERVICE_START_POLICY StartPolicy;
bool Critical;
INIT_SERVICE_RESTART_POLICY RestartPolicy;
@@ -39,6 +36,10 @@ typedef struct INIT_SERVICE_DESCRIPTOR {
uint32_t StopTimeoutMilliseconds;
uint32_t DependencyCount;
uint16_t Dependencies[INIT_SERVICE_DEPENDENCY_MAX];
uint32_t GrantCount;
char Grants[INIT_SERVICE_GRANT_MAX][INIT_SERVICE_GRANT_NAME_MAX + 1u];
uint32_t ExportCount;
char Exports[INIT_SERVICE_EXPORT_MAX][NTL_SERVICE_EXPORT_NAME_MAX + 1u];
} INIT_SERVICE_DESCRIPTOR;
typedef struct INIT_SERVICE_CONFIG {
@@ -46,8 +47,6 @@ typedef struct INIT_SERVICE_CONFIG {
uint16_t StartupOrder[INIT_SERVICE_MAX_COUNT];
uint32_t ServiceCount;
uint32_t StartupCount;
uint16_t ResourceServiceIndex;
uint16_t DwmServiceIndex;
} INIT_SERVICE_CONFIG;
// Loads, strictly parses, and validates services.ini beneath the bootstrap system volume.
+18 -7
View File
@@ -1682,8 +1682,7 @@ static KSTATUS PosixsrvValidateSecurity(void) {
security.Size = sizeof(security);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security);
if (!KSUCCESS(status)) return status;
if (security.EffectivePrivileges != 0 || security.DelegablePrivileges != 0 ||
security.ServiceClaims != 0 || security.DelegableServiceClaims != 0)
if (security.EffectivePrivileges != 0 || security.DelegablePrivileges != 0)
return NTLSTATUS_ACCESS_DENIED;
return NTLSTATUS_SUCCESS;
}
@@ -5081,11 +5080,12 @@ static KSTATUS PosixsrvSendReady(SYS_HANDLE controlEndpoint, SYS_HANDLE publicLi
message.Operation = NTL_SERVICE_CONTROL_READY;
message.ExportCount = 1u;
message.Status = NTLSTATUS_SUCCESS;
if (!NtlServiceControlNameExport(&message, 0u, "public")) return NTLSTATUS_FAILURE;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = publicListener;
transfer.DesiredRights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.DesiredRights = SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_SEND_PORT_REQUEST send;
@@ -5122,8 +5122,7 @@ static void PosixsrvControlLoop(SYS_HANDLE endpoint) {
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) return;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(message) || message.Size != sizeof(message) ||
message.Version != NTL_SERVICE_CONTROL_VERSION || message.ExportCount != 0 ||
message.Reserved[0] != 0 || message.Reserved[1] != 0 || message.Reserved[2] != 0)
!NtlServiceControlValid(&message, NTL_SERVICE_CONTROL_STOP, 0u))
continue;
if (message.Operation == NTL_SERVICE_CONTROL_STOP) return;
}
@@ -5141,8 +5140,9 @@ void _start(void) {
status = SysCreateMutex(&gProcessRegistryMutex);
if (!KSUCCESS(status)) PosixsrvExit(3);
SYS_HANDLE controlListener = SysGetStartupCapability(SYS_STARTUP_CAP_SERVICE_CONTROL);
if (controlListener == SYS_HANDLE_INVALID) PosixsrvExit(4);
SYS_HANDLE controlListener = SysGetStartupCapability("service.control");
SYS_HANDLE publicationListener = SysGetStartupCapability("rssrv.posix-publisher");
if (controlListener == SYS_HANDLE_INVALID || publicationListener == SYS_HANDLE_INVALID) PosixsrvExit(4);
SYS_HANDLE publicListener = SYS_HANDLE_INVALID;
status = PosixsrvCreateListener(&publicListener);
@@ -5163,10 +5163,21 @@ void _start(void) {
status = SysCreateThread(&create);
if (!KSUCCESS(status)) PosixsrvExit(7);
SYS_HANDLE publication = SYS_HANDLE_INVALID;
status = NtlResourceConnectListener(publicationListener, &publication);
(void)SysCloseHandle(publicationListener);
if (!KSUCCESS(status)) PosixsrvExit(8);
status = NtlResourceBind(publication, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH),
NTL_RESOURCE_KIND_CAPABILITY, publicListener,
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) PosixsrvExit(8);
status = PosixsrvSendReady(controlEndpoint, publicListener);
if (!KSUCCESS(status)) PosixsrvExit(8);
PosixsrvControlLoop(controlEndpoint);
(void)NtlResourceUnbind(publication, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH));
(void)NtlResourceClose(publication);
(void)SysDisconnectPort(controlEndpoint);
(void)SysCloseHandle(controlEndpoint);
(void)SysCloseHandle(publicListener);
+43
View File
@@ -112,6 +112,37 @@ static KSTATUS RssrvConsoleWrite(RSSRV_CONSOLE_STATE* console,
return status;
}
// Writes one positioned status line, retaining the console cursor under the
// same RSSRV session lock so ordinary writers keep their cursor position.
static KSTATUS RssrvConsoleWriteAt(RSSRV_CONSOLE_STATE* console,
int32_t x, int32_t y, const char* data,
uint32_t length, uint32_t* writtenOut) {
if (writtenOut) *writtenOut = 0;
if (!console || !data || x < 0 || y < 0 || length > NTL_CONSOLE_WRITE_MAX - 8u)
return NTLSTATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < length; ++i)
if ((uint8_t)data[i] > 0x7fu || (uint8_t)data[i] < 0x20u)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvConsoleLock(console);
if (!KSUCCESS(status)) return status;
SYS_CONSOLE_INFO original;
memset(&original, 0, sizeof(original));
status = RssrvDisplayQueryConsoleInfo(console->Display, &original);
if (KSUCCESS(status))
status = RssrvDisplaySetConsoleCursor(console->Display, x, y);
if (KSUCCESS(status)) {
status = RssrvDisplayWriteConsole(console->Display, data, length);
if (KSUCCESS(status) && length) RssrvConsoleChangedLocked(console);
KSTATUS restore = RssrvDisplaySetConsoleCursor(console->Display,
(int32_t)original.CursorX,
(int32_t)original.CursorY);
if (KSUCCESS(status)) status = restore;
}
RssrvConsoleUnlock(console);
if (KSUCCESS(status) && writtenOut) *writtenOut = length;
return status;
}
// Queries dimensions from the backing display and snapshots RSSRV's console generation.
static KSTATUS RssrvConsoleQueryInfo(RSSRV_CONSOLE_STATE* console, NTL_CONSOLE_INFO* infoOut) {
if (!console || !infoOut) return NTLSTATUS_INVALID_PARAMETER;
@@ -297,6 +328,18 @@ static void RssrvConsoleSessionThread(uintptr_t argument) {
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, written, 0, 0);
break;
case NTL_CONSOLE_OP_WRITE_AT:
if (request.Data.WriteAt.Length > NTL_CONSOLE_WRITE_MAX - 8u) {
status = NTLSTATUS_INVALID_PARAMETER;
} else {
status = RssrvConsoleWriteAt(console, request.Data.WriteAt.X,
request.Data.WriteAt.Y,
request.Data.WriteAt.Data,
request.Data.WriteAt.Length, &written);
}
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
status, written, 0, 0);
break;
case NTL_CONSOLE_OP_QUERY_INFO:
status = RssrvConsoleQueryInfo(console, &consoleInfo);
(void)RssrvConsoleReply(endpoint, info.MessageId, request.Operation,
@@ -0,0 +1,17 @@
// Exact-path namespace publication policy. A scoped capability can bind or unbind one name.
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/resource.h>
static inline bool RssrvScopeAllows(const char* scope, uint32_t operation,
const char* path, uint32_t pathLength) {
if (!scope) return true; // Unscoped administrator; separately issued by RSSRV.
return path &&
(operation == NTL_RESOURCE_OP_BIND || operation == NTL_RESOURCE_OP_UNBIND) &&
strlen(scope) == (size_t)pathLength && memcmp(scope, path, pathLength) == 0;
}
+111
View File
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include "rssrv_internal.h"
#include "publication_scope.h"
#include <stdlib.h>
#include <string.h>
@@ -9,6 +10,7 @@ typedef struct RSSRV_SESSION {
RSSRV_RESOURCE_SERVICE* Service;
SYS_HANDLE Endpoint;
bool Administrative;
const char* ScopePath;
} RSSRV_SESSION;
// Replies with one fixed resource-protocol reply and no handle transfer.
@@ -248,10 +250,16 @@ static KSTATUS RssrvHandleBind(RSSRV_RESOURCE_SERVICE* service,
return KSUCCESS(replyStatus) ? bindStatus : replyStatus;
}
// Allocates one durable, exact-path publication listener owned by RSSRV.
static KSTATUS RssrvCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, uint32_t length,
SYS_HANDLE* listenerOut);
// Dispatches one received resource request and returns whether a received handle was consumed.
static uint32_t RssrvDispatchRequest(RSSRV_RESOURCE_SERVICE* service,
SYS_HANDLE endpoint,
uint32_t administrative,
const char* scopePath,
const uint8_t* payload,
uint32_t payloadLength,
const SYS_PORT_MESSAGE_INFO* info,
@@ -281,6 +289,53 @@ static uint32_t RssrvDispatchRequest(RSSRV_RESOURCE_SERVICE* service,
return 0;
}
// A scoped listener can only publish or withdraw its exact delegated path.
if (!RssrvScopeAllows(scopePath, operation, path, request.PathLength)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
return 0;
}
if (operation == NTL_RESOURCE_OP_CREATE_PUBLISHER) {
if (!administrative || scopePath || request.Flags != NTL_RESOURCE_FLAG_NONE ||
request.AuxLength || info->HandleCount || request.Rights || request.ExportRights ||
request.Kind != NTL_RESOURCE_KIND_INVALID ||
RssrvVolumesOwnPath(path, request.PathLength) || request.PathLength > RSSRV_PUBLICATION_PATH_MAX) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
NTLSTATUS_ACCESS_DENIED, NTL_RESOURCE_KIND_INVALID);
return 0;
}
SYS_HANDLE publisher = SYS_HANDLE_INVALID;
status = RssrvCreatePublisher(service, path, request.PathLength, &publisher);
if (!KSUCCESS(status)) {
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
NTL_RESOURCE_REPLY reply = {0};
reply.Size = sizeof(reply);
reply.Version = NTL_RESOURCE_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Status = NTLSTATUS_SUCCESS;
SYS_PORT_HANDLE_TRANSFER transfer = {0};
transfer.Handle = publisher;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_DUPLICATE |
SYS_RIGHT_TRANSFER | SYS_PORT_RIGHT_CONNECT;
SYS_REPLY_PORT_REQUEST out = {0};
out.Size = sizeof(out);
out.EndpointHandle = endpoint;
out.MessageId = info->MessageId;
out.Payload = &reply;
out.PayloadLength = sizeof(reply);
out.Handles = &transfer;
out.HandleCount = 1u;
status = SysReplyPort(&out);
if (!KSUCCESS(status))
(void)RssrvReplyStatus(endpoint, info->MessageId, operation,
status, NTL_RESOURCE_KIND_INVALID);
return 0;
}
switch (operation) {
case NTL_RESOURCE_OP_LOOKUP:
if (request.Flags != NTL_RESOURCE_FLAG_NONE || request.AuxLength != 0 || info->HandleCount != 0 ||
@@ -408,6 +463,7 @@ static void RssrvSessionThread(uintptr_t argument) {
RSSRV_RESOURCE_SERVICE* service = session->Service;
SYS_HANDLE endpoint = session->Endpoint;
uint32_t administrative = session->Administrative;
const char* scopePath = session->ScopePath;
RssrvSetCurrentThreadName(administrative ? "rssrv.session.admin" : "rssrv.session.public");
free(session);
@@ -440,6 +496,7 @@ static void RssrvSessionThread(uintptr_t argument) {
consumed = RssrvDispatchRequest(service,
endpoint,
administrative,
scopePath,
payload,
info.PayloadLength,
&info,
@@ -449,6 +506,10 @@ static void RssrvSessionThread(uintptr_t argument) {
}
}
// A scoped publisher owns the binding for the lifetime of its connected
// session. Release it on abrupt disconnect as well as graceful unbind.
if (scopePath)
(void)RssrvNamespaceUnbind(service->Namespace, scopePath, (uint32_t)strlen(scopePath));
(void)SysCloseHandle(endpoint);
SysExitThread(0);
}
@@ -483,6 +544,7 @@ static void RssrvListenerThread(uintptr_t argument) {
session->Service = worker->Service;
session->Endpoint = endpoint;
session->Administrative = worker->Administrative;
session->ScopePath = worker->ScopePath[0] ? worker->ScopePath : NULL;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
@@ -532,6 +594,53 @@ static KSTATUS RssrvStartListenerWorker(RSSRV_RESOURCE_SERVICE* service,
return SysCreateThread(&create);
}
// Reuses a per-path authority across service generations and never broadens its scope.
static KSTATUS RssrvCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, uint32_t length,
SYS_HANDLE* listenerOut) {
if (!service || !path || !listenerOut || !length ||
length > RSSRV_PUBLICATION_PATH_MAX) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = SysWait(service->ScopeMutex, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) return status;
for (uint32_t i = 0; i < service->ScopeCount; ++i) {
RSSRV_LISTENER_WORKER* worker = &service->ScopeWorkers[i];
if (strlen(worker->ScopePath) == length &&
memcmp(worker->ScopePath, path, length) == 0) {
*listenerOut = worker->Listener;
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_SUCCESS;
}
}
if (service->ScopeCount >= RSSRV_PUBLICATION_SCOPE_MAX) {
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_LIMIT_REACHED;
}
RSSRV_LISTENER_WORKER* worker = &service->ScopeWorkers[service->ScopeCount];
memset(worker, 0, sizeof(*worker));
memcpy(worker->ScopePath, path, length);
worker->ScopePath[length] = '\0';
status = RssrvCreateListener(&worker->Listener);
if (KSUCCESS(status))
status = RssrvStartListenerWorker(service, worker, worker->Listener, 1u);
if (!KSUCCESS(status)) {
if (worker->Listener != SYS_HANDLE_INVALID) (void)SysCloseHandle(worker->Listener);
memset(worker, 0, sizeof(*worker));
(void)SysReleaseMutex(service->ScopeMutex);
return status;
}
++service->ScopeCount;
*listenerOut = worker->Listener;
(void)SysReleaseMutex(service->ScopeMutex);
return NTLSTATUS_SUCCESS;
}
// Issues a publisher restricted to one RSSRV-owned namespace path.
KSTATUS RssrvResourceCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, SYS_HANDLE* listenerOut) {
if (!path) return NTLSTATUS_INVALID_PARAMETER;
return RssrvCreatePublisher(service, path, (uint32_t)strlen(path), listenerOut);
}
// Initializes the public/admin resource protocol component around existing namespace state.
KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
RSSRV_NAMESPACE* namespaceState) {
@@ -539,6 +648,8 @@ KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
return NTLSTATUS_INVALID_PARAMETER;
memset(service, 0, sizeof(*service));
service->Namespace = namespaceState;
KSTATUS lockStatus = SysCreateMutex(&service->ScopeMutex);
if (!KSUCCESS(lockStatus)) return lockStatus;
service->PublicListener = SYS_HANDLE_INVALID;
service->AdminListener = SYS_HANDLE_INVALID;
service->PublicWorker.Listener = SYS_HANDLE_INVALID;
+47 -29
View File
@@ -2,10 +2,13 @@
#include "rssrv_internal.h"
#include <ntlite/exception.h>
#include <ntlite/desktop.h>
#include <ntlite/posix_service.h>
#include <ntlite/service.h>
#include <ntlite/service_control.h>
#include <ntlite/syscall.h>
#include <stdint.h>
#include <ntlite/bootstrap_capabilities.h>
#include <string.h>
static RSSRV_STATE gRssrv;
@@ -37,12 +40,14 @@ static KSTATUS RssrvAcceptControl(SYS_HANDLE controlListener, SYS_HANDLE* endpoi
return SysAcceptPort(&accept);
}
// Sends service readiness and delegates init-only namespace and DWM-session bootstrap capabilities.
// Exports only the capabilities consumed by dependent services. Init treats
// the names as opaque; RSSRV owns the underlying namespace policies.
static KSTATUS RssrvSendReady(SYS_HANDLE controlEndpoint,
const RSSRV_RESOURCE_SERVICE* resource,
const RSSRV_SESSION_STATE* session) {
if (!resource || !session || resource->AdminListener == SYS_HANDLE_INVALID ||
session->DwmListener == SYS_HANDLE_INVALID)
const RSSRV_SESSION_STATE* session,
SYS_HANDLE desktopPublisher,
SYS_HANDLE posixPublisher) {
if (!session || session->DwmListener == SYS_HANDLE_INVALID ||
desktopPublisher == SYS_HANDLE_INVALID || posixPublisher == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
NTL_SERVICE_CONTROL_MESSAGE message;
@@ -50,18 +55,23 @@ static KSTATUS RssrvSendReady(SYS_HANDLE controlEndpoint,
message.Size = sizeof(message);
message.Version = NTL_SERVICE_CONTROL_VERSION;
message.Operation = NTL_SERVICE_CONTROL_READY;
message.ExportCount = 2u;
message.ExportCount = 3u;
message.Status = NTLSTATUS_SUCCESS;
if (!NtlServiceControlNameExport(&message, 0u, "session-control") ||
!NtlServiceControlNameExport(&message, 1u, "desktop-publisher") ||
!NtlServiceControlNameExport(&message, 2u, "posix-publisher"))
return NTLSTATUS_FAILURE;
SYS_PORT_HANDLE_TRANSFER transfers[2];
SYS_PORT_HANDLE_TRANSFER transfers[3];
memset(transfers, 0, sizeof(transfers));
transfers[0].Handle = resource->AdminListener;
transfers[0].DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfers[0].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
transfers[1].Handle = session->DwmListener;
// Init must transfer this private listener once more into DWM. DWM itself receives only connect/query rights.
transfers[1].DesiredRights = SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfers[1].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
transfers[0].Handle = session->DwmListener;
transfers[1].Handle = desktopPublisher;
transfers[2].Handle = posixPublisher;
for (uint32_t i = 0; i < 3u; ++i) {
transfers[i].DesiredRights = SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER |
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfers[i].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
}
SYS_SEND_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
@@ -71,7 +81,7 @@ static KSTATUS RssrvSendReady(SYS_HANDLE controlEndpoint,
send.Payload = &message;
send.PayloadLength = sizeof(message);
send.Handles = transfers;
send.HandleCount = 2u;
send.HandleCount = 3u;
send.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
return SysSendPort(&send);
}
@@ -99,8 +109,7 @@ static void RssrvControlLoop(SYS_HANDLE controlEndpoint) {
return;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(message) || message.Size != sizeof(message) ||
message.Version != NTL_SERVICE_CONTROL_VERSION || message.ExportCount != 0 ||
message.Reserved[0] != 0 || message.Reserved[1] != 0 || message.Reserved[2] != 0)
!NtlServiceControlValid(&message, NTL_SERVICE_CONTROL_STOP, 0u))
continue;
if (message.Operation == NTL_SERVICE_CONTROL_STOP)
return;
@@ -154,19 +163,19 @@ void _start(void) {
memset(&security, 0, sizeof(security));
security.Size = sizeof(security);
status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security);
if (!KSUCCESS(status) || security.EffectivePrivileges != SYS_PRIV_SYSTEM_SERVICE_REGISTER ||
security.DelegablePrivileges != 0 || security.ServiceClaims != SYS_SERVICE_CLAIM_RESOURCE ||
security.DelegableServiceClaims != 0)
if (!KSUCCESS(status) || security.EffectivePrivileges != 0 ||
security.DelegablePrivileges != 0)
RssrvExit(2);
SYS_HANDLE systemVolume = SysGetStartupCapability(SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE);
SYS_HANDLE controlListener = SysGetStartupCapability(SYS_STARTUP_CAP_SERVICE_CONTROL);
SYS_HANDLE volumeManager = SysGetStartupCapability(SYS_STARTUP_CAP_VOLUME_MANAGER);
SYS_HANDLE inputManager = SysGetStartupCapability(SYS_STARTUP_CAP_INPUT_MANAGER);
SYS_HANDLE displayManager = SysGetStartupCapability(SYS_STARTUP_CAP_DISPLAY_MANAGER);
SYS_HANDLE systemVolume = SysGetStartupCapability(NTL_BOOT_CAP_NAMESPACE_ROOT);
SYS_HANDLE controlListener = SysGetStartupCapability("service.control");
SYS_HANDLE volumeManager = SysGetStartupCapability(NTL_BOOT_CAP_VOLUME_MANAGER);
SYS_HANDLE inputManager = SysGetStartupCapability(NTL_BOOT_CAP_INPUT_MANAGER);
SYS_HANDLE displayManager = SysGetStartupCapability(NTL_BOOT_CAP_DISPLAY_POLICY);
SYS_HANDLE registrationAuthority = SysGetStartupCapability(NTL_BOOT_CAP_RESOURCE_REGISTRATION);
if (systemVolume == SYS_HANDLE_INVALID || controlListener == SYS_HANDLE_INVALID ||
volumeManager == SYS_HANDLE_INVALID || inputManager == SYS_HANDLE_INVALID ||
displayManager == SYS_HANDLE_INVALID)
displayManager == SYS_HANDLE_INVALID || registrationAuthority == SYS_HANDLE_INVALID)
RssrvExit(3);
uint32_t systemVolumeId = 0;
@@ -232,13 +241,22 @@ void _start(void) {
status = RssrvVolumesStartWorker(&gRssrv.Volumes);
if (!KSUCCESS(status)) RssrvExit(11);
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, gRssrv.Resource.PublicListener);
status = SysRegisterSystemService(registrationAuthority, gRssrv.Resource.PublicListener);
if (!KSUCCESS(status)) RssrvExit(12);
status = RssrvSendReady(controlEndpoint, &gRssrv.Resource, &gRssrv.Session);
SYS_HANDLE desktopPublisher = SYS_HANDLE_INVALID;
SYS_HANDLE posixPublisher = SYS_HANDLE_INVALID;
status = RssrvResourceCreatePublisher(&gRssrv.Resource, NTL_DESKTOP_SERVICE_PATH,
&desktopPublisher);
if (!KSUCCESS(status)) RssrvExit(13);
status = RssrvResourceCreatePublisher(&gRssrv.Resource, NTL_POSIX_SERVICE_PATH,
&posixPublisher);
if (!KSUCCESS(status)) RssrvExit(13);
status = RssrvSendReady(controlEndpoint, &gRssrv.Session,
desktopPublisher, posixPublisher);
if (!KSUCCESS(status)) RssrvExit(13);
RssrvControlLoop(controlEndpoint);
(void)SysUnregisterSystemService(SYS_SERVICE_RESOURCE, gRssrv.Resource.PublicListener);
(void)SysUnregisterSystemService(registrationAuthority, gRssrv.Resource.PublicListener);
(void)SysDisconnectPort(controlEndpoint);
(void)SysCloseHandle(controlEndpoint);
RssrvExit(0);
+8
View File
@@ -10,6 +10,8 @@
#include <stdbool.h>
#define RSSRV_LISTENER_BACKLOG 16u
#define RSSRV_PUBLICATION_SCOPE_MAX 32u
#define RSSRV_PUBLICATION_PATH_MAX 127u
#define RSSRV_SYMBOLIC_LINK_MAX_DEPTH 16u
#define RSSRV_VOLUME_PATH_CAPACITY 48u
#define RSSRV_INPUT_KEY_QUEUE_SIZE 256u
@@ -137,6 +139,7 @@ typedef struct RSSRV_LISTENER_WORKER {
RSSRV_RESOURCE_SERVICE* Service;
SYS_HANDLE Listener;
bool Administrative;
char ScopePath[RSSRV_PUBLICATION_PATH_MAX + 1u];
} RSSRV_LISTENER_WORKER;
struct RSSRV_RESOURCE_SERVICE {
@@ -145,6 +148,9 @@ struct RSSRV_RESOURCE_SERVICE {
SYS_HANDLE AdminListener;
RSSRV_LISTENER_WORKER PublicWorker;
RSSRV_LISTENER_WORKER AdminWorker;
SYS_HANDLE ScopeMutex;
uint32_t ScopeCount;
RSSRV_LISTENER_WORKER ScopeWorkers[RSSRV_PUBLICATION_SCOPE_MAX];
};
typedef struct RSSRV_STATE {
@@ -305,3 +311,5 @@ KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service,
// Creates the resource listeners and starts their accept workers.
KSTATUS RssrvResourceStart(RSSRV_RESOURCE_SERVICE* service);
KSTATUS RssrvResourceCreatePublisher(RSSRV_RESOURCE_SERVICE* service,
const char* path, SYS_HANDLE* listenerOut);
@@ -0,0 +1,122 @@
// Native input API smoke test.
// Verifies status-returning keyboard and mouse polling contracts without requiring live input.
#include <stdint.h>
#include <stdbool.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), 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 = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
while (true) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("input_api_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("input_api_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Program entry point.
int main(void) {
print("input_api_smoke: start\n");
expect_status("poll keyboard null", SysPollKeyboard((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("is key down null", SysIsKeyDown(INPUT_KEY_A, (bool*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read key null", SysReadKey((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read char null", SysReadKeyboardChar((char*)0), NTLSTATUS_INVALID_PARAMETER);
bool down = true;
uint32_t status = SysIsKeyDown(INPUT_KEY_A, &down);
if (status != NTLSTATUS_SUCCESS)
fail_status("is key down", status);
INPUT_KEY key = INPUT_KEY_A;
status = SysPollKeyboard(&key);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("poll keyboard", status);
if (status == NTLSTATUS_WOULD_BLOCK && key != INPUT_KEY_NONE)
fail_status("poll keyboard would-block key", key);
INPUT_KEY_RECORD input;
SYS_INPUT_EVENT_REQUEST inputReq;
inputReq.Size = sizeof(inputReq);
inputReq.QueueHandle = 0;
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = 0;
inputReq.RecordOut = &input;
expect_status("peek input null", SysPeekInputEvent((const SYS_INPUT_EVENT_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysPeekInputEvent(&inputReq);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek input", status);
INPUT_MOUSE_EVENT mouse;
expect_status("peek mouse null", SysPeekMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("read mouse null", SysReadMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysPeekMouse(&mouse);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek mouse", status);
uint32_t readStatus = SysReadMouse(&mouse);
if (status == NTLSTATUS_SUCCESS) {
if (readStatus != NTLSTATUS_SUCCESS)
fail_status("read mouse after peek", readStatus);
} else if (readStatus != NTLSTATUS_WOULD_BLOCK) {
fail_status("read mouse would-block", readStatus);
}
int x = 0;
int y = 0;
expect_status("get mouse pos null x", SysGetMousePos((int*)0, &y), NTLSTATUS_INVALID_PARAMETER);
expect_status("get mouse pos null y", SysGetMousePos(&x, (int*)0), NTLSTATUS_INVALID_PARAMETER);
status = SysGetMousePos(&x, &y);
if (status != NTLSTATUS_SUCCESS)
fail_status("get mouse pos", status);
print("input_api_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,135 @@
// Input model smoke test.
// Verifies that input queue handles and request-based reads target one keyboard queue.
#include <stdint.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), 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 = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
while (true) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("input_model_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("input_model_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Program entry point.
int main(void) {
print("input_model_smoke: start\n");
uint32_t inputQueue = 0;
expect_status("open input queue null", SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, (uint32_t*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("open input queue bad flags", SysOpenInputQueue(1, &inputQueue), NTLSTATUS_INVALID_PARAMETER);
uint32_t status = SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, &inputQueue);
if (status != NTLSTATUS_SUCCESS)
fail_status("open input queue", status);
INPUT_KEY_RECORD record;
SYS_INPUT_EVENT_REQUEST req;
req.Size = sizeof(req);
req.QueueHandle = inputQueue;
req.Flags = SYS_INPUT_EVENT_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.RecordOut = &record;
SYS_INPUT_EVENT_REQUEST badReq = req;
badReq.Size = 0;
expect_status("read input bad size", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER);
badReq = req;
badReq.RecordOut = (INPUT_KEY_RECORD*)0;
expect_status("peek input null out", SysPeekInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER);
badReq = req;
badReq.QueueHandle = 0xdeadbeefu;
expect_status("read input bad handle", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_HANDLE);
SYS_GLOBAL_INPUT_STATE state;
state.Size = sizeof(state);
state.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
expect_status("query global input null", SysQueryGlobalInputState((SYS_GLOBAL_INPUT_STATE*)0), NTLSTATUS_INVALID_PARAMETER);
SYS_GLOBAL_INPUT_STATE badState = state;
badState.Size = 0;
expect_status("query global input bad size", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
badState = state;
badState.Flags = 1;
expect_status("query global input bad flags", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
status = SysQueryGlobalInputState(&state);
if (status != NTLSTATUS_SUCCESS)
fail_status("query global input", status);
if (state.Size != sizeof(state))
fail_status("query global input size", state.Size);
if (state.Flags != SYS_GLOBAL_INPUT_STATE_FLAG_NONE)
fail_status("query global input flags", state.Flags);
status = SysPeekInputEvent(&req);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek input immediate", status);
uint32_t waitStatus = SysWaitSingle(inputQueue, SYS_WAIT_FLAG_NONE, 0);
if (status == NTLSTATUS_SUCCESS) {
if (waitStatus != NTLSTATUS_SUCCESS)
fail_status("wait input after readable peek", waitStatus);
} else if (waitStatus != NTLSTATUS_TIMEOUT) {
fail_status("wait empty input immediate", waitStatus);
}
req.TimeoutMilliseconds = 1;
status = SysReadInputEvent(&req);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT)
fail_status("read input finite timeout", status);
SysCloseHandle(inputQueue);
print("input_model_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,197 @@
// Native named queue lifetime smoke test.
// Verifies explicit name withdrawal and automatic withdrawal on owner handle close.
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_MESSAGE_TYPE 0x71756575u
#define TEST_MESSAGE_VALUE 0x123456789abcdef0ull
typedef struct TEST_QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6];
} TEST_QUEUE_MESSAGE;
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
];
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), 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 = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
while (true) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("named_queue_lifetime_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("named_queue_lifetime_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("named_queue_lifetime_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Publishes one queue under a short test name.
static uint32_t publish_queue(const char* name, uint32_t queue) {
SYS_PUBLISH_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Name = SysSvFromCstr(name);
req.QueueHandle = queue;
return SysPublishQueue(&req);
}
// Withdraws one named queue publication.
static uint32_t unpublish_queue(const char* name) {
SYS_UNPUBLISH_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Name = SysSvFromCstr(name);
return SysUnpublishQueue(&req);
}
// Opens one named queue publication.
static uint32_t open_named_queue(const char* name, uint32_t* out) {
SYS_OPEN_NAMED_QUEUE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Name = SysSvFromCstr(name);
req.HandleOut = out;
return SysOpenNamedQueue(&req);
}
// Sends one fixed-size queue message.
static uint32_t send_message(uint32_t queue, const TEST_QUEUE_MESSAGE* message) {
SYS_QUEUE_SEND_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.QueueHandle = queue;
req.Flags = SYS_QUEUE_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.Message = message;
return SysSendQueue(&req);
}
// Receives one fixed-size queue message.
static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* message) {
SYS_QUEUE_RECV_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.QueueHandle = queue;
req.Flags = SYS_QUEUE_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.MessageOut = message;
return SysRecvQueue(&req);
}
// Verifies explicit unpublish removes only the name, not existing handles.
static void check_explicit_unpublish(void) {
uint32_t queue = 0;
uint32_t opened = 0;
uint32_t status = SysCreateQueue(&queue);
if (status != NTLSTATUS_SUCCESS) fail_status("create queue", status);
status = publish_queue("ntl.queue.explicit", queue);
if (status != NTLSTATUS_SUCCESS) fail_status("publish queue", status);
status = open_named_queue("ntl.queue.explicit", &opened);
if (status != NTLSTATUS_SUCCESS) fail_status("open published queue", status);
if (!opened) fail("opened handle is zero");
status = unpublish_queue("ntl.queue.explicit");
if (status != NTLSTATUS_SUCCESS) fail_status("unpublish queue", status);
uint32_t missing = 0;
status = open_named_queue("ntl.queue.explicit", &missing);
expect_status("open withdrawn name", status, NTLSTATUS_NOT_FOUND);
TEST_QUEUE_MESSAGE out;
TEST_QUEUE_MESSAGE in;
memset(&out, 0, sizeof(out));
memset(&in, 0, sizeof(in));
out.Type = TEST_MESSAGE_TYPE;
out.Param[0] = TEST_MESSAGE_VALUE;
status = send_message(queue, &out);
if (status != NTLSTATUS_SUCCESS) fail_status("send after unpublish", status);
status = recv_message(opened, &in);
if (status != NTLSTATUS_SUCCESS) fail_status("recv after unpublish", status);
if (in.Type != TEST_MESSAGE_TYPE || in.Param[0] != TEST_MESSAGE_VALUE)
fail("message mismatch after unpublish");
SysCloseHandle(opened);
SysCloseHandle(queue);
}
// Verifies closing the publishing handle withdraws its names.
static void check_close_withdraws_name(void) {
uint32_t queue = 0;
uint32_t status = SysCreateQueue(&queue);
if (status != NTLSTATUS_SUCCESS) fail_status("create close queue", status);
status = publish_queue("ntl.queue.close", queue);
if (status != NTLSTATUS_SUCCESS) fail_status("publish close queue", status);
SysCloseHandle(queue);
uint32_t opened = 0;
status = open_named_queue("ntl.queue.close", &opened);
expect_status("open close-withdrawn name", status, NTLSTATUS_NOT_FOUND);
}
// Runs the named queue lifetime smoke test.
int main(void) {
print("named_queue_lifetime_smoke: start\n");
check_explicit_unpublish();
check_close_withdraws_name();
print("named_queue_lifetime_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -464,77 +464,21 @@ static int run_local_transport_test(void) {
return 0;
}
// Verifies that registration privilege alone cannot publish a claimed well-known service.
static int run_claimless_client(void) {
SYS_SECURITY_CONTEXT_INFO contextInfo;
memset(&contextInfo, 0, sizeof(contextInfo));
contextInfo.Size = sizeof(contextInfo);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &contextInfo);
if (!KSUCCESS(status)) return 10;
if (contextInfo.EffectivePrivileges != SYS_PRIV_SYSTEM_SERVICE_REGISTER ||
contextInfo.ServiceClaims != 0)
return 11;
SYS_HANDLE listener = SYS_HANDLE_INVALID;
status = create_listener(1, &listener);
if (!KSUCCESS(status)) return 12;
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, listener);
(void)SysCloseHandle(listener);
return status == NTLSTATUS_ACCESS_DENIED ? 0 : 13;
}
// Verifies that the live resource service owns its well-known registry slot and remains connectable.
// Verifies that unrelated handles cannot authorize numeric service publication.
static int run_service_test(void) {
SYS_HANDLE listener = SYS_HANDLE_INVALID;
KSTATUS status = create_listener(1, &listener);
if (!KSUCCESS(status)) return fail("create service probe listener", status);
status = SysRegisterSystemService(SYS_SERVICE_MAX + 1u, listener);
if (status != NTLSTATUS_INVALID_PARAMETER)
return fail("invalid service id", status);
status = SysRegisterSystemService(SYS_HANDLE_INVALID, listener);
if (status != NTLSTATUS_INVALID_HANDLE)
return fail("missing registration capability", status);
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, listener);
if (status != NTLSTATUS_ALREADY_EXISTS)
return fail("resource service must already be registered", status);
status = SysRegisterSystemService(listener, listener);
if (KSUCCESS(status)) return fail("listener cannot impersonate registry authority", status);
SYS_HANDLE claimlessContext = SYS_HANDLE_INVALID;
SYS_DERIVE_SECURITY_CONTEXT_REQUEST deriveContext;
memset(&deriveContext, 0, sizeof(deriveContext));
deriveContext.Size = sizeof(deriveContext);
deriveContext.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
deriveContext.EffectivePrivileges = SYS_PRIV_SYSTEM_SERVICE_REGISTER;
deriveContext.ContextHandleOut = &claimlessContext;
status = SysDeriveSecurityContext(&deriveContext);
if (!KSUCCESS(status)) return fail("derive claimless service context", status);
SYS_PEB* peb = SysGetPeb();
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0)
return fail("missing image path", NTLSTATUS_FAILURE);
SYS_HANDLE claimlessChild = SYS_HANDLE_INVALID;
NTL_CREATE_SYSTEM_PROCESS_REQUEST claimlessCreate;
memset(&claimlessCreate, 0, sizeof(claimlessCreate));
claimlessCreate.Size = sizeof(claimlessCreate);
claimlessCreate.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
claimlessCreate.Path = peb->ImagePath;
claimlessCreate.CommandLine = SysSvFromCstr("claimless");
claimlessCreate.SecurityContext = claimlessContext;
claimlessCreate.ResolveModuleName = NtlResolveSystemModuleName;
claimlessCreate.ProcessHandleOut = &claimlessChild;
status = NtlCreateSystemProcess(&claimlessCreate);
(void)SysCloseHandle(claimlessContext);
if (!KSUCCESS(status)) return fail("create claimless service client", status);
status = SysWait(claimlessChild, SYS_WAIT_FLAG_NONE);
if (!KSUCCESS(status)) return fail("wait claimless service client", status);
int32_t claimlessExit = -1;
status = SysGetProcessExitCode(claimlessChild, &claimlessExit);
(void)SysCloseHandle(claimlessChild);
if (!KSUCCESS(status)) return fail("query claimless service client", status);
if (claimlessExit != 0) return fail("service claim enforcement", (KSTATUS)claimlessExit);
status = SysUnregisterSystemService(SYS_SERVICE_RESOURCE, listener);
if (status != NTLSTATUS_ACCESS_DENIED)
return fail("foreign listener cannot unregister service", status);
status = SysUnregisterSystemService(listener, listener);
if (KSUCCESS(status)) return fail("listener cannot revoke registry authority", status);
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_CONNECT_SYSTEM_SERVICE_REQUEST connect;
@@ -550,14 +494,8 @@ static int run_service_test(void) {
return 0;
}
// Runs either the privileged parent or claimless child half.
// Runs the capability-scoped port and registry smoke tests.
int main(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb) return 2;
if (peb->CommandLine.Length == 9 &&
memcmp(peb->CommandLine.Data, "claimless", 9) == 0)
return run_claimless_client();
print("port_ipc_smoke: start\n");
if (run_connection_lifetime_test() != 0) return 1;
if (run_peer_process_test() != 0) return 1;
@@ -9,7 +9,6 @@
#include <stddef.h>
#include <string.h>
#define TEST_STARTUP_EVENT_TAG 0x50425445u
#define TEST_STARTUP_WORD 0x31503550u
// Exits without relying on a hosted runtime.
@@ -58,7 +57,7 @@ static void verify_child_bootstrap(void) {
// 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);
SYS_HANDLE event = SysGetStartupCapability("test.builder-event");
if (event == SYS_HANDLE_INVALID) exit_now(10);
verify_child_bootstrap();
if (SysSignalEvent(event) != NTLSTATUS_SUCCESS) exit_now(11);
@@ -88,7 +87,7 @@ static int run_parent(void) {
NTL_PROCESS_STARTUP_CAPABILITY startupCapability;
memset(&startupCapability, 0, sizeof(startupCapability));
startupCapability.Tag = TEST_STARTUP_EVENT_TAG;
strcpy(startupCapability.Name, "test.builder-event");
startupCapability.SourceHandle = event;
startupCapability.DesiredRights = SYS_EVENT_RIGHT_SIGNAL | SYS_RIGHT_QUERY;
startupCapability.DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
@@ -2,10 +2,11 @@
// Verifies that kernel-owned processes remain queryable but cannot be terminated.
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include <ntlite/syscall.h>
#define MAX_PROCESSES 16u
#define MAX_PROCESSES 64u
// Prints one NUL-terminated string to the console.
static void print(const char* text) {
@@ -81,10 +82,12 @@ void main(void) {
int sawIdle = 0;
int sawSystem = 0;
int sawInit = 0;
size_t limit = count < MAX_PROCESSES ? count : MAX_PROCESSES;
for (size_t i = 0; i < limit; i++) {
const SYS_PROCESS_INFO* process = &processes[i];
if (!(process->Flags & (SYS_PROCESS_FLAG_IDLE | SYS_PROCESS_FLAG_SYSTEM)))
int isInit = strcmp(process->Name, "system/init.exe") == 0;
if (!(process->Flags & (SYS_PROCESS_FLAG_IDLE | SYS_PROCESS_FLAG_SYSTEM)) && !isInit)
continue;
if (!(process->Flags & SYS_PROCESS_FLAG_TERMINATION_PROTECTED))
@@ -93,11 +96,19 @@ void main(void) {
verify_protected_process(process);
if (process->Flags & SYS_PROCESS_FLAG_IDLE) sawIdle = 1;
if (process->Flags & SYS_PROCESS_FLAG_SYSTEM) sawSystem = 1;
if (isInit) sawInit = 1;
}
if (!sawIdle || !sawSystem)
if (!sawIdle || !sawSystem || !sawInit)
fail_status("missing kernel process", (uint32_t)((sawIdle ? 1u : 0u) | (sawSystem ? 2u : 0u)));
uint32_t action = 0;
SYS_HANDLE event = SYS_HANDLE_INVALID;
if (SysOpenPowerEvent(&event) != NTLSTATUS_ACCESS_DENIED ||
SysQuerySystemPower(&action) != NTLSTATUS_ACCESS_DENIED ||
SysCompleteSystemPower(SYS_POWER_ACTION_SHUTDOWN) != NTLSTATUS_ACCESS_DENIED)
fail_status("power coordinator ownership", NTLSTATUS_FAILURE);
print("process_protection_smoke: PASS\n");
exit_now(0);
}
@@ -86,8 +86,7 @@ static int run_restricted_child(uint32_t parentPid) {
info.Size = sizeof(info);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info);
if (!KSUCCESS(status)) return 10;
if (info.EffectivePrivileges || info.DelegablePrivileges ||
info.ServiceClaims || info.DelegableServiceClaims)
if (info.EffectivePrivileges || info.DelegablePrivileges)
return 11;
SYS_HANDLE process = SYS_HANDLE_INVALID;
@@ -145,9 +144,9 @@ static int run_restricted_child(uint32_t parentPid) {
createPort.HandleOut = &listener;
status = SysCreatePort(&createPort);
if (!KSUCCESS(status)) return 25;
status = SysRegisterSystemService(SYS_SERVICE_RESOURCE, listener);
status = SysRegisterSystemService(SYS_HANDLE_INVALID, listener);
(void)SysCloseHandle(listener);
if (status != NTLSTATUS_ACCESS_DENIED) return 26;
if (status != NTLSTATUS_INVALID_HANDLE) return 26;
uint32_t selfPid = SysGetCurrentPid();
status = SysOpenProcess(selfPid, SYS_RIGHT_QUERY, &process);
@@ -188,9 +187,7 @@ static int run_parent(void) {
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &rootInfo);
if (!KSUCCESS(status)) fail_status("query current context", status);
if (rootInfo.EffectivePrivileges != SYS_PRIV_ALL ||
rootInfo.DelegablePrivileges != SYS_PRIV_ALL ||
rootInfo.ServiceClaims != SYS_SERVICE_CLAIM_ALL ||
rootInfo.DelegableServiceClaims != SYS_SERVICE_CLAIM_ALL)
rootInfo.DelegablePrivileges != SYS_PRIV_ALL)
fail_status("bootstrap context authority", NTLSTATUS_FAILURE);
SYS_HANDLE selfProcess = SYS_HANDLE_INVALID;
@@ -209,9 +206,7 @@ static int run_parent(void) {
if (!KSUCCESS(status)) fail_status("query exact process security context", status);
if (exactInfo.SecurityId != rootInfo.SecurityId ||
exactInfo.EffectivePrivileges != rootInfo.EffectivePrivileges ||
exactInfo.DelegablePrivileges != rootInfo.DelegablePrivileges ||
exactInfo.ServiceClaims != rootInfo.ServiceClaims ||
exactInfo.DelegableServiceClaims != rootInfo.DelegableServiceClaims)
exactInfo.DelegablePrivileges != rootInfo.DelegablePrivileges)
fail_status("exact process security context identity", NTLSTATUS_FAILURE);
(void)SysCloseHandle(exactContext);
(void)SysCloseHandle(selfProcess);
@@ -232,8 +227,7 @@ static int run_parent(void) {
status = SysQuerySecurityContext(restricted, &restrictedInfo);
if (!KSUCCESS(status)) fail_status("query stripped context", status);
if (restrictedInfo.SecurityId <= rootInfo.SecurityId ||
restrictedInfo.EffectivePrivileges || restrictedInfo.DelegablePrivileges ||
restrictedInfo.ServiceClaims || restrictedInfo.DelegableServiceClaims)
restrictedInfo.EffectivePrivileges || restrictedInfo.DelegablePrivileges)
fail_status("stripped context contents", NTLSTATUS_FAILURE);
SYS_HANDLE illegal = SYS_HANDLE_INVALID;
@@ -247,15 +241,6 @@ static int run_parent(void) {
if (status != NTLSTATUS_ACCESS_DENIED)
fail_status("blocked privilege amplification", status);
memset(&amplify, 0, sizeof(amplify));
amplify.Size = sizeof(amplify);
amplify.SourceContext = restricted;
amplify.ServiceClaims = SYS_SERVICE_CLAIM_RESOURCE;
amplify.ContextHandleOut = &illegal;
status = SysDeriveSecurityContext(&amplify);
if (status != NTLSTATUS_ACCESS_DENIED)
fail_status("blocked service-claim amplification", status);
SYS_HANDLE queryOnly = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST deriveHandle = {
sizeof(deriveHandle), restricted, SYS_RIGHT_QUERY, &queryOnly
@@ -7,8 +7,6 @@
#include <ntlite/process_loader.h>
#include <ntlite/syscall.h>
#define TEST_SURFACE_TAG 0x53555246u
#define TEST_READY_TAG 0x53555245u
#define TEST_WIDTH 32u
#define TEST_HEIGHT 16u
#define TEST_PIXEL_X 7u
@@ -49,8 +47,8 @@ static void write_pixel(void* base, uint32_t pitch, uint32_t value) {
// Runs the child side using only the attenuated capabilities supplied at process construction.
static void run_child(void) {
SYS_HANDLE surface = SysGetStartupCapability(TEST_SURFACE_TAG);
SYS_HANDLE ready = SysGetStartupCapability(TEST_READY_TAG);
SYS_HANDLE surface = SysGetStartupCapability("test.surface");
SYS_HANDLE ready = SysGetStartupCapability("test.ready");
if (surface == SYS_HANDLE_INVALID || ready == SYS_HANDLE_INVALID) exit_now(10);
void* invalidBase = (void*)0;
@@ -112,11 +110,11 @@ int main(void) {
NTL_PROCESS_STARTUP_CAPABILITY startup[2];
memset(startup, 0, sizeof(startup));
startup[0].Tag = TEST_SURFACE_TAG;
strcpy(startup[0].Name, "test.surface");
startup[0].SourceHandle = surface;
startup[0].DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_SURFACE_RIGHT_MAP_READ;
startup[0].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
startup[1].Tag = TEST_READY_TAG;
strcpy(startup[1].Name, "test.ready");
startup[1].SourceHandle = ready;
startup[1].DesiredRights = SYS_RIGHT_QUERY | SYS_EVENT_RIGHT_SIGNAL;
startup[1].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;