redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests
This commit is contained in:
1 parent
4add6789ba
commit
f882d339fb
232 files changed
+20934
-8898
No files matched your search
@@ -0,0 +1,501 @@
|
||||
// Port IPC smoke test. This validates copied messaging, service discovery, waitability, and capability transfer.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <ntlite/process_loader.h>
|
||||
#include <ntlite/port.h>
|
||||
#include <ntlite/service.h>
|
||||
|
||||
#define EVENT_AUTO_RESET 0u
|
||||
#define TEST_TIMEOUT_MS 2000u
|
||||
|
||||
// Writes one string to the console.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer without requiring formatted libc helpers.
|
||||
static void print_u32(uint32_t value) {
|
||||
char digits[16];
|
||||
uint32_t count = 0;
|
||||
do {
|
||||
digits[count++] = (char)('0' + value % 10u);
|
||||
value /= 10u;
|
||||
} while (value && count < sizeof(digits));
|
||||
while (count) {
|
||||
char one[2] = { digits[--count], 0 };
|
||||
print(one);
|
||||
}
|
||||
}
|
||||
|
||||
// Fails the current process with one visible status diagnostic.
|
||||
static int fail(const char* operation, KSTATUS status) {
|
||||
print("port_ipc_smoke: ");
|
||||
print(operation);
|
||||
print(" status=");
|
||||
print_u32(status);
|
||||
print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Reads Port transport diagnostics through the privileged kernel-stats interface.
|
||||
static KSTATUS query_port_stats(SYS_PORT_STATS* stats) {
|
||||
if (!stats) return NTLSTATUS_INVALID_PARAMETER;
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
return SysQueryKernelStats(SYS_KERNEL_STATS_PORT, stats, sizeof(*stats), (size_t*)0);
|
||||
}
|
||||
|
||||
// Returns a newly created listener Port.
|
||||
static KSTATUS create_listener(uint32_t backlog, SYS_HANDLE* handleOut) {
|
||||
SYS_CREATE_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Backlog = backlog;
|
||||
request.HandleOut = handleOut;
|
||||
return SysCreatePort(&request);
|
||||
}
|
||||
|
||||
// Connects directly to one listener handle.
|
||||
static KSTATUS connect_listener(SYS_HANDLE listener, SYS_HANDLE* endpointOut) {
|
||||
SYS_CONNECT_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.ListenerHandle = listener;
|
||||
request.EndpointHandleOut = endpointOut;
|
||||
return SysConnectPort(&request);
|
||||
}
|
||||
|
||||
// Accepts one pending connection from a listener.
|
||||
static KSTATUS accept_listener(SYS_HANDLE listener,
|
||||
uint64_t timeoutMilliseconds,
|
||||
SYS_HANDLE* endpointOut,
|
||||
SYS_PORT_CONNECTION_INFO* infoOut) {
|
||||
SYS_ACCEPT_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.ListenerHandle = listener;
|
||||
request.TimeoutMilliseconds = timeoutMilliseconds;
|
||||
request.EndpointHandleOut = endpointOut;
|
||||
request.ConnectionInfoOut = infoOut;
|
||||
if (infoOut) {
|
||||
memset(infoOut, 0, sizeof(*infoOut));
|
||||
infoOut->Size = sizeof(*infoOut);
|
||||
}
|
||||
return SysAcceptPort(&request);
|
||||
}
|
||||
|
||||
// Sends one notification without transferred handles.
|
||||
static KSTATUS send_notification(SYS_HANDLE endpoint,
|
||||
const void* payload,
|
||||
uint32_t payloadLength,
|
||||
uint64_t timeoutMilliseconds) {
|
||||
SYS_SEND_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.EndpointHandle = endpoint;
|
||||
request.Payload = payload;
|
||||
request.PayloadLength = payloadLength;
|
||||
request.TimeoutMilliseconds = timeoutMilliseconds;
|
||||
return SysSendPort(&request);
|
||||
}
|
||||
|
||||
// Receives one Port message into caller-provided storage.
|
||||
static KSTATUS receive_message(SYS_HANDLE endpoint,
|
||||
void* payload,
|
||||
uint32_t payloadCapacity,
|
||||
SYS_HANDLE* handles,
|
||||
uint32_t handleCapacity,
|
||||
uint64_t timeoutMilliseconds,
|
||||
SYS_PORT_MESSAGE_INFO* infoOut) {
|
||||
memset(infoOut, 0, sizeof(*infoOut));
|
||||
infoOut->Size = sizeof(*infoOut);
|
||||
SYS_RECEIVE_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.EndpointHandle = endpoint;
|
||||
request.Payload = payload;
|
||||
request.PayloadCapacity = payloadCapacity;
|
||||
request.HandlesOut = handles;
|
||||
request.HandleCapacity = handleCapacity;
|
||||
request.InfoOut = infoOut;
|
||||
request.TimeoutMilliseconds = timeoutMilliseconds;
|
||||
return SysReceivePort(&request);
|
||||
}
|
||||
|
||||
typedef struct RECEIVE_WORKER_CONTEXT {
|
||||
SYS_HANDLE Endpoint;
|
||||
SYS_HANDLE ReadyEvent;
|
||||
KSTATUS Status;
|
||||
SYS_PORT_MESSAGE_INFO Info;
|
||||
char Payload[16];
|
||||
} RECEIVE_WORKER_CONTEXT;
|
||||
|
||||
// Blocks one worker on a shared endpoint to validate multiple concurrent receivers.
|
||||
static void receive_worker(uintptr_t argument) {
|
||||
RECEIVE_WORKER_CONTEXT* context = (RECEIVE_WORKER_CONTEXT*)argument;
|
||||
if (!context) SysExitThread(1);
|
||||
memset(context->Payload, 0, sizeof(context->Payload));
|
||||
if (!KSUCCESS(SysSignalEvent(context->ReadyEvent)))
|
||||
SysExitThread(2);
|
||||
context->Status = receive_message(context->Endpoint,
|
||||
context->Payload,
|
||||
(uint32_t)sizeof(context->Payload),
|
||||
(SYS_HANDLE*)0,
|
||||
0,
|
||||
TEST_TIMEOUT_MS,
|
||||
&context->Info);
|
||||
SysExitThread(KSUCCESS(context->Status) ? 0 : 1);
|
||||
}
|
||||
|
||||
// Verifies listener backlog and pending-connection teardown semantics.
|
||||
static int run_connection_lifetime_test(void) {
|
||||
SYS_HANDLE listener = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = create_listener(1, &listener);
|
||||
if (!KSUCCESS(status)) return fail("create lifetime listener", status);
|
||||
|
||||
SYS_HANDLE client = SYS_HANDLE_INVALID;
|
||||
status = connect_listener(listener, &client);
|
||||
if (!KSUCCESS(status)) return fail("connect pending client", status);
|
||||
|
||||
SYS_HANDLE overflow = SYS_HANDLE_INVALID;
|
||||
status = connect_listener(listener, &overflow);
|
||||
if (status != NTLSTATUS_LIMIT_REACHED)
|
||||
return fail("listener backlog", status);
|
||||
|
||||
status = SysCloseHandle(listener);
|
||||
if (!KSUCCESS(status)) return fail("close pending listener", status);
|
||||
|
||||
SYS_PORT_MESSAGE_INFO info;
|
||||
status = receive_message(client, (void*)0, 0, (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (status != NTLSTATUS_PORT_DISCONNECTED)
|
||||
return fail("pending connection disconnect", status);
|
||||
(void)SysCloseHandle(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Exercises same-process connection semantics, waitability, buffering, and queue quotas.
|
||||
static int run_local_transport_test(void) {
|
||||
SYS_HANDLE listener = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = create_listener(4, &listener);
|
||||
if (!KSUCCESS(status)) return fail("create local listener", status);
|
||||
|
||||
SYS_HANDLE client = SYS_HANDLE_INVALID;
|
||||
status = connect_listener(listener, &client);
|
||||
if (!KSUCCESS(status)) return fail("connect local listener", status);
|
||||
|
||||
status = SysWaitSingle(listener, SYS_WAIT_FLAG_NONE, 0);
|
||||
if (!KSUCCESS(status)) return fail("listener waitability", status);
|
||||
|
||||
SYS_PORT_CONNECTION_INFO connectionInfo;
|
||||
SYS_HANDLE server = SYS_HANDLE_INVALID;
|
||||
status = accept_listener(listener, 0, &server, &connectionInfo);
|
||||
if (!KSUCCESS(status)) return fail("accept local connection", status);
|
||||
if (connectionInfo.ClientProcessId != SysGetCurrentPid() ||
|
||||
connectionInfo.ClientThreadId != SysGetCurrentTid() ||
|
||||
connectionInfo.ClientSecurityId == 0)
|
||||
return fail("local connection identity", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_PORT_STATS directBefore;
|
||||
status = query_port_stats(&directBefore);
|
||||
if (!KSUCCESS(status)) return fail("query direct stats before", status);
|
||||
|
||||
RECEIVE_WORKER_CONTEXT workers[2];
|
||||
memset(workers, 0, sizeof(workers));
|
||||
SYS_HANDLE workerHandles[2] = { SYS_HANDLE_INVALID, SYS_HANDLE_INVALID };
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
workers[i].Endpoint = server;
|
||||
workers[i].ReadyEvent = SYS_HANDLE_INVALID;
|
||||
workers[i].Status = NTLSTATUS_PENDING;
|
||||
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &workers[i].ReadyEvent);
|
||||
if (!KSUCCESS(status)) return fail("create receive ready event", status);
|
||||
uint32_t threadId = 0;
|
||||
SYS_CREATE_THREAD_REQUEST createThread = {
|
||||
sizeof(createThread), (uintptr_t)receive_worker, (uintptr_t)&workers[i],
|
||||
1, SYS_CREATE_THREAD_FLAG_NONE, &workerHandles[i], &threadId
|
||||
};
|
||||
status = SysCreateThread(&createThread);
|
||||
if (!KSUCCESS(status)) return fail("create receive worker", status);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
status = SysWaitSingle(workers[i].ReadyEvent, SYS_WAIT_FLAG_NONE, TEST_TIMEOUT_MS);
|
||||
if (!KSUCCESS(status)) return fail("wait receive ready event", status);
|
||||
}
|
||||
// The ready event is signaled immediately before ReceivePort; give each worker one scheduling turn to block.
|
||||
(void)SysSleep(20);
|
||||
static const char workerPayloadA[] = "worker-a";
|
||||
static const char workerPayloadB[] = "worker-b";
|
||||
status = send_notification(client, workerPayloadA, (uint32_t)sizeof(workerPayloadA), TEST_TIMEOUT_MS);
|
||||
if (!KSUCCESS(status)) return fail("send worker payload a", status);
|
||||
status = send_notification(client, workerPayloadB, (uint32_t)sizeof(workerPayloadB), TEST_TIMEOUT_MS);
|
||||
if (!KSUCCESS(status)) return fail("send worker payload b", status);
|
||||
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
status = SysWait(workerHandles[i], SYS_WAIT_FLAG_NONE);
|
||||
if (!KSUCCESS(status)) return fail("wait receive worker", status);
|
||||
(void)SysCloseHandle(workerHandles[i]);
|
||||
(void)SysCloseHandle(workers[i].ReadyEvent);
|
||||
if (!KSUCCESS(workers[i].Status) || workers[i].Info.Type != SYS_PORT_MESSAGE_NOTIFICATION ||
|
||||
workers[i].Info.SenderProcessId != SysGetCurrentPid())
|
||||
return fail("receive worker result", workers[i].Status);
|
||||
}
|
||||
bool firstIsA = memcmp(workers[0].Payload, workerPayloadA, sizeof(workerPayloadA)) == 0;
|
||||
bool firstIsB = memcmp(workers[0].Payload, workerPayloadB, sizeof(workerPayloadB)) == 0;
|
||||
bool secondIsA = memcmp(workers[1].Payload, workerPayloadA, sizeof(workerPayloadA)) == 0;
|
||||
bool secondIsB = memcmp(workers[1].Payload, workerPayloadB, sizeof(workerPayloadB)) == 0;
|
||||
if (!((firstIsA && secondIsB) || (firstIsB && secondIsA)))
|
||||
return fail("multiple receive payloads", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_PORT_STATS directAfter;
|
||||
status = query_port_stats(&directAfter);
|
||||
if (!KSUCCESS(status)) return fail("query direct stats after", status);
|
||||
if (directAfter.DirectDeliveries < directBefore.DirectDeliveries + 2u ||
|
||||
directAfter.QueuedMessages != directBefore.QueuedMessages)
|
||||
return fail("direct rendezvous counters", NTLSTATUS_FAILURE);
|
||||
|
||||
char payload[] = "buffer-too-small";
|
||||
static const char expectedPayload[] = "buffer-too-small";
|
||||
status = send_notification(client, payload, (uint32_t)sizeof(payload), 0);
|
||||
if (!KSUCCESS(status)) return fail("queue local notification", status);
|
||||
payload[0] = 'X';
|
||||
|
||||
char tiny[4];
|
||||
SYS_PORT_MESSAGE_INFO info;
|
||||
status = receive_message(server, tiny, (uint32_t)sizeof(tiny), (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (status != NTLSTATUS_BUFFER_TOO_SMALL ||
|
||||
info.PayloadLength != sizeof(expectedPayload) || info.Type != SYS_PORT_MESSAGE_NOTIFICATION)
|
||||
return fail("buffer-too-small contract", status);
|
||||
|
||||
char received[32];
|
||||
memset(received, 0, sizeof(received));
|
||||
status = receive_message(server, received, (uint32_t)sizeof(received), (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (!KSUCCESS(status)) return fail("retry local receive", status);
|
||||
if (info.SenderProcessId != SysGetCurrentPid() ||
|
||||
info.SenderThreadId != SysGetCurrentTid() ||
|
||||
info.SenderSecurityId == 0 ||
|
||||
info.PayloadLength != sizeof(expectedPayload) ||
|
||||
memcmp(received, expectedPayload, sizeof(expectedPayload)) != 0)
|
||||
return fail("queued message snapshot", NTLSTATUS_FAILURE);
|
||||
|
||||
SYS_HANDLE sourceEvent = SYS_HANDLE_INVALID;
|
||||
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &sourceEvent);
|
||||
if (!KSUCCESS(status)) return fail("create queued transfer event", status);
|
||||
SYS_PORT_HANDLE_TRANSFER queuedTransfer = {
|
||||
sourceEvent,
|
||||
SYS_RIGHT_WAIT,
|
||||
SYS_PORT_TRANSFER_FLAG_DERIVE
|
||||
};
|
||||
SYS_SEND_PORT_REQUEST transferSend;
|
||||
memset(&transferSend, 0, sizeof(transferSend));
|
||||
transferSend.Size = sizeof(transferSend);
|
||||
transferSend.EndpointHandle = client;
|
||||
transferSend.Handles = &queuedTransfer;
|
||||
transferSend.HandleCount = 1;
|
||||
status = SysSendPort(&transferSend);
|
||||
if (!KSUCCESS(status)) return fail("queue capability transfer", status);
|
||||
status = SysCloseHandle(sourceEvent);
|
||||
if (!KSUCCESS(status)) return fail("close transferred source", status);
|
||||
|
||||
SYS_HANDLE receivedEvent = SYS_HANDLE_INVALID;
|
||||
status = receive_message(server,
|
||||
(void*)0,
|
||||
0,
|
||||
&receivedEvent,
|
||||
1,
|
||||
0,
|
||||
&info);
|
||||
if (!KSUCCESS(status) || info.HandleCount != 1)
|
||||
return fail("receive queued capability transfer", status);
|
||||
status = SysWaitSingle(receivedEvent, SYS_WAIT_FLAG_NONE, 0);
|
||||
if (status != NTLSTATUS_TIMEOUT)
|
||||
return fail("queued transfer wait right", status);
|
||||
status = SysSignalEvent(receivedEvent);
|
||||
if (status != NTLSTATUS_ACCESS_DENIED)
|
||||
return fail("queued transfer attenuation", status);
|
||||
(void)SysCloseHandle(receivedEvent);
|
||||
|
||||
SYS_SEND_PORT_REQUEST oversized;
|
||||
memset(&oversized, 0, sizeof(oversized));
|
||||
oversized.Size = sizeof(oversized);
|
||||
oversized.EndpointHandle = client;
|
||||
oversized.Payload = (const void*)1;
|
||||
oversized.PayloadLength = SYS_PORT_MAX_INLINE_BYTES + 1u;
|
||||
status = SysSendPort(&oversized);
|
||||
if (status != NTLSTATUS_INVALID_PARAMETER)
|
||||
return fail("inline payload bound", status);
|
||||
|
||||
for (uint32_t i = 0; i < SYS_PORT_QUEUE_MESSAGE_LIMIT; ++i) {
|
||||
status = send_notification(client, (const void*)0, 0, 0);
|
||||
if (!KSUCCESS(status)) return fail("fill local queue", status);
|
||||
}
|
||||
status = send_notification(client, (const void*)0, 0, 0);
|
||||
if (status != NTLSTATUS_WOULD_BLOCK)
|
||||
return fail("local queue backpressure", status);
|
||||
|
||||
for (uint32_t i = 0; i < SYS_PORT_QUEUE_MESSAGE_LIMIT; ++i) {
|
||||
status = receive_message(server, (void*)0, 0, (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (!KSUCCESS(status)) return fail("drain local queue", status);
|
||||
}
|
||||
|
||||
// One process cannot multiply endpoint queue limits into unbounded kernel IPC storage.
|
||||
SYS_HANDLE quotaClients[5] = { client, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID };
|
||||
SYS_HANDLE quotaServers[5] = { server, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID, SYS_HANDLE_INVALID };
|
||||
for (uint32_t i = 1; i < 5; ++i) {
|
||||
status = connect_listener(listener, "aClients[i]);
|
||||
if (!KSUCCESS(status)) return fail("connect quota endpoint", status);
|
||||
status = accept_listener(listener, 0, "aServers[i], (SYS_PORT_CONNECTION_INFO*)0);
|
||||
if (!KSUCCESS(status)) return fail("accept quota endpoint", status);
|
||||
}
|
||||
|
||||
SYS_PORT_STATS quotaBefore;
|
||||
status = query_port_stats("aBefore);
|
||||
if (!KSUCCESS(status)) return fail("query quota stats before", status);
|
||||
|
||||
bool processQuotaHit = false;
|
||||
for (uint32_t i = 0; i < 5 && !processQuotaHit; ++i) {
|
||||
for (uint32_t j = 0; j < SYS_PORT_QUEUE_MESSAGE_LIMIT; ++j) {
|
||||
status = send_notification(quotaClients[i], (const void*)0, 0, 0);
|
||||
if (status == NTLSTATUS_LIMIT_REACHED) {
|
||||
processQuotaHit = true;
|
||||
break;
|
||||
}
|
||||
if (!KSUCCESS(status)) return fail("fill process quota", status);
|
||||
}
|
||||
}
|
||||
if (!processQuotaHit)
|
||||
return fail("process ipc quota", NTLSTATUS_FAILURE);
|
||||
SYS_PORT_STATS quotaAfter;
|
||||
status = query_port_stats("aAfter);
|
||||
if (!KSUCCESS(status)) return fail("query quota stats after", status);
|
||||
if (quotaAfter.ProcessQuotaRejects <= quotaBefore.ProcessQuotaRejects)
|
||||
return fail("process quota counter", NTLSTATUS_FAILURE);
|
||||
|
||||
for (uint32_t i = 0; i < 5; ++i) {
|
||||
while (true) {
|
||||
status = receive_message(quotaServers[i], (void*)0, 0, (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (status == NTLSTATUS_WOULD_BLOCK)
|
||||
break;
|
||||
if (!KSUCCESS(status)) return fail("drain process quota", status);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 1; i < 5; ++i) {
|
||||
(void)SysCloseHandle(quotaServers[i]);
|
||||
(void)SysCloseHandle(quotaClients[i]);
|
||||
}
|
||||
|
||||
status = SysDisconnectPort(client);
|
||||
if (!KSUCCESS(status)) return fail("disconnect local endpoint", status);
|
||||
status = receive_message(server, (void*)0, 0, (SYS_HANDLE*)0, 0, 0, &info);
|
||||
if (status != NTLSTATUS_PORT_DISCONNECTED)
|
||||
return fail("local peer disconnect", status);
|
||||
|
||||
(void)SysCloseHandle(server);
|
||||
(void)SysCloseHandle(client);
|
||||
(void)SysCloseHandle(listener);
|
||||
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.
|
||||
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_SERVICE_RESOURCE, listener);
|
||||
if (status != NTLSTATUS_ALREADY_EXISTS)
|
||||
return fail("resource service must already be registered", 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);
|
||||
|
||||
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
|
||||
SYS_CONNECT_SYSTEM_SERVICE_REQUEST connect;
|
||||
memset(&connect, 0, sizeof(connect));
|
||||
connect.Size = sizeof(connect);
|
||||
connect.ServiceId = SYS_SERVICE_RESOURCE;
|
||||
connect.EndpointHandleOut = &endpoint;
|
||||
status = SysConnectSystemService(&connect);
|
||||
if (!KSUCCESS(status)) return fail("connect live resource service", status);
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
(void)SysCloseHandle(listener);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Runs either the privileged parent or claimless child half.
|
||||
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_local_transport_test() != 0) return 1;
|
||||
if (run_service_test() != 0) return 1;
|
||||
print("port_ipc_smoke: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user