implement generic I/O async request object to replace file-specific async requests, refactor lwIP network stack to dispatch requests through IRPs with cancellation support, add cancellation polling to lwIP accept and receive operations using LwipSocketWaitRecvNetbuf and LwipSocketWaitAccept, introduce NetSubmitSocketIrp for routing socket operations via device IRPs, track network IRP statistics with SYS_NET_IRP_STATS, add process module lock spinlock and reference counting for process modules, implement dynamic module loading and unloading via user module handles, add POSIX dynamic loader APIs including dlopen, dlsym, dlerror, and dlclose, introduce PosixStart entry point for posix applications, add new native smoke tests for net IRPs.

This commit is contained in:
maksw committed 2026-06-27 10:37:53 +02:00
1 parent ed3d065abd
commit 1f5477cf66
32 files changed
+3713 -551

No files matched your search

@@ -0,0 +1,347 @@
// User-mode smoke test for socket IRP dispatch, public async sockets, cancellation, and statistics.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define EVENT_AUTO_RESET 0u
#define NET_IRP_SMOKE_UDP_PORT 40219u
#define NET_IRP_SMOKE_HTTP_PORT 80u
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { 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 = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current process.
static void exit_now(int32_t code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one message.
static void fail(const char* message) {
print("net_irp_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("net_irp_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Reads the public network IRP statistics snapshot.
static SYS_NET_IRP_STATS query_stats(void) {
SYS_NET_IRP_STATS stats;
memset(&stats, 0, sizeof(stats));
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_NET_IRP, &stats, sizeof(stats), 0);
if (status != STATUS_SUCCESS)
fail_status("query net irp stats", status);
if (stats.Size != sizeof(stats))
fail("net irp stats size mismatch");
if (stats.Version != SYS_STATS_VERSION)
fail("net irp stats version mismatch");
return stats;
}
// Builds an IPv4 socket address.
static SYS_SOCKET_ADDRESS make_ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port) {
SYS_SOCKET_ADDRESS address;
memset(&address, 0, sizeof(address));
address.Size = sizeof(address);
address.Family = SYS_AF_INET;
address.Port = port;
address.Address[0] = a;
address.Address[1] = b;
address.Address[2] = c;
address.Address[3] = d;
return address;
}
// Sets one uint32 socket option.
static void set_u32_option(uint32_t socket, uint32_t option, uint32_t value) {
SYS_SOCKET_OPTION_REQUEST req;
memset(&req, 0, sizeof(req));
req.Option = option;
req.Value = &value;
req.ValueSize = sizeof(value);
uint32_t status = SysSetSocketOption(socket, &req);
if (status != STATUS_SUCCESS)
fail_status("set socket option", status);
}
// Waits for an async socket request to publish a terminal IOSB status.
static void wait_for_async(uint32_t event, SYS_IO_STATUS_BLOCK* iosb, const char* what) {
for (uint32_t i = 0; i < 200u && iosb->Status == STATUS_PENDING; ++i) {
uint32_t status = SysWait(event, 0);
if (status != STATUS_SUCCESS && status != STATUS_ALERTED &&
status != STATUS_KERNEL_APC && status != STATUS_USER_APC) {
fail_status(what, status);
}
}
if (iosb->Status == STATUS_PENDING)
fail("async request stayed pending");
}
// Verifies nonblocking UDP receive returns would-block through the IRP path.
static void check_nonblocking_receive(void) {
uint32_t udp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp);
if (status != STATUS_SUCCESS)
fail_status("create udp socket", status);
SYS_SOCKET_ADDRESS bindAddress = make_ipv4(0, 0, 0, 0, NET_IRP_SMOKE_UDP_PORT);
status = SysBindSocket(udp, &bindAddress, sizeof(bindAddress));
if (status != STATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("bind udp socket", status);
}
set_u32_option(udp, SYS_SOCKET_OPT_NONBLOCKING, 1);
char buffer[16];
SYS_SOCKET_ADDRESS remote;
size_t got = 0;
size_t remoteSize = sizeof(remote);
memset(buffer, 0, sizeof(buffer));
memset(&remote, 0, sizeof(remote));
SYS_SOCKET_RECV_FROM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Buffer = buffer;
req.Length = sizeof(buffer);
req.RemoteAddressOut = &remote;
req.RemoteAddressSizeInOut = &remoteSize;
req.ReceivedOut = &got;
status = SysRecvFromSocket(udp, &req);
SysCloseHandle(udp);
if (status != STATUS_WOULD_BLOCK)
fail_status("nonblocking udp recvfrom", status);
}
// Verifies a pending async UDP receive can be cancelled and waited through its request handle.
static void check_async_receive_cancel(void) {
uint32_t udp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp);
if (status != STATUS_SUCCESS)
fail_status("create cancel udp socket", status);
SYS_SOCKET_ADDRESS bindAddress = make_ipv4(0, 0, 0, 0, NET_IRP_SMOKE_UDP_PORT + 1u);
status = SysBindSocket(udp, &bindAddress, sizeof(bindAddress));
if (status != STATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("bind cancel udp socket", status);
}
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS) {
SysCloseHandle(udp);
fail_status("create event", status);
}
char buffer[16];
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
uint32_t requestHandle = 0;
SYS_IO_STATUS_BLOCK iosb;
memset(buffer, 0, sizeof(buffer));
memset(&remote, 0, sizeof(remote));
iosb.Status = 0;
iosb.Information = 0;
SYS_ASYNC_SOCKET_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.SocketHandle = udp;
req.EventHandle = event;
req.Operation = SYS_ASYNC_SOCKET_OP_RECV_FROM;
req.Buffer = buffer;
req.Length = sizeof(buffer);
req.AddressOut = &remote;
req.AddressSizeInOut = &remoteSize;
req.Iosb = &iosb;
req.RequestHandleOut = &requestHandle;
SYS_NET_IRP_STATS beforeSubmit = query_stats();
status = SysSocketIoAsync(&req);
if (status != STATUS_PENDING || requestHandle == 0) {
SysCloseHandle(event);
SysCloseHandle(udp);
fail_status("submit async recvfrom", status);
}
uint32_t enteredIrp = 0;
for (uint32_t i = 0; i < 40u; ++i) {
SYS_NET_IRP_STATS now = query_stats();
if (now.ByOperation[SYS_NET_IRP_OP_RECEIVE_FROM] >
beforeSubmit.ByOperation[SYS_NET_IRP_OP_RECEIVE_FROM]) {
enteredIrp = 1;
break;
}
SysSleep(10);
}
if (!enteredIrp)
fail("async recvfrom did not enter the IRP path");
uint32_t cancelStatus = SysCancelIo(requestHandle);
if (cancelStatus != STATUS_SUCCESS && cancelStatus != STATUS_PENDING)
fail_status("cancel async recvfrom", cancelStatus);
wait_for_async(event, &iosb, "wait async recvfrom cancel");
if (iosb.Status != STATUS_CANCELLED)
fail_status("async recvfrom iosb", iosb.Status);
if (iosb.Information != 0)
fail("async recvfrom cancel transferred bytes");
status = SysWaitSingle(requestHandle, 0, 0);
if (status != STATUS_SUCCESS)
fail_status("wait cancelled request handle", status);
SysCloseHandle(requestHandle);
SysCloseHandle(event);
SysCloseHandle(udp);
}
// Tries a host-backed HTTP transfer to prove async send and receive success paths.
static void try_http_async_success(void) {
uint32_t tcp = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &tcp);
if (status != STATUS_SUCCESS) {
print("net_irp_smoke: http success path skipped create status=");
print_u64(status);
print("\n");
return;
}
SYS_SOCKET_ADDRESS address = make_ipv4(10, 0, 2, 2, NET_IRP_SMOKE_HTTP_PORT);
status = SysConnectSocket(tcp, &address, sizeof(address));
if (status != STATUS_SUCCESS) {
print("net_irp_smoke: http success path skipped connect status=");
print_u64(status);
print("\n");
SysCloseHandle(tcp);
return;
}
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS) {
SysCloseHandle(tcp);
fail_status("create http event", status);
}
static const char requestText[] = "GET / HTTP/1.0\r\nHost: 10.0.2.2\r\n\r\n";
SYS_IO_STATUS_BLOCK sendIosb;
uint32_t sendRequest = 0;
memset(&sendIosb, 0, sizeof(sendIosb));
SYS_ASYNC_SOCKET_REQUEST sendReq;
memset(&sendReq, 0, sizeof(sendReq));
sendReq.Size = sizeof(sendReq);
sendReq.SocketHandle = tcp;
sendReq.EventHandle = event;
sendReq.Operation = SYS_ASYNC_SOCKET_OP_SEND;
sendReq.Buffer = (void*)requestText;
sendReq.Length = sizeof(requestText) - 1u;
sendReq.Iosb = &sendIosb;
sendReq.RequestHandleOut = &sendRequest;
status = SysSocketIoAsync(&sendReq);
if (status != STATUS_PENDING || sendRequest == 0)
fail_status("submit async send", status);
wait_for_async(event, &sendIosb, "wait async send");
if (sendIosb.Status != STATUS_SUCCESS)
fail_status("async send iosb", sendIosb.Status);
if (sendIosb.Information != sizeof(requestText) - 1u)
fail("async send byte count mismatch");
SysCloseHandle(sendRequest);
char response[128];
SYS_IO_STATUS_BLOCK recvIosb;
uint32_t recvRequest = 0;
memset(response, 0, sizeof(response));
memset(&recvIosb, 0, sizeof(recvIosb));
SYS_ASYNC_SOCKET_REQUEST recvReq;
memset(&recvReq, 0, sizeof(recvReq));
recvReq.Size = sizeof(recvReq);
recvReq.SocketHandle = tcp;
recvReq.EventHandle = event;
recvReq.Operation = SYS_ASYNC_SOCKET_OP_RECV;
recvReq.Buffer = response;
recvReq.Length = sizeof(response) - 1u;
recvReq.Iosb = &recvIosb;
recvReq.RequestHandleOut = &recvRequest;
status = SysSocketIoAsync(&recvReq);
if (status != STATUS_PENDING || recvRequest == 0)
fail_status("submit async recv", status);
wait_for_async(event, &recvIosb, "wait async recv");
if (recvIosb.Status != STATUS_SUCCESS)
fail_status("async recv iosb", recvIosb.Status);
if (recvIosb.Information == 0)
fail("async recv returned no bytes");
SysCloseHandle(recvRequest);
SysCloseHandle(event);
SysCloseHandle(tcp);
}
// Verifies counters prove socket requests entered and completed through the IRP path.
static void check_stats_delta(const SYS_NET_IRP_STATS* before) {
SYS_NET_IRP_STATS after = query_stats();
if (after.Submitted <= before->Submitted)
fail("net irp submitted counter did not advance");
if (after.Completed <= before->Completed)
fail("net irp completed counter did not advance");
if (after.WouldBlock <= before->WouldBlock)
fail("net irp would-block counter did not advance");
if (after.CancelRequested <= before->CancelRequested)
fail("net irp cancel-request counter did not advance");
if (after.Cancelled <= before->Cancelled)
fail("net irp cancelled counter did not advance");
if (after.ByOperation[SYS_NET_IRP_OP_RECEIVE_FROM] <= before->ByOperation[SYS_NET_IRP_OP_RECEIVE_FROM])
fail("net irp recvfrom operation counter did not advance");
}
// Runs the network IRP smoke test.
int main(void) {
print("net_irp_smoke: start\n");
SYS_NET_IRP_STATS before = query_stats();
check_nonblocking_receive();
check_async_receive_cancel();
try_http_async_success();
check_stats_delta(&before);
print("net_irp_smoke: PASS\n");
exit_now(0);
return 0;
}