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

+10 -3
View File
@@ -51,7 +51,7 @@ void create_layout() {
"obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping", "obj\\doom", "obj\\video", "obj\\memtop",
"obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\input_api_smoke", "obj\\input_model_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\cwd_peb_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap",
"obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread",
"obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_present"
"obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_present", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke"
};
for (size_t i = 0; i < NOB_ARRAY_LEN(dirs); i++) nob_mkdir_if_not_exists(dirs[i]);
nob_mkdir_if_not_exists(PCI_BUS_DRIVER.obj_dir);
@@ -96,7 +96,7 @@ bool clean_build_object_directories(void) {
"obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping",
"obj\\doom", "obj\\video", "obj\\memtop", "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\input_api_smoke", "obj\\input_model_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\cwd_peb_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap",
"obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread",
"obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_present"
"obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_present", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke"
};
for (size_t i = 0; i < NOB_ARRAY_LEN(dirs); i++) {
@@ -167,6 +167,7 @@ int main(int argc, char **argv) {
"cl /nologo /c /Foobj\\libc.obj code\\common\\libc.c",
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\user_heap.obj code\\user\\runtime\\heap.c", CFLAGS),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\user_heap_exports.obj code\\user\\runtime\\heap_exports.c", CFLAGS),
nob_temp_sprintf("cl %s /c /Foobj\\runtime\\posix_start.obj code\\user\\runtime\\posix_start.c", CFLAGS),
// Bootloader.
nob_temp_sprintf("cl %s /MP /c /Icode\\boot\\uefi\\include /Foobj\\boot\\ code\\boot\\uefi\\*.c && link %s /FORCE:MULTIPLE /SUBSYSTEM:EFI_APPLICATION /ENTRY:UefiMain /OUT:bin\\bootx64.efi obj\\libc.obj obj\\ini_parser.obj code\\boot\\uefi\\efilib.lib obj\\boot\\*.obj", CFLAGS, LFLAGS),
@@ -233,7 +234,7 @@ int main(int argc, char **argv) {
// Folder, Libs, Name, Optional Extra Link Args.
{"system\\init", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "init", "/ENTRY:_start"},
{"apps\\shell_win32", "bin\\win32.lib obj\\libc.obj", "shell_win32", "/ENTRY:main"},
{"apps\\shell_posix", "bin\\posix.lib obj\\libc.obj", "shell_posix", "/ENTRY:main"},
{"apps\\shell_posix", "bin\\posix.lib obj\\libc.obj obj\\runtime\\posix_start.obj", "shell_posix", "/ENTRY:PosixStart"},
{"apps\\shell_native", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "shell_native", "/ENTRY:main"},
{"tests\\async_apc", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "async_apc", "/ENTRY:main"},
{"tests\\apc_return_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "apc_return_smoke", "/ENTRY:main"},
@@ -248,6 +249,9 @@ int main(int argc, char **argv) {
{"tests\\file_lock_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "file_lock_smoke", "/ENTRY:main"},
{"tests\\process_protection_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "process_protection_smoke", "/ENTRY:main"},
{"tests\\socket_addr_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "socket_addr_smoke", "/ENTRY:main"},
{"tests\\net_irp_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "net_irp_smoke", "/ENTRY:main"},
{"tests\\module_load_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "module_load_smoke", "/ENTRY:main"},
{"tests\\posix_module_smoke", "bin\\posix.lib obj\\libc.obj obj\\runtime\\posix_start.obj", "posix_module_smoke", "/ENTRY:PosixStart"},
{"tests\\syscall_contract_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "syscall_contract_smoke", "/ENTRY:main"},
{"tests\\would_block_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "would_block_smoke", "/ENTRY:main"},
{"tests\\cwd_peb_smoke", "bin\\syscall.lib obj\\libc.obj obj\\runtime\\user_heap.obj obj\\runtime\\user_heap_exports.obj", "cwd_peb_smoke", "/ENTRY:main"},
@@ -343,6 +347,9 @@ int main(int argc, char **argv) {
{"bin\\file_lock_smoke.exe", "Y:\\apps\\file_lock_smoke.exe"},
{"bin\\process_protection_smoke.exe", "Y:\\apps\\process_protection_smoke.exe"},
{"bin\\socket_addr_smoke.exe", "Y:\\apps\\socket_addr_smoke.exe"},
{"bin\\net_irp_smoke.exe", "Y:\\apps\\net_irp_smoke.exe"},
{"bin\\module_load_smoke.exe", "Y:\\apps\\module_load_smoke.exe"},
{"bin\\posix_module_smoke.exe", "Y:\\apps\\posix_module_smoke.exe"},
{"bin\\syscall_contract_smoke.exe", "Y:\\apps\\syscall_contract_smoke.exe"},
{"bin\\would_block_smoke.exe", "Y:\\apps\\would_block_smoke.exe"},
{"bin\\cwd_peb_smoke.exe", "Y:\\apps\\cwd_peb_smoke.exe"},
+320 -37
View File
@@ -7,6 +7,7 @@
#include <ntlite/ddi/hal_api.h>
#include <ntlite/status.h>
#include <ntlite/ddi/io_driver.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_settings.h>
@@ -38,6 +39,7 @@
#define LWIP_PROVIDER_SOCKET_IO_MAX_BYTES (1024u * 1024u)
#define LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES 65507u
#define LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES 65535u
#define LWIP_PROVIDER_CANCEL_POLL_MS 25u
#define LWIP_SETTINGS_DEFAULT_SECTION "network.default"
#define LWIP_SETTINGS_SECTION_PREFIX "network."
@@ -105,38 +107,28 @@ static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t
static void LwipProviderSocketClose(void* socket);
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
static KSTATUS LwipProviderListen(void* socket, uint32_t backlog);
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested);
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested);
static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested);
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested);
static KSTATUS LwipProviderShutdown(void* socket, uint32_t how);
static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize);
static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut);
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp);
static const NET_STACK_PROVIDER gLwipProvider = {
static DRIVER_OBJECT gLwipDriver;
static DEVICE_OBJECT* gLwipDevice;
static NET_STACK_PROVIDER gLwipProvider = {
NET_STACK_PROVIDER_VERSION,
"lwIP",
NULL,
LwipProviderAttachDevice,
LwipProviderReceiveFrame,
LwipProviderSocketCreate,
LwipProviderSocketClose,
LwipProviderBind,
LwipProviderListen,
LwipProviderAccept,
LwipProviderConnect,
LwipProviderSend,
LwipProviderReceive,
LwipProviderSendTo,
LwipProviderReceiveFrom,
LwipProviderShutdown,
LwipProviderSetOption,
LwipProviderGetOption,
LwipProviderPoll,
LwipProviderResolveHost,
};
// Implements the err To Status operation.
@@ -377,10 +369,18 @@ static void LwipSocketCallback(struct netconn* conn, enum netconn_evt evt, u16_t
if (!conn)
return;
LWIP_PROVIDER_SOCKET* socket = (LWIP_PROVIDER_SOCKET*)netconn_get_callback_arg(conn);
if (!socket)
void* callbackArg = netconn_get_callback_arg(conn);
uintptr_t rawArg = (uintptr_t)callbackArg;
if (!callbackArg)
return;
// Accepted netconns inherit the listener callback before the wrapper can
// replace lwIP's internal negative socket sentinel with our socket object.
if (rawArg >= 0x80000000ull && rawArg <= 0xffffffffull)
return;
LWIP_PROVIDER_SOCKET* socket = (LWIP_PROVIDER_SOCKET*)callbackArg;
switch (evt) {
case NETCONN_EVT_RCVPLUS:
socket->PollReadEvents++;
@@ -426,12 +426,118 @@ static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32
s->Type = type;
s->Protocol = protocol;
netconn_set_callback_arg(conn, s);
conn->callback = LwipSocketCallback;
netconn_set_recvtimeout(conn, 0);
netconn_set_sendtimeout(conn, 0);
*out = s;
return STATUS_SUCCESS;
}
// Returns whether the IRP cancel token has been set by SysCancelIo.
static int LwipSocketCancelRequested(volatile uint8_t* cancelRequested) {
return cancelRequested && *cancelRequested;
}
// Computes the bounded wait slice used by cancel-aware receive and accept loops.
static uint32_t LwipSocketCancelWaitSlice(uint32_t remainingMs) {
if (remainingMs != 0 && remainingMs < LWIP_PROVIDER_CANCEL_POLL_MS)
return remainingMs;
return LWIP_PROVIDER_CANCEL_POLL_MS;
}
// Waits for a receive-style netconn mailbox while periodically observing cancellation.
static KSTATUS LwipSocketWaitRecvNetbuf(LWIP_PROVIDER_SOCKET* s,
struct netbuf** bufferOut,
volatile uint8_t* cancelRequested,
err_t* errOut) {
if (!s || !s->Conn || !bufferOut || !errOut)
return STATUS_INVALID_PARAMETER;
*bufferOut = NULL;
*errOut = ERR_OK;
if (!cancelRequested || s->NonBlocking) {
*errOut = netconn_recv(s->Conn, bufferOut);
return STATUS_SUCCESS;
}
uint32_t originalTimeout = s->ReceiveTimeoutMs;
uint32_t elapsed = 0;
for (;;) {
if (LwipSocketCancelRequested(cancelRequested)) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
return STATUS_CANCELLED;
}
uint32_t remaining = 0;
if (originalTimeout != 0) {
if (elapsed >= originalTimeout) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
*errOut = ERR_TIMEOUT;
return STATUS_SUCCESS;
}
remaining = originalTimeout - elapsed;
}
uint32_t slice = LwipSocketCancelWaitSlice(remaining);
netconn_set_recvtimeout(s->Conn, slice);
err_t err = netconn_recv(s->Conn, bufferOut);
if (err != ERR_TIMEOUT) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
*errOut = err;
return STATUS_SUCCESS;
}
elapsed += slice;
}
}
// Waits for accept while preserving the caller's receive timeout model.
static KSTATUS LwipSocketWaitAccept(LWIP_PROVIDER_SOCKET* s,
struct netconn** acceptedOut,
volatile uint8_t* cancelRequested,
err_t* errOut) {
if (!s || !s->Conn || !acceptedOut || !errOut)
return STATUS_INVALID_PARAMETER;
*acceptedOut = NULL;
*errOut = ERR_OK;
if (!cancelRequested || s->NonBlocking) {
*errOut = netconn_accept(s->Conn, acceptedOut);
return STATUS_SUCCESS;
}
uint32_t originalTimeout = s->ReceiveTimeoutMs;
uint32_t elapsed = 0;
for (;;) {
if (LwipSocketCancelRequested(cancelRequested)) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
return STATUS_CANCELLED;
}
uint32_t remaining = 0;
if (originalTimeout != 0) {
if (elapsed >= originalTimeout) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
*errOut = ERR_TIMEOUT;
return STATUS_SUCCESS;
}
remaining = originalTimeout - elapsed;
}
uint32_t slice = LwipSocketCancelWaitSlice(remaining);
netconn_set_recvtimeout(s->Conn, slice);
err_t err = netconn_accept(s->Conn, acceptedOut);
if (err != ERR_TIMEOUT) {
netconn_set_recvtimeout(s->Conn, originalTimeout);
*errOut = err;
return STATUS_SUCCESS;
}
elapsed += slice;
}
}
static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
if (!address || addressSize < sizeof(SYS_SOCKET_ADDRESS_K))
return 0;
@@ -798,7 +904,7 @@ static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t
return STATUS_NOT_SUPPORTED;
}
struct netconn* conn = netconn_new_with_proto_and_callback(netconnType, rawProtocol, LwipSocketCallback);
struct netconn* conn = netconn_new_with_proto_and_callback(netconnType, rawProtocol, NULL);
if (!conn)
return STATUS_NO_MEMORY;
@@ -905,7 +1011,7 @@ static KSTATUS LwipProviderListen(void* socket, uint32_t backlog) {
}
// Implements the provider Accept operation.
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (!s || !acceptedOut)
return STATUS_INVALID_PARAMETER;
@@ -926,16 +1032,17 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
}
struct netconn* acceptedConn = NULL;
err_t err = netconn_accept(s->Conn, &acceptedConn);
err_t err = ERR_OK;
st = LwipSocketWaitAccept(s, &acceptedConn, cancelRequested, &err);
LwipSocketUnlock(s);
if (!KSUCCESS(st))
return st;
if (err != ERR_OK)
return LwipErrToStatus(err);
if (!acceptedConn)
return STATUS_FAILURE;
acceptedConn->callback = LwipSocketCallback;
LWIP_PROVIDER_SOCKET* accepted = NULL;
st = LwipSocketAllocateWithConn(s->Domain, s->Type, s->Protocol, acceptedConn, &accepted);
if (!KSUCCESS(st)) {
@@ -963,7 +1070,7 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
}
// Implements the provider Connect operation.
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (!s || !address)
return STATUS_INVALID_PARAMETER;
@@ -994,12 +1101,58 @@ static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* add
return st;
}
err_t err = netconn_connect(s->Conn, &ip, address->Port);
if (err == ERR_OK)
s->Connected = 1;
if (!cancelRequested || s->Type != SYS_SOCK_STREAM) {
err_t err = netconn_connect(s->Conn, &ip, address->Port);
if (err == ERR_OK)
s->Connected = 1;
LwipSocketUnlock(s);
return LwipErrToStatus(err);
LwipSocketUnlock(s);
return LwipErrToStatus(err);
}
u8_t wasNonBlocking = netconn_is_nonblocking(s->Conn) ? 1 : 0;
netconn_set_nonblocking(s->Conn, 1);
err_t err = netconn_connect(s->Conn, &ip, address->Port);
if (err == ERR_OK) {
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
s->Connected = 1;
LwipSocketUnlock(s);
return STATUS_SUCCESS;
}
if (err != ERR_INPROGRESS) {
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
LwipSocketUnlock(s);
return LwipErrToStatus(err);
}
for (;;) {
if (LwipSocketCancelRequested(cancelRequested)) {
(void)netconn_close(s->Conn);
s->ShutdownRead = 1;
s->ShutdownWrite = 1;
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
LwipSocketUnlock(s);
return STATUS_CANCELLED;
}
if (s->Conn->state != NETCONN_CONNECT) {
err = netconn_err(s->Conn);
netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking);
if (err == ERR_OK) {
s->Connected = 1;
LwipSocketUnlock(s);
return STATUS_SUCCESS;
}
LwipSocketUnlock(s);
return LwipErrToStatus(err);
}
LwipSocketUnlock(s);
(void)KeDelayThread(LWIP_PROVIDER_CANCEL_POLL_MS);
st = LwipSocketLock(s);
if (!KSUCCESS(st))
return st;
}
}
// Implements the provider Send operation.
@@ -1087,7 +1240,7 @@ static KSTATUS LwipReceiveFromBuffered(LWIP_PROVIDER_SOCKET* s, uint8_t* out,
}
// Implements the provider Receive operation.
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut) {
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (receivedOut)
*receivedOut = 0;
@@ -1124,7 +1277,12 @@ static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, ui
if (copied == 0 && !s->ReceiveBuffer) {
struct netbuf* nb = NULL;
err_t err = netconn_recv(s->Conn, &nb);
err_t err = ERR_OK;
st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err);
if (!KSUCCESS(st)) {
LwipSocketUnlock(s);
return st;
}
if (err == ERR_CLSD) {
s->ShutdownRead = 1;
if (receivedOut)
@@ -1209,7 +1367,7 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
}
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (receivedOut)
*receivedOut = 0;
@@ -1236,7 +1394,12 @@ static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length
}
struct netbuf* nb = NULL;
err_t err = netconn_recv(s->Conn, &nb);
err_t err = ERR_OK;
st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err);
if (!KSUCCESS(st)) {
LwipSocketUnlock(s);
return st;
}
if (err != ERR_OK) {
LwipSocketUnlock(s);
return LwipErrToStatus(err);
@@ -1449,6 +1612,109 @@ static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SO
return STATUS_SUCCESS;
}
// Dispatches a provider-private socket request carried by an IRP.
static KSTATUS LwipDispatchSocketRequest(NET_SOCKET_IRP_REQUEST* request) {
if (!request || request->Size != sizeof(*request))
return STATUS_INVALID_PARAMETER;
switch (request->Operation) {
case NetSocketIrpCreate:
return LwipProviderSocketCreate(request->u.Create.Domain,
request->u.Create.Type,
request->u.Create.Protocol,
request->u.Create.ProviderSocketOut);
case NetSocketIrpClose:
LwipProviderSocketClose(request->u.Close.Socket);
return STATUS_SUCCESS;
case NetSocketIrpBind:
return LwipProviderBind(request->u.Bind.Socket,
request->u.Bind.Address,
request->u.Bind.AddressSize);
case NetSocketIrpListen:
return LwipProviderListen(request->u.Listen.Socket,
request->u.Listen.Backlog);
case NetSocketIrpAccept:
return LwipProviderAccept(request->u.Accept.Socket,
request->u.Accept.AcceptedOut,
request->u.Accept.RemoteOut,
request->CancelRequested);
case NetSocketIrpConnect:
return LwipProviderConnect(request->u.Connect.Socket,
request->u.Connect.Address,
request->u.Connect.AddressSize,
request->CancelRequested);
case NetSocketIrpSend:
return LwipProviderSend(request->u.Send.Socket,
request->u.Send.Buffer,
request->u.Send.Length,
request->u.Send.Flags,
request->u.Send.SentOut);
case NetSocketIrpReceive:
return LwipProviderReceive(request->u.Receive.Socket,
request->u.Receive.Buffer,
request->u.Receive.Length,
request->u.Receive.Flags,
request->u.Receive.ReceivedOut,
request->CancelRequested);
case NetSocketIrpSendTo:
return LwipProviderSendTo(request->u.SendTo.Socket,
request->u.SendTo.Buffer,
request->u.SendTo.Length,
request->u.SendTo.Flags,
request->u.SendTo.Remote,
request->u.SendTo.RemoteSize,
request->u.SendTo.SentOut);
case NetSocketIrpReceiveFrom:
return LwipProviderReceiveFrom(request->u.ReceiveFrom.Socket,
request->u.ReceiveFrom.Buffer,
request->u.ReceiveFrom.Length,
request->u.ReceiveFrom.Flags,
request->u.ReceiveFrom.RemoteOut,
request->u.ReceiveFrom.ReceivedOut,
request->CancelRequested);
case NetSocketIrpShutdown:
return LwipProviderShutdown(request->u.Shutdown.Socket,
request->u.Shutdown.How);
case NetSocketIrpSetOption:
return LwipProviderSetOption(request->u.SetOption.Socket,
request->u.SetOption.Option,
request->u.SetOption.Value,
request->u.SetOption.ValueSize);
case NetSocketIrpGetOption:
return LwipProviderGetOption(request->u.GetOption.Socket,
request->u.GetOption.Option,
request->u.GetOption.Value,
request->u.GetOption.ValueSize,
request->u.GetOption.ActualSizeOut);
case NetSocketIrpPoll:
return LwipProviderPoll(request->u.Poll.Socket,
request->u.Poll.Events,
request->u.Poll.ReturnedEventsOut);
case NetSocketIrpResolveHost:
return LwipProviderResolveHost(request->u.ResolveHost.Name,
request->u.ResolveHost.Family,
request->u.ResolveHost.AddressOut);
default:
return STATUS_INVALID_PARAMETER;
}
}
// Dispatches socket internal device controls for the lwIP stack device.
static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp) {
(void)device;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios)
return STATUS_INVALID_PARAMETER;
if (ios->Parameters.DeviceIoControl.IoControlCode != NET_IOCTL_SOCKET)
return STATUS_NOT_SUPPORTED;
if (ios->Parameters.DeviceIoControl.InputLength < sizeof(NET_SOCKET_IRP_REQUEST))
return STATUS_BUFFER_TOO_SMALL;
NET_SOCKET_IRP_REQUEST* request = (NET_SOCKET_IRP_REQUEST*)ios->Parameters.DeviceIoControl.InputBuffer;
return LwipDispatchSocketRequest(request);
}
// Implements the driver Entry operation.
KSTATUS DriverEntry(const HAL_API* hal) {
DbgPrint("lwIP: driver loading...\n");
@@ -1467,6 +1733,23 @@ KSTATUS DriverEntry(const HAL_API* hal) {
return st;
}
IoInitializeDriverObject(&gLwipDriver, SYS_SV_LITERAL("lwip-stack"));
gLwipDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = LwipDispatchInternalDeviceControl;
st = IoRegisterDriverObject(&gLwipDriver);
if (!KSUCCESS(st)) {
DbgPrint("lwIP: driver object registration failed: 0x%x\n", st);
return st;
}
st = IoCreateDevice(&gLwipDriver, 0, &gLwipDevice);
if (!KSUCCESS(st)) {
DbgPrint("lwIP: stack device creation failed: 0x%x\n", st);
return st;
}
(void)IoSetDeviceName(gLwipDevice, SYS_SV_LITERAL("lwip-stack"));
gLwipProvider.StackDevice = gLwipDevice;
st = NetRegisterStackProvider(&gLwipProvider);
if (!KSUCCESS(st)) {
DbgPrint("lwIP: provider registration failed: 0x%x\n", st);
+88
View File
@@ -0,0 +1,88 @@
// Kernel async I/O request declarations shared by implementation files and kernel clients.
#pragma once
// io_async.h - waitable asynchronous I/O request objects.
// Async request objects own the common completion, APC, wait, query, and
// cancellation surface used by file and socket I/O.
#include <stddef.h>
#include <stdint.h>
#include <kernel/ke_list.h>
#include <kernel/ob_object.h>
#include <ntlite/status.h>
struct EVENT;
struct KAPC;
struct IRP;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
// Completion status visible to async callers.
typedef struct IO_STATUS_BLOCK {
volatile KSTATUS Status;
volatile size_t Information;
} IO_STATUS_BLOCK;
typedef enum IO_ASYNC_REQUEST_STATE {
IoAsyncRequestQueued = 0,
IoAsyncRequestRunning = 1,
IoAsyncRequestCompleted = 2,
IoAsyncRequestCancelled = 3,
} IO_ASYNC_REQUEST_STATE;
typedef struct IO_ASYNC_REQUEST_OBJECT IO_ASYNC_REQUEST_OBJECT;
typedef void (*IO_ASYNC_REQUEST_CLEANUP_ROUTINE)(IO_ASYNC_REQUEST_OBJECT* request,
void* context);
typedef KSTATUS (*IO_ASYNC_REQUEST_CANCEL_ROUTINE)(IO_ASYNC_REQUEST_OBJECT* request,
void* context);
// Common waitable async I/O request object.
struct IO_ASYNC_REQUEST_OBJECT {
OBJECT Header;
LIST_ENTRY Link;
IO_ASYNC_REQUEST_STATE State;
uint8_t CancelRequested;
struct EVENT* WaitEvent;
struct EVENT* CompletionEvent;
struct KAPC* CompletionApc;
IO_STATUS_BLOCK* Iosb;
IO_STATUS_BLOCK IosbSnapshot;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
struct IRP* Irp;
IO_ASYNC_REQUEST_CLEANUP_ROUTINE CleanupRoutine;
void* CleanupContext;
IO_ASYNC_REQUEST_CANCEL_ROUTINE CancelRoutine;
void* CancelContext;
};
__declspec(dllexport) KSTATUS IoCreateAsyncRequest(size_t objectSize,
IO_ASYNC_REQUEST_CLEANUP_ROUTINE cleanupRoutine,
void* cleanupContext,
IO_ASYNC_REQUEST_CANCEL_ROUTINE cancelRoutine,
void* cancelContext,
IO_ASYNC_REQUEST_OBJECT** requestOut);
__declspec(dllexport) OBJECT* IoAsyncRequestObject(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) void IoInitializeAsyncRequestCompletion(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosb,
struct EVENT* completionEvent,
struct KAPC* completionApc,
MM_LOCKED_USER_BUFFER* lockedBuffer,
MM_LOCKED_USER_BUFFER* lockedIosb);
__declspec(dllexport) void IoAsyncRequestSetState(IO_ASYNC_REQUEST_OBJECT* request,
IO_ASYNC_REQUEST_STATE state);
__declspec(dllexport) IO_ASYNC_REQUEST_STATE IoAsyncRequestGetState(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) void IoAsyncRequestRequestCancel(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) uint8_t IoAsyncRequestIsCancelRequested(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) struct EVENT* IoGetAsyncRequestWaitEvent(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoWaitAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
uint32_t flags);
__declspec(dllexport) KSTATUS IoCancelAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoCompleteAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
KSTATUS status,
size_t information);
__declspec(dllexport) KSTATUS IoQueryAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut);
+1 -6
View File
@@ -10,6 +10,7 @@
#include <ntlite/status.h>
#include <kernel/ob_object.h>
#include <kernel/ke_spinlock.h>
#include <kernel/io_async.h>
#include <ntlite/string_view.h>
#include <ntlite/dirent.h>
#include <ntlite/file_stat.h>
@@ -137,12 +138,6 @@ typedef struct IO_FILESYSTEM {
void (*CloseDir)(struct IO_FILESYSTEM* fs, void* dirCtx);
} IO_FILESYSTEM;
// IO_STATUS_BLOCK - completion status visible to async callers.
typedef struct IO_STATUS_BLOCK {
volatile KSTATUS Status;
volatile size_t Information;
} IO_STATUS_BLOCK;
struct EVENT;
struct KAPC;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
+9 -3
View File
@@ -14,8 +14,9 @@
#define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256
#define PROCESS_MODULE_MAX 16
#define PROCESS_CWD_MAX 512
#define PROCESS_MODULE_MAX 16
#define PROCESS_MODULE_NAME_MAX 64
#define PROCESS_CWD_MAX 512
#define KE_PROCESS_FLAG_SYSTEM 0x00000001u
#define KE_PROCESS_FLAG_IDLE 0x00000002u
@@ -38,9 +39,13 @@ typedef struct HANDLE_ENTRY {
} HANDLE_ENTRY;
typedef struct PROCESS_MODULE {
char Name[32];
char Name[PROCESS_MODULE_NAME_MAX];
uintptr_t Base;
size_t Size;
uintptr_t EntryPoint;
uint32_t Flags;
uint32_t PublicRefCount;
uint32_t ImportRefCount;
} PROCESS_MODULE;
typedef struct PROCESS {
@@ -93,6 +98,7 @@ typedef struct PROCESS {
// Per-process loaded modules (user processes only).
PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
uint32_t ModuleCount;
SPINLOCK ModuleLock;
// Signaled (manual-reset) when the process exits.
// Waiters block on this via KeWaitForEvent.
+9 -1
View File
@@ -10,6 +10,7 @@
struct PROCESS;
struct FILE_OBJECT;
struct SYS_MODULE_INFO;
// Map a PE32+ image from a bootloader/driver raw buffer into kernel space.
// User images must be loaded from FILE_OBJECTs so image sections have stable identity.
@@ -38,7 +39,14 @@ KSTATUS LdrProtectImageForProcess(struct PROCESS* process, uintptr_t imageBase,
// Load a DLL for a specific process, resolve it against that process's
// loaded modules, and register it in the process-local registry.
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
#define LDR_MODULE_LOAD_IMPORT 0x00000001u
#define LDR_MODULE_LOAD_PUBLIC 0x00000002u
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uint32_t loadFlags, uintptr_t* baseOut);
KSTATUS LdrUnloadModuleForProcess(struct PROCESS* process, uintptr_t imageBase);
KSTATUS LdrQueryModuleForProcess(struct PROCESS* process, uintptr_t imageBase, struct SYS_MODULE_INFO* infoOut);
KSTATUS LdrCopyProcessModuleInfo(struct PROCESS* process, struct SYS_MODULE_INFO* infoOut, size_t maxCount, size_t* countOut);
void* LdrGetModuleExportForProcess(struct PROCESS* process, uintptr_t imageBase, const char* functionName);
// Called by KernelMain to register all modules from BOOT_INFO so that
// LdrGetExport can find them.
+54 -4
View File
@@ -11,8 +11,13 @@
#include <ntlite/status.h>
#include <kernel/ob_object.h>
#include <ntlite/ddi/net_abi.h>
#include <kernel/io_async.h>
struct NET_STACK_PROVIDER;
struct EVENT;
struct KAPC;
struct PROCESS;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
typedef struct SOCKET_OBJECT {
OBJECT Header; // must be first
@@ -25,17 +30,62 @@ typedef struct SOCKET_OBJECT {
uint32_t Protocol;
} SOCKET_OBJECT;
typedef struct NET_ASYNC_SOCKET_SUBMIT {
struct PROCESS* OwnerProcess;
SOCKET_OBJECT* Socket;
uint32_t Operation;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* KernelBuffer;
void* UserBuffer;
size_t Length;
SYS_SOCKET_ADDRESS_K Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
void* UserOptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
IO_STATUS_BLOCK* Iosb;
struct EVENT* CompletionEvent;
struct KAPC* CompletionApc;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
MM_LOCKED_USER_BUFFER* LockedAddressOut;
MM_LOCKED_USER_BUFFER* LockedAddressSize;
MM_LOCKED_USER_BUFFER* LockedOptionValue;
MM_LOCKED_USER_BUFFER* LockedOptionActualSize;
MM_LOCKED_USER_BUFFER* LockedHandleOut;
MM_LOCKED_USER_BUFFER* LockedPollReturnedEvents;
} NET_ASYNC_SOCKET_SUBMIT;
typedef struct NET_ASYNC_SOCKET_REQUEST_OBJECT NET_ASYNC_SOCKET_REQUEST_OBJECT;
KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKET_OBJECT** out);
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketShutdown(SOCKET_OBJECT* socket, uint32_t how);
KSTATUS NetSocketSetOption(SOCKET_OBJECT* socket, uint32_t option, const void* value, size_t valueSize);
KSTATUS NetSocketGetOption(SOCKET_OBJECT* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returnedEventsOut);
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
KSTATUS NetSocketSubmitAsyncKernel(const NET_ASYNC_SOCKET_SUBMIT* submit,
NET_ASYNC_SOCKET_REQUEST_OBJECT** requestOut);
OBJECT* NetAsyncSocketRequestObject(NET_ASYNC_SOCKET_REQUEST_OBJECT* request);
+1
View File
@@ -28,6 +28,7 @@ typedef enum OBJECT_TYPE {
ObjectTypeInputQueue = 17,
ObjectTypeImageSection = 18,
ObjectTypeTimer = 19,
ObjectTypeModule = 20,
} OBJECT_TYPE;
typedef struct OBJECT {
+111 -105
View File
@@ -7,7 +7,7 @@
// R10 and R11 carry args 4-5 and are loaded by six-argument stubs.
// RAX carries the return value.
// Process, threads, waits, and APC.
// Process, threads, modules, waits, and APC.
#define SYS_EXIT_PROCESS 0
#define SYS_EXIT_THREAD 1
#define SYS_CREATE_PROCESS 2
@@ -18,124 +18,130 @@
#define SYS_GET_PROCESS_PID 7
#define SYS_CREATE_THREAD 8
#define SYS_GET_THREAD_LIST 9
#define SYS_GET_CURRENT_TID 10
#define SYS_GET_CURRENT_PID 11
#define SYS_SET_THREAD_PRIORITY 12
#define SYS_YIELD 13
#define SYS_SLEEP 14
#define SYS_WAIT_SINGLE 15
#define SYS_WAIT_MANY 16
#define SYS_SET_USER_APC_DISPATCHER 17
#define SYS_APC_RETURN 18
#define SYS_LOAD_MODULE 10
#define SYS_UNLOAD_MODULE 11
#define SYS_GET_MODULE_INFO 12
#define SYS_GET_MODULE_EXPORT 13
#define SYS_GET_MODULE_LIST 14
#define SYS_GET_CURRENT_TID 15
#define SYS_GET_CURRENT_PID 16
#define SYS_SET_THREAD_PRIORITY 17
#define SYS_YIELD 18
#define SYS_SLEEP 19
#define SYS_WAIT_SINGLE 20
#define SYS_WAIT_MANY 21
#define SYS_SET_USER_APC_DISPATCHER 22
#define SYS_APC_RETURN 23
// Virtual memory, mappings, and handles.
#define SYS_ALLOC_VM 19
#define SYS_FREE_VM 20
#define SYS_PROTECT_VM 21
#define SYS_QUERY_VM 22
#define SYS_CLOSE_HANDLE 23
#define SYS_DUPLICATE_HANDLE 24
#define SYS_CREATE_FILE_MAPPING 25
#define SYS_MAP_FILE 26
#define SYS_ALLOC_VM 24
#define SYS_FREE_VM 25
#define SYS_PROTECT_VM 26
#define SYS_QUERY_VM 27
#define SYS_CLOSE_HANDLE 28
#define SYS_DUPLICATE_HANDLE 29
#define SYS_CREATE_FILE_MAPPING 30
#define SYS_MAP_FILE 31
// Synchronization objects.
#define SYS_CREATE_MUTEX 27
#define SYS_TRY_ACQUIRE_MUTEX 28
#define SYS_RELEASE_MUTEX 29
#define SYS_CREATE_SEM 30
#define SYS_TRY_WAIT_SEM 31
#define SYS_SIGNAL_SEM 32
#define SYS_CREATE_EVENT 33
#define SYS_SIGNAL_EVENT 34
#define SYS_CLEAR_EVENT 35
#define SYS_CREATE_MUTEX 32
#define SYS_TRY_ACQUIRE_MUTEX 33
#define SYS_RELEASE_MUTEX 34
#define SYS_CREATE_SEM 35
#define SYS_TRY_WAIT_SEM 36
#define SYS_SIGNAL_SEM 37
#define SYS_CREATE_EVENT 38
#define SYS_SIGNAL_EVENT 39
#define SYS_CLEAR_EVENT 40
// Queues and named queues.
#define SYS_CREATE_QUEUE 36
#define SYS_SEND_QUEUE 37
#define SYS_RECV_QUEUE 38
#define SYS_PUBLISH_QUEUE 39
#define SYS_OPEN_NAMED_QUEUE 40
#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 41
#define SYS_CREATE_QUEUE 41
#define SYS_SEND_QUEUE 42
#define SYS_RECV_QUEUE 43
#define SYS_PUBLISH_QUEUE 44
#define SYS_OPEN_NAMED_QUEUE 45
#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 46
// Files, directories, and current directory.
#define SYS_OPEN_FILE 42
#define SYS_READ_FILE 43
#define SYS_WRITE_FILE 44
#define SYS_READ_FILE_ASYNC 45
#define SYS_WRITE_FILE_ASYNC 46
#define SYS_CANCEL_IO 47
#define SYS_SEEK_FILE 48
#define SYS_GET_FILE_POS 49
#define SYS_GET_FILE_SIZE 50
#define SYS_GET_FILE_STAT 51
#define SYS_STAT_FILE 52
#define SYS_TRUNCATE_FILE 53
#define SYS_FLUSH_FILE 54
#define SYS_DELETE_FILE 55
#define SYS_RENAME_FILE 56
#define SYS_LOCK_FILE 57
#define SYS_UNLOCK_FILE 58
#define SYS_OPEN_DIR 59
#define SYS_READ_DIR 60
#define SYS_CREATE_DIR 61
#define SYS_REMOVE_DIR 62
#define SYS_SET_CWD 63
#define SYS_REAL_PATH 64
#define SYS_OPEN_FILE 47
#define SYS_READ_FILE 48
#define SYS_WRITE_FILE 49
#define SYS_READ_FILE_ASYNC 50
#define SYS_WRITE_FILE_ASYNC 51
#define SYS_CANCEL_IO 52
#define SYS_SEEK_FILE 53
#define SYS_GET_FILE_POS 54
#define SYS_GET_FILE_SIZE 55
#define SYS_GET_FILE_STAT 56
#define SYS_STAT_FILE 57
#define SYS_TRUNCATE_FILE 58
#define SYS_FLUSH_FILE 59
#define SYS_DELETE_FILE 60
#define SYS_RENAME_FILE 61
#define SYS_LOCK_FILE 62
#define SYS_UNLOCK_FILE 63
#define SYS_OPEN_DIR 64
#define SYS_READ_DIR 65
#define SYS_CREATE_DIR 66
#define SYS_REMOVE_DIR 67
#define SYS_SET_CWD 68
#define SYS_REAL_PATH 69
// Console, display, and surfaces.
#define SYS_WRITE_CONSOLE 65
#define SYS_GET_CONSOLE_INFO 66
#define SYS_SET_CONSOLE_CURSOR 67
#define SYS_FILL_CONSOLE_CHARACTER 68
#define SYS_FILL_CONSOLE_ATTRIBUTE 69
#define SYS_PUT_SCREEN_CHAR 70
#define SYS_CLEAR_SCREEN 71
#define SYS_GET_SCREEN_SIZE 72
#define SYS_QUERY_DISPLAY_INFO 73
#define SYS_CREATE_SURFACE 74
#define SYS_MAP_SURFACE 75
#define SYS_QUERY_SURFACE 76
#define SYS_PRESENT_SURFACE 77
#define SYS_WRITE_CONSOLE 70
#define SYS_GET_CONSOLE_INFO 71
#define SYS_SET_CONSOLE_CURSOR 72
#define SYS_FILL_CONSOLE_CHARACTER 73
#define SYS_FILL_CONSOLE_ATTRIBUTE 74
#define SYS_PUT_SCREEN_CHAR 75
#define SYS_CLEAR_SCREEN 76
#define SYS_GET_SCREEN_SIZE 77
#define SYS_QUERY_DISPLAY_INFO 78
#define SYS_CREATE_SURFACE 79
#define SYS_MAP_SURFACE 80
#define SYS_QUERY_SURFACE 81
#define SYS_PRESENT_SURFACE 82
// Input.
#define SYS_POLL_KEYBOARD 78
#define SYS_IS_KEY_DOWN 79
#define SYS_READ_KEYBOARD_CHAR 80
#define SYS_READ_KEY 81
#define SYS_GET_KEY_MODIFIERS 82
#define SYS_OPEN_INPUT_QUEUE 83
#define SYS_READ_INPUT_EVENT 84
#define SYS_PEEK_INPUT_EVENT 85
#define SYS_PEEK_MOUSE 86
#define SYS_READ_MOUSE 87
#define SYS_GET_MOUSE_POS 88
#define SYS_POLL_KEYBOARD 83
#define SYS_IS_KEY_DOWN 84
#define SYS_READ_KEYBOARD_CHAR 85
#define SYS_READ_KEY 86
#define SYS_GET_KEY_MODIFIERS 87
#define SYS_OPEN_INPUT_QUEUE 88
#define SYS_READ_INPUT_EVENT 89
#define SYS_PEEK_INPUT_EVENT 90
#define SYS_PEEK_MOUSE 91
#define SYS_READ_MOUSE 92
#define SYS_GET_MOUSE_POS 93
// Time, debug, and system information.
#define SYS_CREATE_TIMER 89
#define SYS_SET_TIMER 90
#define SYS_CANCEL_TIMER 91
#define SYS_GET_TICK 92
#define SYS_GET_TICK_FREQUENCY 93
#define SYS_GET_SYSTEM_TIME 94
#define SYS_PRINT_DEBUG 95
#define SYS_QUERY_TRAP_FRAME 96
#define SYS_GET_MEMORY_STATUS 97
#define SYS_QUERY_KERNEL_STATS 98
#define SYS_CREATE_TIMER 94
#define SYS_SET_TIMER 95
#define SYS_CANCEL_TIMER 96
#define SYS_GET_TICK 97
#define SYS_GET_TICK_FREQUENCY 98
#define SYS_GET_SYSTEM_TIME 99
#define SYS_PRINT_DEBUG 100
#define SYS_QUERY_TRAP_FRAME 101
#define SYS_GET_MEMORY_STATUS 102
#define SYS_QUERY_KERNEL_STATS 103
// Native sockets and polling.
#define SYS_CREATE_SOCKET 99
#define SYS_BIND_SOCKET 100
#define SYS_LISTEN_SOCKET 101
#define SYS_ACCEPT_SOCKET 102
#define SYS_CONNECT_SOCKET 103
#define SYS_SEND_SOCKET 104
#define SYS_RECV_SOCKET 105
#define SYS_SHUTDOWN_SOCKET 106
#define SYS_SEND_TO_SOCKET 107
#define SYS_RECV_FROM_SOCKET 108
#define SYS_RESOLVE_HOST 109
#define SYS_SET_SOCKET_OPTION 110
#define SYS_GET_SOCKET_OPTION 111
#define SYS_POLL 112
#define SYS_CREATE_SOCKET 104
#define SYS_SOCKET_IO_ASYNC 105
#define SYS_BIND_SOCKET 106
#define SYS_LISTEN_SOCKET 107
#define SYS_ACCEPT_SOCKET 108
#define SYS_CONNECT_SOCKET 109
#define SYS_SEND_SOCKET 110
#define SYS_RECV_SOCKET 111
#define SYS_SHUTDOWN_SOCKET 112
#define SYS_SEND_TO_SOCKET 113
#define SYS_RECV_FROM_SOCKET 114
#define SYS_RESOLVE_HOST 115
#define SYS_SET_SOCKET_OPTION 116
#define SYS_GET_SOCKET_OPTION 117
#define SYS_POLL 118
#define SYS_MAX 113
#define SYS_MAX 119
+202
View File
@@ -0,0 +1,202 @@
// Kernel asynchronous I/O request object support.
// This file owns the common wait, completion, cancellation, APC, IOSB, and
// locked-buffer lifetime rules for all async I/O families.
#include <string.h>
#include <kernel/io_async.h>
#include <kernel/ke_event.h>
#include <kernel/ke_apc.h>
#include <kernel/mm_virt.h>
#include <ntlite/syscall.h>
// Releases resources owned by the common async request object.
static void KiIoAsyncRequestDestroy(OBJECT* obj) {
IO_ASYNC_REQUEST_OBJECT* request =
CONTAINING_RECORD(obj, IO_ASYNC_REQUEST_OBJECT, Header);
if (request->CleanupRoutine)
request->CleanupRoutine(request, request->CleanupContext);
if (request->WaitEvent) {
ObDereferenceObject(&request->WaitEvent->Header);
request->WaitEvent = (struct EVENT*)0;
}
if (request->CompletionEvent) {
ObDereferenceObject(&request->CompletionEvent->Header);
request->CompletionEvent = (struct EVENT*)0;
}
if (request->LockedBuffer) {
MmUnlockUserBuffer(request->LockedBuffer);
request->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (request->LockedIosb) {
MmUnlockUserBuffer(request->LockedIosb);
request->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Allocates a typed async request object with the common header first.
__declspec(dllexport) KSTATUS IoCreateAsyncRequest(size_t objectSize,
IO_ASYNC_REQUEST_CLEANUP_ROUTINE cleanupRoutine,
void* cleanupContext,
IO_ASYNC_REQUEST_CANCEL_ROUTINE cancelRoutine,
void* cancelContext,
IO_ASYNC_REQUEST_OBJECT** requestOut) {
if (!requestOut) return STATUS_INVALID_PARAMETER;
*requestOut = (IO_ASYNC_REQUEST_OBJECT*)0;
if (objectSize < sizeof(IO_ASYNC_REQUEST_OBJECT)) return STATUS_INVALID_PARAMETER;
IO_ASYNC_REQUEST_OBJECT* request =
(IO_ASYNC_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo, objectSize);
if (!request) return STATUS_NO_MEMORY;
request->Header.Destroy = KiIoAsyncRequestDestroy;
LIST_INIT(&request->Link);
request->State = IoAsyncRequestQueued;
request->CancelRequested = 0;
request->IosbSnapshot.Status = STATUS_PENDING;
request->IosbSnapshot.Information = 0;
request->CleanupRoutine = cleanupRoutine;
request->CleanupContext = cleanupContext;
request->CancelRoutine = cancelRoutine;
request->CancelContext = cancelContext;
KSTATUS status = KeEventCreate(EventManualReset, 0, &request->WaitEvent);
if (!KSUCCESS(status)) {
ObDereferenceObject(&request->Header);
return status;
}
*requestOut = request;
return STATUS_SUCCESS;
}
// Returns the object header embedded in an async request.
__declspec(dllexport) OBJECT* IoAsyncRequestObject(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? &request->Header : (OBJECT*)0;
}
// Attaches caller-visible completion state to a new async request.
__declspec(dllexport) void IoInitializeAsyncRequestCompletion(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosb,
EVENT* completionEvent,
KAPC* completionApc,
MM_LOCKED_USER_BUFFER* lockedBuffer,
MM_LOCKED_USER_BUFFER* lockedIosb) {
if (!request) return;
request->CompletionEvent = completionEvent;
request->CompletionApc = completionApc;
request->Iosb = iosb;
request->LockedBuffer = lockedBuffer;
request->LockedIosb = lockedIosb;
request->IosbSnapshot.Status = STATUS_PENDING;
request->IosbSnapshot.Information = 0;
if (iosb) {
iosb->Status = STATUS_PENDING;
iosb->Information = 0;
}
if (completionEvent)
ObReferenceObject(&completionEvent->Header);
}
// Updates the lifecycle state owned by the request's operation family.
__declspec(dllexport) void IoAsyncRequestSetState(IO_ASYNC_REQUEST_OBJECT* request,
IO_ASYNC_REQUEST_STATE state) {
if (request) request->State = state;
}
// Returns the last lifecycle state published by the operation family.
__declspec(dllexport) IO_ASYNC_REQUEST_STATE IoAsyncRequestGetState(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->State : IoAsyncRequestCancelled;
}
// Marks a request as cancelled so providers can observe it before completion.
__declspec(dllexport) void IoAsyncRequestRequestCancel(IO_ASYNC_REQUEST_OBJECT* request) {
if (request) request->CancelRequested = 1;
}
// Returns whether cancellation has been requested for the async request.
__declspec(dllexport) uint8_t IoAsyncRequestIsCancelRequested(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->CancelRequested : 0;
}
// Returns the wait event owned by an async request.
__declspec(dllexport) EVENT* IoGetAsyncRequestWaitEvent(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->WaitEvent : (EVENT*)0;
}
// Waits for an async request to complete according to wait flags.
__declspec(dllexport) KSTATUS IoWaitAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
uint32_t flags) {
if (!request || !request->WaitEvent) return STATUS_INVALID_PARAMETER;
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
return KeWaitForEvent(request->WaitEvent,
(flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
}
// Cancels an async request through its operation-family cancel routine.
__declspec(dllexport) KSTATUS IoCancelAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request) {
if (!request) return STATUS_INVALID_PARAMETER;
if (request->CancelRoutine)
return request->CancelRoutine(request, request->CancelContext);
request->CancelRequested = 1;
if (request->State == IoAsyncRequestCompleted ||
request->State == IoAsyncRequestCancelled)
return STATUS_OBJECT_DEAD;
return STATUS_PENDING;
}
// Completes an async request and publishes all caller-visible completion state.
__declspec(dllexport) KSTATUS IoCompleteAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
KSTATUS status,
size_t information) {
if (!request) return STATUS_INVALID_PARAMETER;
request->IosbSnapshot.Status = status;
request->IosbSnapshot.Information = information;
request->State = (status == STATUS_CANCELLED) ? IoAsyncRequestCancelled
: IoAsyncRequestCompleted;
if (request->Iosb) {
request->Iosb->Status = status;
request->Iosb->Information = information;
}
// User APCs run against the same IOSB state that waiters will observe.
if (request->CompletionApc) {
if (!KeInsertQueueApc(request->CompletionApc,
request->Iosb,
(void*)(uintptr_t)status)) {
if (request->CompletionApc->RundownRoutine)
request->CompletionApc->RundownRoutine(request->CompletionApc);
}
}
if (request->CompletionEvent)
KeSignalEvent(request->CompletionEvent);
if (request->WaitEvent)
KeSignalEvent(request->WaitEvent);
if (request->LockedBuffer) {
MmUnlockUserBuffer(request->LockedBuffer);
request->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (request->LockedIosb) {
MmUnlockUserBuffer(request->LockedIosb);
request->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
return STATUS_SUCCESS;
}
// Copies the last completion snapshot out of an async request.
__declspec(dllexport) KSTATUS IoQueryAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut) {
if (!request || !iosbOut) return STATUS_INVALID_PARAMETER;
iosbOut->Status = request->IosbSnapshot.Status;
iosbOut->Information = request->IosbSnapshot.Information;
return STATUS_SUCCESS;
}
+77 -166
View File
@@ -32,31 +32,12 @@ typedef enum IO_ASYNC_FILE_OP {
IoAsyncFileWrite = 1,
} IO_ASYNC_FILE_OP;
typedef enum IO_ASYNC_FILE_STATE {
IoAsyncStateQueued = 0,
IoAsyncStateRunning = 1,
IoAsyncStateCompleted = 2,
IoAsyncStateCancelled = 3,
} IO_ASYNC_FILE_STATE;
struct IO_ASYNC_FILE_REQUEST_OBJECT {
OBJECT Header; // ObjectTypeAsyncIo
LIST_ENTRY Link; // protected by gAsyncFileQueueLock
IO_ASYNC_REQUEST_OBJECT Async;
IO_ASYNC_FILE_OP Op;
IO_ASYNC_FILE_STATE State;
uint8_t CancelRequested;
FILE_OBJECT* File; // referenced until object destruction
EVENT* WaitEvent; // internal completion event
EVENT* CompletionEvent; // optional, referenced until object destruction
KAPC* CompletionApc; // caller-owned APC storage
IO_STATUS_BLOCK* Iosb; // caller/user-owned completion block
IO_STATUS_BLOCK IosbSnapshot;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
void* Buffer; // caller-owned; user buffers must be locked until completion/cancel
FILE_OBJECT* File;
void* Buffer;
size_t Size;
uint64_t Offset;
};
@@ -228,72 +209,45 @@ static void KiIoSignalFileLockWaitersLocked(const IO_FILE_IDENTITY* identity) {
}
}
// Implements the io Async File Request Destroy operation.
static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
// Releases file-specific references owned by an async file request.
static void KiIoAsyncFileRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(obj, IO_ASYNC_FILE_REQUEST_OBJECT, Header);
(IO_ASYNC_FILE_REQUEST_OBJECT*)async;
if (req->WaitEvent) {
ObDereferenceObject(&req->WaitEvent->Header);
req->WaitEvent = (EVENT*)0;
}
if (req->CompletionEvent) {
ObDereferenceObject(&req->CompletionEvent->Header);
req->CompletionEvent = (EVENT*)0;
}
if (req->File) {
ObDereferenceObject(&req->File->Header);
req->File = (FILE_OBJECT*)0;
}
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
}
static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
KSTATUS status,
size_t information) {
if (!req) return;
req->IosbSnapshot.Status = status;
req->IosbSnapshot.Information = information;
// Cancels an async file request from the common async request surface.
static KSTATUS KiIoCancelAsyncFileRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
if (!async) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = (status == STATUS_CANCELLED) ? IoAsyncStateCancelled : IoAsyncStateCompleted;
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
if (state == IoAsyncRequestQueued) {
ListRemove(&async->Link);
IoAsyncRequestRequestCancel(async);
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
ObDereferenceObject(&async->Header);
return STATUS_SUCCESS;
}
if (state == IoAsyncRequestRunning) {
IoAsyncRequestRequestCancel(async);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
if (req->Iosb) {
req->Iosb->Status = status;
req->Iosb->Information = information;
}
// Match IRP completion ordering: queue APC first, then signal the event.
if (req->CompletionApc) {
if (!KeInsertQueueApc(req->CompletionApc, req->Iosb, (void*)(uintptr_t)status)) {
if (req->CompletionApc->RundownRoutine)
req->CompletionApc->RundownRoutine(req->CompletionApc);
}
}
if (req->CompletionEvent)
KeSignalEvent(req->CompletionEvent);
if (req->WaitEvent)
KeSignalEvent(req->WaitEvent);
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
return STATUS_OBJECT_DEAD;
}
// Implements the io Async File Dequeue operation.
@@ -306,12 +260,12 @@ static IO_ASYNC_FILE_REQUEST_OBJECT* KiIoAsyncFileDequeue(void) {
}
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(node, IO_ASYNC_FILE_REQUEST_OBJECT, Link);
(IO_ASYNC_FILE_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
if (req->CancelRequested) {
req->State = IoAsyncStateCancelled;
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
} else {
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return req;
@@ -331,9 +285,9 @@ static void KiIoAsyncFileWorker(void* arg) {
IO_ASYNC_FILE_REQUEST_OBJECT* req = KiIoAsyncFileDequeue();
if (!req) continue;
if (req->CancelRequested || req->State == IoAsyncStateCancelled) {
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue/worker reference
if (IoAsyncRequestIsCancelRequested(&req->Async) || IoAsyncRequestGetState(&req->Async) == IoAsyncRequestCancelled) {
IoCompleteAsyncRequest(&req->Async, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
continue;
}
@@ -346,7 +300,7 @@ static void KiIoAsyncFileWorker(void* arg) {
req->Size, &done);
}
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
// The operation may already have reached the filesystem. The
// completion contract still reports cancellation so waiters/APCs do
// not mistake a cancelled request for useful data.
@@ -354,8 +308,8 @@ static void KiIoAsyncFileWorker(void* arg) {
done = 0;
}
KiIoCompleteAsyncFileRequest(req, s, done);
ObDereferenceObject(&req->Header); // queue/worker reference
IoCompleteAsyncRequest(&req->Async, s, done);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
}
}
@@ -404,13 +358,13 @@ static void KiIoNativeFsAsyncComplete(void* completeContext,
(IO_ASYNC_FILE_REQUEST_OBJECT*)completeContext;
if (!req) return;
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
status = STATUS_CANCELLED;
information = 0;
}
KiIoCompleteAsyncFileRequest(req, status, information);
ObDereferenceObject(&req->Header); // native filesystem provider reference
IoCompleteAsyncRequest(&req->Async, status, information);
ObDereferenceObject(&req->Async.Header); // native filesystem provider reference
}
static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
@@ -443,45 +397,30 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
iosb->Status = STATUS_PENDING;
iosb->Information = 0;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
(IO_ASYNC_FILE_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo,
sizeof(IO_ASYNC_FILE_REQUEST_OBJECT));
if (!req) {
iosb->Status = STATUS_NO_MEMORY;
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
KSTATUS createStatus = IoCreateAsyncRequest(sizeof(IO_ASYNC_FILE_REQUEST_OBJECT),
KiIoAsyncFileRequestCleanup,
(void*)0,
KiIoCancelAsyncFileRequest,
(void*)0,
&async);
if (!KSUCCESS(createStatus)) {
iosb->Status = createStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
return STATUS_NO_MEMORY;
return createStatus;
}
req->Header.Destroy = KiIoAsyncFileRequestDestroy;
LIST_INIT(&req->Link);
IO_ASYNC_FILE_REQUEST_OBJECT* req = (IO_ASYNC_FILE_REQUEST_OBJECT*)async;
req->Op = op;
req->State = IoAsyncStateQueued;
req->CancelRequested = 0;
KSTATUS waitStatus = KeEventCreate(EventManualReset, 0, &req->WaitEvent);
if (!KSUCCESS(waitStatus)) {
iosb->Status = waitStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
ObDereferenceObject(&req->Header);
return waitStatus;
}
req->File = file;
req->CompletionEvent = completionEvent;
req->CompletionApc = completionApc;
req->Iosb = iosb;
req->IosbSnapshot.Status = STATUS_PENDING;
req->IosbSnapshot.Information = 0;
req->LockedBuffer = lockedBuffer;
req->LockedIosb = lockedIosb;
req->Buffer = buffer;
req->Size = size;
req->Offset = offset;
IoInitializeAsyncRequestCompletion(&req->Async, iosb, completionEvent,
completionApc, lockedBuffer, lockedIosb);
ObReferenceObject(&file->Header);
if (completionEvent)
ObReferenceObject(&completionEvent->Header);
IO_FS_ASYNC_COMPLETE complete = KiIoNativeFsAsyncComplete;
KSTATUS ns = STATUS_NOT_SUPPORTED;
@@ -490,10 +429,10 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (hasNative) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
ObReferenceObject(&req->Header); // native filesystem provider reference
ObReferenceObject(&req->Async.Header); // native filesystem provider reference
if (op == IoAsyncFileRead) {
ns = file->Fs->ReadAsync(file->Fs, file->FileCtx, offset, buffer,
size, complete, req);
@@ -505,34 +444,34 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req;
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
}
ObDereferenceObject(&req->Header); // provider rejected synchronously
ObDereferenceObject(&req->Async.Header); // provider rejected synchronously
iosb->Status = ns;
req->IosbSnapshot.Status = ns;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header); // creator reference
req->Async.IosbSnapshot.Status = ns;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header); // creator reference
return ns;
}
KSTATUS s = KiIoEnsureAsyncFileIoStarted();
if (!KSUCCESS(s)) {
iosb->Status = s;
req->IosbSnapshot.Status = s;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header);
req->Async.IosbSnapshot.Status = s;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header);
return s;
}
// Queue ownership keeps the request alive until the worker completes or a
// successful queued cancellation completes it from IoCancelAsyncFileRequest.
ObReferenceObject(&req->Header);
ObReferenceObject(&req->Async.Header);
uintptr_t qflags = KeAcquireSpinlock(&gAsyncFileQueueLock);
ListInsertTail(&gAsyncFileQueue, &req->Link);
ListInsertTail(&gAsyncFileQueue, &req->Async.Link);
KeReleaseSpinlock(&gAsyncFileQueueLock, qflags);
KeSignalSemaphore(gAsyncFileSemaphore);
@@ -540,7 +479,7 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req; // creator reference returned to caller
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
@@ -574,56 +513,28 @@ __declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const vo
// Implements the async File Request Object operation.
__declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? &req->Header : (OBJECT*)0;
return req ? &req->Async.Header : (OBJECT*)0;
}
// Returns the wait event owned by an async file request.
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? req->WaitEvent : (EVENT*)0;
return req ? IoGetAsyncRequestWaitEvent(&req->Async) : (EVENT*)0;
}
// Waits for an async file request to complete according to wait flags.
__declspec(dllexport) KSTATUS IoWaitAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req, uint32_t flags) {
if (!req || !req->WaitEvent) return STATUS_INVALID_PARAMETER;
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
return KeWaitForEvent(req->WaitEvent, (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
return IoWaitAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0, flags);
}
// Cancels async File Request.
// Cancels an async file request through the common async I/O object.
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
if (!req) return STATUS_INVALID_PARAMETER;
IO_ASYNC_FILE_STATE state;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
state = req->State;
if (state == IoAsyncStateQueued) {
ListRemove(&req->Link);
req->CancelRequested = 1;
req->State = IoAsyncStateCancelled;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue reference removed above
return STATUS_SUCCESS;
}
if (state == IoAsyncStateRunning) {
req->CancelRequested = 1;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_OBJECT_DEAD;
return IoCancelAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0);
}
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
IO_STATUS_BLOCK* iosbOut) {
if (!req || !iosbOut) return STATUS_INVALID_PARAMETER;
iosbOut->Status = req->IosbSnapshot.Status;
iosbOut->Information = req->IosbSnapshot.Information;
return STATUS_SUCCESS;
return IoQueryAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0,
iosbOut);
}
// Current Working Directory
+5
View File
@@ -114,7 +114,9 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
info->ImageBase = p->ImageBase;
info->ImageSize = p->ImageSize;
info->PebBase = p->UserPebBase;
uintptr_t modFlags = KeAcquireSpinlock(&p->ModuleLock);
info->ModuleCount = p->ModuleCount;
KeReleaseSpinlock(&p->ModuleLock, modFlags);
uintptr_t handleFlags = KeAcquireSpinlock(&p->HandleLock);
uint32_t threadCount = p->ThreadCount;
@@ -288,7 +290,9 @@ static void KiProcessRundownResources(PROCESS* proc) {
MmVadDestroyAllForProcess(proc);
proc->ImageBase = 0;
proc->ImageSize = 0;
uintptr_t modFlags = KeAcquireSpinlock(&proc->ModuleLock);
proc->ModuleCount = 0;
KeReleaseSpinlock(&proc->ModuleLock, modFlags);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
proc->UserImagePathBase = 0;
@@ -360,6 +364,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
proc->ModuleCount = 0;
proc->ModuleLock = (SPINLOCK)SPINLOCK_INIT;
proc->CurrentDirectory[0] = '\0';
proc->CurrentDirectoryLength = 0;
proc->CurrentDrive = KeGetSystemRootDrive();
+699 -10
View File
@@ -62,8 +62,10 @@
#include <kernel/syscall_nr.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/io_file.h>
#include <kernel/io_async.h>
#include <ntlite/ddi/io_driver.h>
#include <kernel/net_socket.h>
#include <ntlite/ddi/net_stack.h>
#include <kernel/ke_trap.h>
#include <kernel/ke_input.h>
#include <kernel/ke_display.h>
@@ -119,6 +121,45 @@ static void KiFileMappingDestroy(OBJECT* obj) {
}
}
typedef struct MODULE_HANDLE_OBJECT {
OBJECT Header;
PROCESS* Owner;
uintptr_t Base;
} MODULE_HANDLE_OBJECT;
// Releasing the last module handle releases the public load reference it owns.
static void KiModuleHandleDestroy(OBJECT* obj) {
MODULE_HANDLE_OBJECT* module = CONTAINING_RECORD(obj, MODULE_HANDLE_OBJECT, Header);
if (module->Owner && module->Base) {
(void)LdrUnloadModuleForProcess(module->Owner, module->Base);
module->Base = 0;
}
if (module->Owner) {
ObDereferenceObject(&module->Owner->Header);
module->Owner = (PROCESS*)0;
}
}
// Creates a user-visible module handle for one public load reference.
static KSTATUS KiCreateModuleHandle(PROCESS* owner, uintptr_t base, uint32_t* handleOut) {
if (!owner || !base || !handleOut) return STATUS_INVALID_PARAMETER;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)ObAllocateObject(ObjectTypeModule, sizeof(MODULE_HANDLE_OBJECT));
if (!module) return STATUS_NO_MEMORY;
module->Header.Destroy = KiModuleHandleDestroy;
module->Owner = owner;
module->Base = base;
ObReferenceObject(&owner->Header);
KSTATUS s = KeAllocateHandleWithRights(owner,
&module->Header,
HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE | HANDLE_RIGHT_DUPLICATE,
handleOut);
ObDereferenceObject(&module->Header);
return s;
}
typedef struct SURFACE_OBJECT {
OBJECT Header;
PROCESS* Owner;
@@ -565,7 +606,7 @@ static KSTATUS KiHandleToWaitEvent(PROCESS* proc, uint32_t handle, EVENT** event
event = CONTAINING_RECORD(obj, EVENT, Header);
break;
case ObjectTypeAsyncIo:
event = IoGetAsyncFileRequestWaitEvent((IO_ASYNC_FILE_REQUEST_OBJECT*)obj);
event = IoGetAsyncRequestWaitEvent((IO_ASYNC_REQUEST_OBJECT*)obj);
break;
case ObjectTypeInputQueue:
event = KeInputQueueWaitEvent(CONTAINING_RECORD(obj, INPUT_QUEUE, Header));
@@ -1921,12 +1962,12 @@ static uint64_t SyscallWriteFileAsync(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallCancelIo(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
(IO_ASYNC_FILE_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE);
IO_ASYNC_REQUEST_OBJECT* req =
(IO_ASYNC_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE);
if (!req) return STATUS_INVALID_HANDLE;
KSTATUS s = IoCancelAsyncFileRequest(req);
ObDereferenceObject(IoAsyncFileRequestObject(req));
KSTATUS s = IoCancelAsyncRequest(req);
ObDereferenceObject(IoAsyncRequestObject(req));
return s;
}
@@ -3324,6 +3365,181 @@ static uint64_t SyscallGetThreadList(uint64_t a0, uint64_t a1, uint64_t a2,
return STATUS_SUCCESS;
}
// SYS_LOAD_MODULE a0=*SYS_LOAD_MODULE_REQUEST.
static uint64_t SyscallLoadModule(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SYS_LOAD_MODULE_REQUEST req;
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
if (!KSUCCESS(s)) return s;
if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER;
if (req.Flags != SYS_LOAD_MODULE_FLAG_NONE) return STATUS_INVALID_PARAMETER;
if (!req.ModuleHandleOut) return STATUS_INVALID_PARAMETER;
s = MmProbeUserWrite(req.ModuleHandleOut, sizeof(*req.ModuleHandleOut));
if (!KSUCCESS(s)) return s;
if (req.InfoOut) {
s = MmProbeUserWrite(req.InfoOut, sizeof(*req.InfoOut));
if (!KSUCCESS(s)) return s;
}
char pathBuf[SYSCALL_STRING_MAX];
SYS_STRING_VIEW path;
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
if (!KSUCCESS(s)) return s;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
uintptr_t base = 0;
s = LdrLoadModuleForProcess(proc, pathBuf, LDR_MODULE_LOAD_PUBLIC, &base);
if (!KSUCCESS(s)) return s;
uint32_t handle = 0;
s = KiCreateModuleHandle(proc, base, &handle);
if (!KSUCCESS(s)) {
(void)LdrUnloadModuleForProcess(proc, base);
return s;
}
SYS_MODULE_INFO info;
if (req.InfoOut) {
s = LdrQueryModuleForProcess(proc, base, &info);
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
}
s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ModuleHandleOut, handle);
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
if (req.InfoOut) {
s = CopyToUser(req.InfoOut, &info, sizeof(info));
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
}
return STATUS_SUCCESS;
}
// SYS_UNLOAD_MODULE a0=moduleHandle.
static uint64_t SyscallUnloadModule(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
uint32_t handle = (uint32_t)a0;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(handle, ObjectTypeModule, HANDLE_RIGHT_TERMINATE);
if (!module) return STATUS_INVALID_HANDLE;
KeCloseHandle(proc, handle);
ObDereferenceObject(&module->Header);
return STATUS_SUCCESS;
}
// SYS_GET_MODULE_INFO a0=moduleHandle a1=*SYS_MODULE_INFO.
static uint64_t SyscallGetModuleInfo(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_MODULE_INFO));
if (!KSUCCESS(s)) return s;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeModule, HANDLE_RIGHT_QUERY);
if (!module) return STATUS_INVALID_HANDLE;
SYS_MODULE_INFO info;
s = LdrQueryModuleForProcess(module->Owner, module->Base, &info);
ObDereferenceObject(&module->Header);
if (!KSUCCESS(s)) return s;
return CopyToUser((void*)(uintptr_t)a1, &info, sizeof(info));
}
// SYS_GET_MODULE_EXPORT a0=*SYS_GET_MODULE_EXPORT_REQUEST.
static uint64_t SyscallGetModuleExport(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SYS_GET_MODULE_EXPORT_REQUEST req;
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
if (!KSUCCESS(s)) return s;
if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER;
if (!req.AddressOut) return STATUS_INVALID_PARAMETER;
s = MmProbeUserWrite(req.AddressOut, sizeof(*req.AddressOut));
if (!KSUCCESS(s)) return s;
char nameBuf[SYSCALL_STRING_MAX];
SYS_STRING_VIEW name;
s = KiImportUserTextView(req.Name, nameBuf, sizeof(nameBuf), &name, true);
if (!KSUCCESS(s)) return s;
if (name.Length == 0) return STATUS_INVALID_PARAMETER;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(req.ModuleHandle, ObjectTypeModule, HANDLE_RIGHT_QUERY);
if (!module) return STATUS_INVALID_HANDLE;
void* address = LdrGetModuleExportForProcess(module->Owner, module->Base, nameBuf);
ObDereferenceObject(&module->Header);
if (!address) return STATUS_NOT_FOUND;
uintptr_t userAddress = (uintptr_t)address;
return CopyToUser(req.AddressOut, &userAddress, sizeof(userAddress));
}
// SYS_GET_MODULE_LIST a0=*SYS_MODULE_INFO[] a1=maxCount a2=*countOut.
static uint64_t SyscallGetModuleList(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
size_t maxCount = (size_t)a1;
if (maxCount && !a0) return STATUS_INVALID_PARAMETER;
if (a2) {
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a2, sizeof(size_t));
if (!KSUCCESS(s)) return s;
}
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
size_t total = 0;
KSTATUS s = LdrCopyProcessModuleInfo(proc, NULL, 0, &total);
if (s != STATUS_BUFFER_TOO_SMALL && !KSUCCESS(s)) return s;
size_t toCopy = total < maxCount ? total : maxCount;
if (toCopy) {
size_t bytes = 0;
if (!KiMulSize(toCopy, sizeof(SYS_MODULE_INFO), &bytes)) return STATUS_INVALID_PARAMETER;
SYS_MODULE_INFO* tmp = (SYS_MODULE_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('m', 'o', 'd', 'l'));
if (!tmp) return STATUS_NO_MEMORY;
size_t copiedTotal = 0;
s = LdrCopyProcessModuleInfo(proc, tmp, toCopy, &copiedTotal);
if (s == STATUS_BUFFER_TOO_SMALL)
s = STATUS_SUCCESS;
if (KSUCCESS(s))
s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(SYS_MODULE_INFO));
MmFreePool(tmp);
if (!KSUCCESS(s)) return s;
}
if (a2) return KiCopySizeToUser(a2, total);
return STATUS_SUCCESS;
}
// SYS_RENAME_FILE (66) a0=*oldPath a1=*newPath.
static uint64_t SyscallRenameFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
@@ -3459,6 +3675,39 @@ typedef struct SYS_SOCKET_OPTION_REQUEST_K {
size_t* ActualSizeOut;
} SYS_SOCKET_OPTION_REQUEST_K;
typedef struct SYS_ASYNC_SOCKET_REQUEST_K {
uint32_t Size;
uint32_t SocketHandle;
uint32_t EventHandle;
uint32_t Operation;
uint32_t Flags;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* Buffer;
size_t Length;
const SYS_SOCKET_ADDRESS_K* Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
SYS_IO_STATUS_BLOCK_K* Iosb;
SYS_USER_APC_ROUTINE_K ApcRoutine;
void* ApcContext;
uint32_t* RequestHandleOut;
} SYS_ASYNC_SOCKET_REQUEST_K;
typedef struct SYS_MEMORY_STATUS_K {
uint64_t FreePhysicalBytes;
uint64_t KernelPoolBytes;
@@ -3557,7 +3806,7 @@ static uint64_t SyscallAccept(uint64_t a0, uint64_t a1, uint64_t a2,
SOCKET_OBJECT* accepted = NULL;
SYS_SOCKET_ADDRESS_K remote;
KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL);
KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&listener->Header);
if (!KSUCCESS(s)) return s;
@@ -3599,7 +3848,7 @@ static uint64_t SyscallConnect(uint64_t a0, uint64_t a1, uint64_t a2,
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
if (!socket) return STATUS_NOT_FOUND;
s = NetSocketConnect(socket, &address, (size_t)a2);
s = NetSocketConnect(socket, &address, (size_t)a2, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
return s;
}
@@ -3661,7 +3910,7 @@ static uint64_t SyscallRecv(uint64_t a0, uint64_t a1, uint64_t a2,
if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
size_t received = 0;
s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received);
s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
if (KSUCCESS(s) && received)
@@ -3772,7 +4021,7 @@ static uint64_t SyscallRecvFrom(uint64_t a0, uint64_t a1, uint64_t a2,
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
size_t received = 0;
s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received);
s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
if (KSUCCESS(s) && received)
@@ -3958,6 +4207,417 @@ static uint64_t SyscallPoll(uint64_t a0, uint64_t a1, uint64_t a2,
return s;
}
// Releases resources prepared for an async socket request before queue ownership starts.
static void SyscallAsyncSocketReleaseSubmitResources(NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!submit)
return;
if (submit->KernelBuffer) {
MmFreePool(submit->KernelBuffer);
submit->KernelBuffer = (void*)0;
}
if (submit->OptionValue) {
MmFreePool(submit->OptionValue);
submit->OptionValue = (void*)0;
}
if (submit->LockedBuffer) {
MmUnlockUserBuffer(submit->LockedBuffer);
submit->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedIosb) {
MmUnlockUserBuffer(submit->LockedIosb);
submit->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedAddressOut) {
MmUnlockUserBuffer(submit->LockedAddressOut);
submit->LockedAddressOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedAddressSize) {
MmUnlockUserBuffer(submit->LockedAddressSize);
submit->LockedAddressSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedOptionValue) {
MmUnlockUserBuffer(submit->LockedOptionValue);
submit->LockedOptionValue = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedOptionActualSize) {
MmUnlockUserBuffer(submit->LockedOptionActualSize);
submit->LockedOptionActualSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedHandleOut) {
MmUnlockUserBuffer(submit->LockedHandleOut);
submit->LockedHandleOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedPollReturnedEvents) {
MmUnlockUserBuffer(submit->LockedPollReturnedEvents);
submit->LockedPollReturnedEvents = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Locks an optional async socket output pointer for worker-thread completion.
static KSTATUS SyscallAsyncSocketLockOptional(PROCESS* proc,
void* ptr,
size_t size,
MM_LOCKED_USER_BUFFER** out) {
if (out)
*out = (MM_LOCKED_USER_BUFFER*)0;
if (!ptr || size == 0)
return STATUS_SUCCESS;
return MmLockUserBuffer(proc, ptr, size, 1, out);
}
// Copies an input address into a kernel-owned async socket request.
static KSTATUS SyscallAsyncSocketCopyInputAddress(const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!req || !submit || !req->Address)
return STATUS_INVALID_PARAMETER;
if (req->AddressSize < sizeof(SYS_SOCKET_ADDRESS_K))
return STATUS_BUFFER_TOO_SMALL;
KSTATUS status = CopyFromUser(&submit->Address, req->Address, sizeof(submit->Address));
if (!KSUCCESS(status))
return status;
submit->AddressSize = req->AddressSize;
return STATUS_SUCCESS;
}
// Validates and locks optional remote-address outputs for async accept/recvfrom.
static KSTATUS SyscallAsyncSocketPrepareAddressOutput(PROCESS* proc,
const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!req || !submit)
return STATUS_INVALID_PARAMETER;
if (!req->AddressOut)
return STATUS_SUCCESS;
if (!req->AddressSizeInOut)
return STATUS_INVALID_PARAMETER;
size_t capacity = 0;
KSTATUS status = CopyFromUser(&capacity, req->AddressSizeInOut, sizeof(capacity));
if (!KSUCCESS(status))
return status;
if (capacity < sizeof(SYS_SOCKET_ADDRESS_K)) {
size_t required = sizeof(SYS_SOCKET_ADDRESS_K);
(void)CopyToUser(req->AddressSizeInOut, &required, sizeof(required));
return STATUS_BUFFER_TOO_SMALL;
}
status = MmLockUserBuffer(proc, req->AddressOut, sizeof(SYS_SOCKET_ADDRESS_K), 1,
&submit->LockedAddressOut);
if (!KSUCCESS(status))
return status;
status = MmLockUserBuffer(proc, req->AddressSizeInOut, sizeof(*req->AddressSizeInOut), 1,
&submit->LockedAddressSize);
if (!KSUCCESS(status))
return status;
submit->AddressOut = req->AddressOut;
submit->AddressSizeInOut = req->AddressSizeInOut;
return STATUS_SUCCESS;
}
// Prepares kernel-owned buffers and locked outputs for one async socket operation.
static KSTATUS SyscallAsyncSocketPrepareOperation(PROCESS* proc,
const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!proc || !req || !submit)
return STATUS_INVALID_PARAMETER;
submit->Operation = req->Operation;
submit->SocketFlags = req->SocketFlags;
submit->Backlog = req->Backlog;
submit->ShutdownHow = req->ShutdownHow;
submit->Option = req->Option;
submit->PollEvents = req->PollEvents;
submit->Length = req->Length;
switch (req->Operation) {
case SYS_ASYNC_SOCKET_OP_BIND:
case SYS_ASYNC_SOCKET_OP_CONNECT:
return SyscallAsyncSocketCopyInputAddress(req, submit);
case SYS_ASYNC_SOCKET_OP_LISTEN:
case SYS_ASYNC_SOCKET_OP_SHUTDOWN:
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_ACCEPT: {
if (!req->HandleOut)
return STATUS_INVALID_PARAMETER;
uint32_t zero = 0;
KSTATUS status = MmLockUserBuffer(proc, req->HandleOut, sizeof(*req->HandleOut), 1,
&submit->LockedHandleOut);
if (!KSUCCESS(status))
return status;
*req->HandleOut = zero;
submit->HandleOut = req->HandleOut;
return SyscallAsyncSocketPrepareAddressOutput(proc, req, submit);
}
case SYS_ASYNC_SOCKET_OP_SEND:
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
if (req->Length) {
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'n'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length);
}
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_RECV:
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
if (req->Length) {
KSTATUS status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer);
if (!KSUCCESS(status))
return status;
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'n'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
}
submit->UserBuffer = req->Buffer;
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_SEND_TO: {
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
KSTATUS status = SyscallAsyncSocketCopyInputAddress(req, submit);
if (!KSUCCESS(status))
return status;
if (req->Length) {
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'd'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length);
}
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_RECV_FROM: {
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
KSTATUS status = SyscallAsyncSocketPrepareAddressOutput(proc, req, submit);
if (!KSUCCESS(status))
return status;
if (req->Length) {
status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer);
if (!KSUCCESS(status))
return status;
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'd'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
}
submit->UserBuffer = req->Buffer;
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_SET_OPT:
if (!req->OptionValue)
return STATUS_INVALID_PARAMETER;
if (req->OptionValueSize > sizeof(uint64_t))
return STATUS_BUFFER_TOO_SMALL;
submit->OptionValueSize = req->OptionValueSize;
if (req->OptionValueSize) {
submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 's'));
if (!submit->OptionValue)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->OptionValue, req->OptionValue, req->OptionValueSize);
}
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_GET_OPT: {
if (!req->OptionValue)
return STATUS_INVALID_PARAMETER;
if (req->OptionValueSize > sizeof(uint64_t))
return STATUS_BUFFER_TOO_SMALL;
submit->OptionValueSize = req->OptionValueSize;
submit->UserOptionValue = req->OptionValue;
KSTATUS status = MmLockUserBuffer(proc, req->OptionValue, req->OptionValueSize, 1,
&submit->LockedOptionValue);
if (!KSUCCESS(status))
return status;
if (req->OptionActualSizeOut) {
status = MmLockUserBuffer(proc, req->OptionActualSizeOut, sizeof(*req->OptionActualSizeOut), 1,
&submit->LockedOptionActualSize);
if (!KSUCCESS(status))
return status;
submit->OptionActualSizeOut = req->OptionActualSizeOut;
}
if (req->OptionValueSize) {
submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 'g'));
if (!submit->OptionValue)
return STATUS_NO_MEMORY;
}
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_POLL: {
if (req->PollReturnedEventsOut) {
KSTATUS status = MmLockUserBuffer(proc, req->PollReturnedEventsOut,
sizeof(*req->PollReturnedEventsOut), 1,
&submit->LockedPollReturnedEvents);
if (!KSUCCESS(status))
return status;
*req->PollReturnedEventsOut = 0;
submit->PollReturnedEventsOut = req->PollReturnedEventsOut;
}
return STATUS_SUCCESS;
}
default:
return STATUS_INVALID_PARAMETER;
}
}
// SYS_SOCKET_IO_ASYNC a0=*SYS_ASYNC_SOCKET_REQUEST.
static uint64_t SyscallSocketIoAsync(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0)
return STATUS_INVALID_PARAMETER;
SYS_ASYNC_SOCKET_REQUEST_K req;
KSTATUS status = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req));
if (!KSUCCESS(status))
return status;
if (req.Size != sizeof(req) || req.Flags != SYS_ASYNC_SOCKET_FLAG_NONE || !req.Iosb)
return STATUS_INVALID_PARAMETER;
if (req.RequestHandleOut) {
status = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut));
if (!KSUCCESS(status))
return status;
}
PROCESS* proc = KeGetCurrentProcess();
if (!proc)
return STATUS_FAILURE;
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.SocketHandle,
ObjectTypeSocket,
HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
if (!socket)
return STATUS_NOT_FOUND;
EVENT* event = (EVENT*)0;
if (req.EventHandle) {
event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL);
if (!event) {
ObDereferenceObject(&socket->Header);
return STATUS_NOT_FOUND;
}
}
KAPC* completionApc = (KAPC*)0;
SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0;
if (req.ApcRoutine) {
THREAD* thread = KeGetCurrentThread();
if (!thread || !thread->UserApcDispatcher) {
if (event) ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return STATUS_INVALID_PARAMETER;
}
apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePoolTag(sizeof(SYS_USER_ASYNC_APC_CONTEXT), MM_POOL_TAG('u', 'a', 'p', 'c'));
if (!apcCtx) {
if (event) ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return STATUS_NO_MEMORY;
}
memset(apcCtx, 0, sizeof(*apcCtx));
ObReferenceObject(&thread->Header);
apcCtx->TargetThread = thread;
apcCtx->UserRoutine = req.ApcRoutine;
apcCtx->UserContext = req.ApcContext;
KeInitializeApc(&apcCtx->KernelApc,
thread,
SyscallUserAsyncKernelRoutine,
SyscallUserAsyncNormalRoutine,
apcCtx);
KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown);
completionApc = &apcCtx->KernelApc;
}
NET_ASYNC_SOCKET_SUBMIT submit;
memset(&submit, 0, sizeof(submit));
submit.OwnerProcess = proc;
submit.Socket = socket;
submit.CompletionEvent = event;
submit.CompletionApc = completionApc;
submit.Iosb = (IO_STATUS_BLOCK*)req.Iosb;
status = MmLockUserBuffer(proc, req.Iosb, sizeof(*req.Iosb), 1, &submit.LockedIosb);
if (!KSUCCESS(status))
goto fail;
status = SyscallAsyncSocketPrepareOperation(proc, &req, &submit);
if (!KSUCCESS(status))
goto fail;
NET_ASYNC_SOCKET_REQUEST_OBJECT* asyncReq = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
status = NetSocketSubmitAsyncKernel(&submit, &asyncReq);
if (status == STATUS_PENDING) {
memset(&submit, 0, sizeof(submit));
}
if (!KSUCCESS(status) && status != STATUS_PENDING)
goto fail;
if (req.RequestHandleOut && asyncReq) {
uint32_t requestHandle = 0;
KSTATUS hs = KeAllocateHandleWithRights(proc,
NetAsyncSocketRequestObject(asyncReq),
HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE,
&requestHandle);
if (!KSUCCESS(hs)) {
(void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq);
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
status = hs;
goto fail_after_submit;
}
KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle));
if (!KSUCCESS(cs)) {
(void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq);
KeCloseHandle(proc, requestHandle);
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
status = cs;
goto fail_after_submit;
}
} else if (req.RequestHandleOut) {
uint32_t zeroHandle = 0;
KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle));
if (!KSUCCESS(cs)) {
status = cs;
goto fail_after_submit;
}
}
if (asyncReq)
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
if (event)
ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return status;
fail:
SyscallAsyncSocketReleaseSubmitResources(&submit);
if (apcCtx)
SyscallUserAsyncApcContextFree(apcCtx);
fail_after_submit:
if (event)
ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return status;
}
// SYS_GET_MEMORY_STATUS a0=*SYS_MEMORY_STATUS.
static uint64_t SyscallGetMemoryStatus(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
@@ -4332,6 +4992,29 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um));
}
case SYS_KERNEL_STATS_NET_IRP: {
NET_IRP_STATS km;
SYS_NET_IRP_STATS um;
NetQueryIrpStats(&km);
memset(&um, 0, sizeof(um));
um.Size = sizeof(um);
um.Version = SYS_STATS_VERSION;
um.Submitted = km.Submitted;
um.Completed = km.Completed;
um.CompletedSynchronously = km.CompletedSynchronously;
um.CompletedAsynchronously = km.CompletedAsynchronously;
um.Pending = km.Pending;
um.CancelRequested = km.CancelRequested;
um.Cancelled = km.Cancelled;
um.WouldBlock = km.WouldBlock;
um.Timeout = km.Timeout;
um.Failed = km.Failed;
for (uint32_t i = 0; i < SYS_NET_IRP_OPERATION_COUNT && i < NetSocketIrpOperationCount; ++i)
um.ByOperation[i] = km.ByOperation[i];
return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um));
}
case SYS_KERNEL_STATS_CPU: {
struct {
SYS_CPU_STATS Header;
@@ -4381,7 +5064,7 @@ typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
// Process, threads, waits, and APC.
// Process, threads, modules, waits, and APC.
[SYS_EXIT_PROCESS] = SyscallExitProcess,
[SYS_EXIT_THREAD] = SyscallExitThread,
[SYS_CREATE_PROCESS] = SyscallCreateProcess,
@@ -4392,6 +5075,11 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
[SYS_GET_PROCESS_PID] = SyscallGetProcessPid,
[SYS_CREATE_THREAD] = SyscallCreateThread,
[SYS_GET_THREAD_LIST] = SyscallGetThreadList,
[SYS_LOAD_MODULE] = SyscallLoadModule,
[SYS_UNLOAD_MODULE] = SyscallUnloadModule,
[SYS_GET_MODULE_INFO] = SyscallGetModuleInfo,
[SYS_GET_MODULE_EXPORT] = SyscallGetModuleExport,
[SYS_GET_MODULE_LIST] = SyscallGetModuleList,
[SYS_GET_CURRENT_TID] = SyscallGetCurrentTid,
[SYS_GET_CURRENT_PID] = SyscallGetCurrentPid,
[SYS_SET_THREAD_PRIORITY] = SyscallSetThreadPriority,
@@ -4498,6 +5186,7 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
// Native sockets and polling.
[SYS_CREATE_SOCKET] = SyscallSocket,
[SYS_SOCKET_IO_ASYNC] = SyscallSocketIoAsync,
[SYS_BIND_SOCKET] = SyscallBind,
[SYS_LISTEN_SOCKET] = SyscallListen,
[SYS_ACCEPT_SOCKET] = SyscallAccept,
+185 -19
View File
@@ -10,6 +10,7 @@
#include <kernel/mm_virt.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_system_root.h>
#include <ntlite/syscall.h>
#ifndef LDR_DEBUG_VERBOSE
#define LDR_DEBUG_VERBOSE 0
@@ -326,6 +327,24 @@ void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const cha
return KiGetExportAtBase(base, functionName);
}
// Looks up an export in a specific process module mapping.
void* LdrGetModuleExportForProcess(PROCESS* process, uintptr_t imageBase, const char* functionName) {
if (!process || !imageBase || !functionName) return (void*)0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
int found = 0;
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (process->Modules[i].Base == imageBase) {
found = 1;
break;
}
}
KeReleaseSpinlock(&process->ModuleLock, flags);
if (!found) return (void*)0;
return KiGetExportAtBase(imageBase, functionName);
}
// Implements the ldr Get Image Size operation.
size_t LdrGetImageSize(uintptr_t base) {
if (!base) return 0;
@@ -559,7 +578,7 @@ static KSTATUS LdrResolveImportsCommon(
if (!modBase) {
KSTATUS s =
LdrLoadModuleForProcess(process, dllName, &modBase);
LdrLoadModuleForProcess(process, dllName, LDR_MODULE_LOAD_IMPORT, &modBase);
if (!KSUCCESS(s)) {
DbgPrint(
@@ -674,38 +693,101 @@ KSTATUS LdrLoadImageFileForProcess(PROCESS* process, FILE_OBJECT* file, uintptr_
return STATUS_SUCCESS;
}
// Implements the ldr Register Process Module operation.
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) {
if (!process || !name || !base) return STATUS_INVALID_PARAMETER;
// Converts a process module entry into the public module ABI.
static void KiFillProcessModuleInfo(const PROCESS_MODULE* mod, SYS_MODULE_INFO* info) {
memset(info, 0, sizeof(*info));
info->Size = sizeof(*info);
info->Version = SYS_MODULE_INFO_VERSION;
info->Flags = mod->Flags;
if (mod->PublicRefCount)
info->Flags |= SYS_MODULE_FLAG_PUBLIC_REF;
if (mod->ImportRefCount)
info->Flags |= SYS_MODULE_FLAG_IMPORT_PINNED;
if (mod->PublicRefCount && mod->ImportRefCount == 0)
info->Flags |= SYS_MODULE_FLAG_UNLOADABLE;
info->Base = mod->Base;
info->ImageSize = mod->Size;
info->EntryPoint = mod->EntryPoint;
info->PublicRefCount = mod->PublicRefCount;
info->ImportRefCount = mod->ImportRefCount;
info->TotalRefCount = mod->PublicRefCount + mod->ImportRefCount;
strncpy(info->Name, mod->Name, sizeof(info->Name) - 1);
info->Name[sizeof(info->Name) - 1] = '\0';
}
// Adds one reference of the requested class to an existing process module.
static void KiReferenceProcessModule(PROCESS_MODULE* mod, uint32_t loadFlags) {
if (loadFlags & LDR_MODULE_LOAD_PUBLIC)
mod->PublicRefCount++;
if (loadFlags & LDR_MODULE_LOAD_IMPORT)
mod->ImportRefCount++;
}
// Finds a process module by canonical name while the caller owns ModuleLock.
static PROCESS_MODULE* KiFindProcessModuleByNameLocked(PROCESS* process, const char* name) {
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (_stricmp(process->Modules[i].Name, name) == 0) {
return STATUS_SUCCESS;
}
if (_stricmp(process->Modules[i].Name, name) == 0)
return &process->Modules[i];
}
return (PROCESS_MODULE*)0;
}
// Finds a process module by image base while the caller owns ModuleLock.
static PROCESS_MODULE* KiFindProcessModuleByBaseLocked(PROCESS* process, uintptr_t base) {
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (process->Modules[i].Base == base)
return &process->Modules[i];
}
return (PROCESS_MODULE*)0;
}
// Registers a mapped process module or records a reference to an existing one.
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base, uint32_t loadFlags, uintptr_t* registeredBaseOut) {
if (!process || !name || !base || !loadFlags) return STATUS_INVALID_PARAMETER;
if (registeredBaseOut) *registeredBaseOut = 0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, name);
if (existing) {
KiReferenceProcessModule(existing, loadFlags);
if (registeredBaseOut) *registeredBaseOut = existing->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_ALREADY_EXISTS;
}
if (process->ModuleCount >= PROCESS_MODULE_MAX) return STATUS_NO_MEMORY;
if (process->ModuleCount >= PROCESS_MODULE_MAX) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NO_MEMORY;
}
PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++];
strncpy(mod->Name, name, 31);
mod->Name[31] = '\0';
memset(mod, 0, sizeof(*mod));
strncpy(mod->Name, name, sizeof(mod->Name) - 1);
mod->Name[sizeof(mod->Name) - 1] = '\0';
mod->Base = base;
mod->Size = LdrGetMappedImageSize(process, base);
mod->EntryPoint = LdrGetEntryPoint(base);
KiReferenceProcessModule(mod, loadFlags);
if (registeredBaseOut) *registeredBaseOut = mod->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
// Implements the ldr Load Module For Process operation.
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) {
if (!process || !filename) return STATUS_INVALID_PARAMETER;
// Loads a DLL-like user module into a process and records the requested reference.
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uint32_t loadFlags, uintptr_t* baseOut) {
if (!process || !filename || !loadFlags) return STATUS_INVALID_PARAMETER;
filename = KiCanonicalModuleName(filename);
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (_stricmp(process->Modules[i].Name, filename) == 0) {
if (baseOut) *baseOut = process->Modules[i].Base;
return STATUS_SUCCESS;
}
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, filename);
if (existing) {
KiReferenceProcessModule(existing, loadFlags);
if (baseOut) *baseOut = existing->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
KeReleaseSpinlock(&process->ModuleLock, flags);
FILE_OBJECT* f;
KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
@@ -750,7 +832,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return s;
}
s = LdrRegisterProcessModule(process, filename, imageBase);
uintptr_t registeredBase = 0;
s = LdrRegisterProcessModule(process, filename, imageBase, loadFlags, &registeredBase);
if (s == STATUS_ALREADY_EXISTS) {
KiFreeLoadedImageForProcess(process, imageBase, true);
imageBase = registeredBase;
s = STATUS_SUCCESS;
}
if (!KSUCCESS(s)) {
KiFreeLoadedImageForProcess(process, imageBase, true);
return s;
@@ -762,6 +850,84 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return STATUS_SUCCESS;
}
// Releases one public module load reference and unmaps the image when no references remain.
KSTATUS LdrUnloadModuleForProcess(PROCESS* process, uintptr_t imageBase) {
if (!process || !imageBase) return STATUS_INVALID_PARAMETER;
char name[PROCESS_MODULE_NAME_MAX];
uintptr_t base = 0;
int shouldUnmap = 0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
if (!mod) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NOT_FOUND;
}
if (mod->PublicRefCount == 0) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_INVALID_PARAMETER;
}
mod->PublicRefCount--;
if (mod->PublicRefCount == 0 && mod->ImportRefCount == 0) {
base = mod->Base;
strncpy(name, mod->Name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
uint32_t index = (uint32_t)(mod - process->Modules);
if (index + 1 < process->ModuleCount) {
memmove(&process->Modules[index],
&process->Modules[index + 1],
(process->ModuleCount - index - 1) * sizeof(process->Modules[0]));
}
process->ModuleCount--;
shouldUnmap = 1;
}
KeReleaseSpinlock(&process->ModuleLock, flags);
if (shouldUnmap) {
LdrDbg("LDR: unload module pid=%u name=%s base=%016llx\n",
process->ProcessId, name, (unsigned long long)base);
KiFreeLoadedImageForProcess(process, base, true);
}
return STATUS_SUCCESS;
}
// Queries one loaded process module by image base.
KSTATUS LdrQueryModuleForProcess(PROCESS* process, uintptr_t imageBase, SYS_MODULE_INFO* infoOut) {
if (!process || !imageBase || !infoOut) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
if (!mod) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NOT_FOUND;
}
KiFillProcessModuleInfo(mod, infoOut);
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
// Copies a bounded snapshot of the process module table.
KSTATUS LdrCopyProcessModuleInfo(PROCESS* process, SYS_MODULE_INFO* infoOut, size_t maxCount, size_t* countOut) {
if (!process || !countOut) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
size_t count = process->ModuleCount;
*countOut = count;
size_t toCopy = count;
if (toCopy > maxCount) toCopy = maxCount;
if (toCopy && !infoOut) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_INVALID_PARAMETER;
}
for (size_t i = 0; i < toCopy; i++)
KiFillProcessModuleInfo(&process->Modules[i], &infoOut[i]);
KeReleaseSpinlock(&process->ModuleLock, flags);
return (maxCount < count) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
// Loads a boot/kernel PE image from a raw memory buffer into kernel space.
static KSTATUS KiLdrLoadKernelImageFromBuffer(const void* buffer, size_t size, uintptr_t* baseOut) {
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
+761 -36
View File
@@ -2,25 +2,591 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <kernel/net_socket.h>
#include <ntlite/ddi/net_stack.h>
#include <kernel/ke_event.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_process.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_thread.h>
#include <kernel/mm_virt.h>
#include <kernel/ob_object.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/net_stack.h>
#include <ntlite/syscall.h>
#define NET_SOCKET_IO_MAX_BYTES (1024u * 1024u)
#define NET_SOCKET_UDP_MAX_BYTES 65507u
#define NET_SOCKET_RAW_MAX_BYTES 65535u
// Implements the net Socket Destroy operation.
static SPINLOCK gNetIrpStatsLock = SPINLOCK_INIT;
static NET_IRP_STATS gNetIrpStats;
// Records that a socket operation entered the IRP boundary.
static void NetIrpStatsSubmit(uint32_t operation) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Submitted++;
if (operation < NetSocketIrpOperationCount)
gNetIrpStats.ByOperation[operation]++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records that a submitted socket IRP returned pending to the socket facade.
static void NetIrpStatsPending(void) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Pending++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records the final status of a socket IRP after synchronous wait completion.
static void NetIrpStatsComplete(KSTATUS status, bool asynchronous) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Completed++;
if (asynchronous)
gNetIrpStats.CompletedAsynchronously++;
else
gNetIrpStats.CompletedSynchronously++;
if (status == STATUS_CANCELLED)
gNetIrpStats.Cancelled++;
else if (status == STATUS_WOULD_BLOCK)
gNetIrpStats.WouldBlock++;
else if (status == STATUS_TIMEOUT)
gNetIrpStats.Timeout++;
else if (KFAILED(status))
gNetIrpStats.Failed++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Returns a stable snapshot of socket IRP diagnostics.
__declspec(dllexport) void NetQueryIrpStats(NET_IRP_STATS* out) {
if (!out)
return;
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
*out = gNetIrpStats;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records that a public async socket request requested cancellation.
static void NetIrpStatsCancelRequested(void) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.CancelRequested++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
struct NET_ASYNC_SOCKET_REQUEST_OBJECT {
IO_ASYNC_REQUEST_OBJECT Async;
PROCESS* OwnerProcess;
SOCKET_OBJECT* Socket;
uint32_t Operation;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* KernelBuffer;
void* UserBuffer;
size_t Length;
SYS_SOCKET_ADDRESS_K Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
void* UserOptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
MM_LOCKED_USER_BUFFER* LockedAddressOut;
MM_LOCKED_USER_BUFFER* LockedAddressSize;
MM_LOCKED_USER_BUFFER* LockedOptionValue;
MM_LOCKED_USER_BUFFER* LockedOptionActualSize;
MM_LOCKED_USER_BUFFER* LockedHandleOut;
MM_LOCKED_USER_BUFFER* LockedPollReturnedEvents;
};
static SPINLOCK gAsyncSocketInitLock = SPINLOCK_INIT;
static int gAsyncSocketInitState;
static SPINLOCK gAsyncSocketQueueLock = SPINLOCK_INIT;
static LIST_ENTRY gAsyncSocketQueue;
static SEMAPHORE* gAsyncSocketSemaphore;
// Releases operation-specific user locks that the common async object does not own.
static void NetAsyncSocketReleaseExtraLocks(NET_ASYNC_SOCKET_REQUEST_OBJECT* req) {
if (!req)
return;
if (req->LockedAddressOut) {
MmUnlockUserBuffer(req->LockedAddressOut);
req->LockedAddressOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedAddressSize) {
MmUnlockUserBuffer(req->LockedAddressSize);
req->LockedAddressSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedOptionValue) {
MmUnlockUserBuffer(req->LockedOptionValue);
req->LockedOptionValue = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedOptionActualSize) {
MmUnlockUserBuffer(req->LockedOptionActualSize);
req->LockedOptionActualSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedHandleOut) {
MmUnlockUserBuffer(req->LockedHandleOut);
req->LockedHandleOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedPollReturnedEvents) {
MmUnlockUserBuffer(req->LockedPollReturnedEvents);
req->LockedPollReturnedEvents = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Releases heap storage no longer needed after a socket operation has finished.
static void NetAsyncSocketReleaseKernelStorage(NET_ASYNC_SOCKET_REQUEST_OBJECT* req) {
if (!req)
return;
if (req->KernelBuffer) {
MmFreePool(req->KernelBuffer);
req->KernelBuffer = (void*)0;
}
if (req->OptionValue) {
MmFreePool(req->OptionValue);
req->OptionValue = (void*)0;
}
}
// Releases socket-specific references owned by an async socket request.
static void NetAsyncSocketRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
NET_ASYNC_SOCKET_REQUEST_OBJECT* req =
(NET_ASYNC_SOCKET_REQUEST_OBJECT*)async;
NetAsyncSocketReleaseExtraLocks(req);
NetAsyncSocketReleaseKernelStorage(req);
if (req->Socket) {
ObDereferenceObject(&req->Socket->Header);
req->Socket = (SOCKET_OBJECT*)0;
}
if (req->OwnerProcess) {
ObDereferenceObject(&req->OwnerProcess->Header);
req->OwnerProcess = (PROCESS*)0;
}
}
// Cancels an async socket request from the shared async I/O handle.
static KSTATUS NetCancelAsyncSocketRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
if (!async)
return STATUS_INVALID_PARAMETER;
NetIrpStatsCancelRequested();
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
if (state == IoAsyncRequestQueued) {
ListRemove(&async->Link);
IoAsyncRequestRequestCancel(async);
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
ObDereferenceObject(&async->Header);
return STATUS_SUCCESS;
}
if (state == IoAsyncRequestRunning) {
IoAsyncRequestRequestCancel(async);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return STATUS_OBJECT_DEAD;
}
// Removes one queued async socket request for the worker.
static NET_ASYNC_SOCKET_REQUEST_OBJECT* NetAsyncSocketDequeue(void) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
LIST_ENTRY* node = ListPopHead(&gAsyncSocketQueue);
if (!node) {
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
}
NET_ASYNC_SOCKET_REQUEST_OBJECT* req =
(NET_ASYNC_SOCKET_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
if (IoAsyncRequestIsCancelRequested(&req->Async))
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
else
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return req;
}
// Publishes a remote address output for accept and recvfrom.
static size_t NetAsyncSocketPublishAddress(NET_ASYNC_SOCKET_REQUEST_OBJECT* req,
const SYS_SOCKET_ADDRESS_K* address) {
if (!req || !req->AddressOut || !address)
return 0;
memcpy(req->AddressOut, address, sizeof(*address));
if (req->AddressSizeInOut)
*req->AddressSizeInOut = sizeof(*address);
return sizeof(*address);
}
// Executes one queued async socket operation on the worker thread.
static void NetAsyncSocketExecute(NET_ASYNC_SOCKET_REQUEST_OBJECT* req,
KSTATUS* statusOut,
size_t* informationOut) {
KSTATUS status = STATUS_INVALID_PARAMETER;
size_t information = 0;
if (!req || !statusOut || !informationOut)
return;
switch (req->Operation) {
case SYS_ASYNC_SOCKET_OP_BIND:
status = NetSocketBind(req->Socket, &req->Address, req->AddressSize);
break;
case SYS_ASYNC_SOCKET_OP_LISTEN:
status = NetSocketListen(req->Socket, req->Backlog);
break;
case SYS_ASYNC_SOCKET_OP_ACCEPT: {
SOCKET_OBJECT* accepted = (SOCKET_OBJECT*)0;
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
status = NetSocketAccept(req->Socket, &accepted, req->AddressOut ? &remote : (SYS_SOCKET_ADDRESS_K*)0, &req->Async);
if (KSUCCESS(status)) {
uint32_t handle = 0;
status = KeAllocateHandleWithRights(req->OwnerProcess,
&accepted->Header,
HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY,
&handle);
ObDereferenceObject(&accepted->Header);
if (KSUCCESS(status)) {
if (req->HandleOut)
*req->HandleOut = handle;
information = handle;
(void)NetAsyncSocketPublishAddress(req, &remote);
}
}
break;
}
case SYS_ASYNC_SOCKET_OP_CONNECT:
status = NetSocketConnect(req->Socket, &req->Address, req->AddressSize, &req->Async);
break;
case SYS_ASYNC_SOCKET_OP_SEND: {
size_t sent = 0;
status = NetSocketSend(req->Socket, req->KernelBuffer, req->Length, req->SocketFlags, &sent);
information = sent;
break;
}
case SYS_ASYNC_SOCKET_OP_RECV: {
size_t received = 0;
status = NetSocketReceive(req->Socket, req->KernelBuffer, req->Length, req->SocketFlags, &received, &req->Async);
if (KSUCCESS(status) && received)
memcpy(req->UserBuffer, req->KernelBuffer, received);
information = received;
break;
}
case SYS_ASYNC_SOCKET_OP_SEND_TO: {
size_t sent = 0;
status = NetSocketSendTo(req->Socket, req->KernelBuffer, req->Length,
req->SocketFlags, &req->Address, req->AddressSize, &sent);
information = sent;
break;
}
case SYS_ASYNC_SOCKET_OP_RECV_FROM: {
size_t received = 0;
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
status = NetSocketReceiveFrom(req->Socket, req->KernelBuffer, req->Length,
req->SocketFlags,
req->AddressOut ? &remote : (SYS_SOCKET_ADDRESS_K*)0,
&received,
&req->Async);
if (KSUCCESS(status) && received)
memcpy(req->UserBuffer, req->KernelBuffer, received);
if (KSUCCESS(status))
(void)NetAsyncSocketPublishAddress(req, &remote);
information = received;
break;
}
case SYS_ASYNC_SOCKET_OP_SHUTDOWN:
status = NetSocketShutdown(req->Socket, req->ShutdownHow);
break;
case SYS_ASYNC_SOCKET_OP_SET_OPT:
status = NetSocketSetOption(req->Socket, req->Option, req->OptionValue, req->OptionValueSize);
break;
case SYS_ASYNC_SOCKET_OP_GET_OPT: {
size_t actual = 0;
status = NetSocketGetOption(req->Socket, req->Option, req->OptionValue, req->OptionValueSize, &actual);
if (KSUCCESS(status) && actual && req->UserOptionValue)
memcpy(req->UserOptionValue, req->OptionValue, actual);
if ((KSUCCESS(status) || status == STATUS_BUFFER_TOO_SMALL) && req->OptionActualSizeOut)
*req->OptionActualSizeOut = actual;
information = actual;
break;
}
case SYS_ASYNC_SOCKET_OP_POLL: {
uint32_t returnedEvents = 0;
status = NetSocketPoll(req->Socket, req->PollEvents, &returnedEvents);
if (req->PollReturnedEventsOut)
*req->PollReturnedEventsOut = returnedEvents;
information = returnedEvents;
break;
}
default:
status = STATUS_INVALID_PARAMETER;
break;
}
*statusOut = status;
*informationOut = information;
}
// Worker for public async socket requests.
static void NetAsyncSocketWorker(void* arg) {
(void)arg;
for (;;) {
KSTATUS waitStatus = KeWaitSemaphore(gAsyncSocketSemaphore);
if (!KSUCCESS(waitStatus)) {
KeYield();
continue;
}
NET_ASYNC_SOCKET_REQUEST_OBJECT* req = NetAsyncSocketDequeue();
if (!req)
continue;
KSTATUS status = STATUS_CANCELLED;
size_t information = 0;
if (!IoAsyncRequestIsCancelRequested(&req->Async) &&
IoAsyncRequestGetState(&req->Async) != IoAsyncRequestCancelled) {
NetAsyncSocketExecute(req, &status, &information);
}
NetAsyncSocketReleaseExtraLocks(req);
NetAsyncSocketReleaseKernelStorage(req);
IoCompleteAsyncRequest(&req->Async, status, information);
ObDereferenceObject(&req->Async.Header);
}
}
// Starts the async socket worker on first use.
static KSTATUS NetEnsureAsyncSocketWorkerStarted(void) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketInitLock);
if (gAsyncSocketInitState == 2) {
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
return STATUS_SUCCESS;
}
if (gAsyncSocketInitState == 1) {
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
while (gAsyncSocketInitState == 1)
KeYield();
return (gAsyncSocketInitState == 2) ? STATUS_SUCCESS : STATUS_FAILURE;
}
gAsyncSocketInitState = 1;
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
LIST_INIT(&gAsyncSocketQueue);
KSTATUS status = KeSemaphoreCreate(0, 0x7fffffff, &gAsyncSocketSemaphore);
if (KSUCCESS(status)) {
THREAD* worker = (THREAD*)0;
status = KeCreateKernelThread((uintptr_t)NetAsyncSocketWorker,
0,
PriorityNormal,
&worker);
(void)worker;
}
flags = KeAcquireSpinlock(&gAsyncSocketInitLock);
gAsyncSocketInitState = KSUCCESS(status) ? 2 : 0;
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
return status;
}
// Submits a public async socket request to the socket worker.
KSTATUS NetSocketSubmitAsyncKernel(const NET_ASYNC_SOCKET_SUBMIT* submit,
NET_ASYNC_SOCKET_REQUEST_OBJECT** requestOut) {
if (requestOut)
*requestOut = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
if (!submit || !submit->OwnerProcess || !submit->Socket || !submit->Iosb)
return STATUS_INVALID_PARAMETER;
KSTATUS status = NetEnsureAsyncSocketWorkerStarted();
if (!KSUCCESS(status))
return status;
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
status = IoCreateAsyncRequest(sizeof(NET_ASYNC_SOCKET_REQUEST_OBJECT),
NetAsyncSocketRequestCleanup,
(void*)0,
NetCancelAsyncSocketRequest,
(void*)0,
&async);
if (!KSUCCESS(status))
return status;
NET_ASYNC_SOCKET_REQUEST_OBJECT* req = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)async;
req->OwnerProcess = submit->OwnerProcess;
req->Socket = submit->Socket;
req->Operation = submit->Operation;
req->SocketFlags = submit->SocketFlags;
req->Backlog = submit->Backlog;
req->ShutdownHow = submit->ShutdownHow;
req->Option = submit->Option;
req->PollEvents = submit->PollEvents;
req->KernelBuffer = submit->KernelBuffer;
req->UserBuffer = submit->UserBuffer;
req->Length = submit->Length;
req->Address = submit->Address;
req->AddressSize = submit->AddressSize;
req->AddressOut = submit->AddressOut;
req->AddressSizeInOut = submit->AddressSizeInOut;
req->OptionValue = submit->OptionValue;
req->UserOptionValue = submit->UserOptionValue;
req->OptionValueSize = submit->OptionValueSize;
req->OptionActualSizeOut = submit->OptionActualSizeOut;
req->HandleOut = submit->HandleOut;
req->PollReturnedEventsOut = submit->PollReturnedEventsOut;
req->LockedAddressOut = submit->LockedAddressOut;
req->LockedAddressSize = submit->LockedAddressSize;
req->LockedOptionValue = submit->LockedOptionValue;
req->LockedOptionActualSize = submit->LockedOptionActualSize;
req->LockedHandleOut = submit->LockedHandleOut;
req->LockedPollReturnedEvents = submit->LockedPollReturnedEvents;
ObReferenceObject(&req->OwnerProcess->Header);
ObReferenceObject(&req->Socket->Header);
IoInitializeAsyncRequestCompletion(&req->Async,
submit->Iosb,
submit->CompletionEvent,
submit->CompletionApc,
submit->LockedBuffer,
submit->LockedIosb);
ObReferenceObject(&req->Async.Header);
uintptr_t qflags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
ListInsertTail(&gAsyncSocketQueue, &req->Async.Link);
KeReleaseSpinlock(&gAsyncSocketQueueLock, qflags);
KeSignalSemaphore(gAsyncSocketSemaphore);
if (requestOut)
*requestOut = req;
else
ObDereferenceObject(&req->Async.Header);
return STATUS_PENDING;
}
// Returns the object header embedded in a socket async request.
OBJECT* NetAsyncSocketRequestObject(NET_ASYNC_SOCKET_REQUEST_OBJECT* request) {
return request ? &request->Async.Header : (OBJECT*)0;
}
// Submits a socket provider request through the active stack DEVICE_OBJECT.
static KSTATUS NetSubmitSocketIrp(const NET_STACK_PROVIDER* provider,
NET_SOCKET_IRP_REQUEST* request,
IO_ASYNC_REQUEST_OBJECT* async) {
if (!provider || !provider->StackDevice || !request)
return STATUS_INVALID_PARAMETER;
if (request->Size != sizeof(*request))
return STATUS_INVALID_PARAMETER;
if (request->Operation == NetSocketIrpInvalid ||
request->Operation >= NetSocketIrpOperationCount)
return STATUS_INVALID_PARAMETER;
request->CancelRequested = async ? &async->CancelRequested : (volatile uint8_t*)0;
EVENT* event = NULL;
KSTATUS st = KeEventCreate(EventAutoReset, 0, &event);
if (!KSUCCESS(st))
return st;
IRP irp;
IoInitializeIrp(&irp, 1);
IoBuildDeviceIoControlIrp(&irp,
IRP_MJ_INTERNAL_DEVICE_CONTROL,
NET_IOCTL_SOCKET,
request,
sizeof(*request),
NULL,
0);
irp.CompletionEvent = event;
NetIrpStatsSubmit(request->Operation);
st = IoSubmitIrp(provider->StackDevice, &irp);
bool asynchronous = false;
if (st == STATUS_PENDING) {
asynchronous = true;
NetIrpStatsPending();
(void)KeWaitForEvent(event, 0);
st = irp.Status;
}
NetIrpStatsComplete(st, asynchronous);
ObDereferenceObject(&event->Header);
return st;
}
// Closes a provider socket through the IRP path while object teardown is still safe.
static void NetSocketCloseProvider(const NET_STACK_PROVIDER* provider, void* providerSocket) {
if (!provider || !providerSocket)
return;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpClose;
request.u.Close.Socket = providerSocket;
(void)NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Releases provider state owned by a process socket object.
static void NetSocketDestroy(OBJECT* object) {
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)object;
if (socket->Provider && socket->ProviderSocket) {
socket->Provider->SocketClose(socket->ProviderSocket);
NetSocketCloseProvider(socket->Provider, socket->ProviderSocket);
socket->ProviderSocket = NULL;
}
}
// Allocates a process-visible socket object around provider-owned state.
static KSTATUS NetSocketAllocate(const NET_STACK_PROVIDER* provider,
void* providerSocket,
uint32_t domain,
@@ -44,6 +610,7 @@ static KSTATUS NetSocketAllocate(const NET_STACK_PROVIDER* provider,
return STATUS_SUCCESS;
}
// Returns the provider and provider-private state behind a live socket object.
static KSTATUS NetSocketGetProviderSocket(SOCKET_OBJECT* socket,
const NET_STACK_PROVIDER** providerOut,
void** providerSocketOut) {
@@ -57,7 +624,7 @@ static KSTATUS NetSocketGetProviderSocket(SOCKET_OBJECT* socket,
return STATUS_SUCCESS;
}
// Implements the net Socket Create operation.
// Creates a socket by submitting a create request to the active stack device.
KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKET_OBJECT** out) {
if (!out)
return STATUS_INVALID_PARAMETER;
@@ -68,7 +635,16 @@ KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKE
return STATUS_NOT_SUPPORTED;
void* providerSocket = NULL;
KSTATUS st = provider->SocketCreate(domain, type, protocol, &providerSocket);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpCreate;
request.u.Create.Domain = domain;
request.u.Create.Type = type;
request.u.Create.Protocol = protocol;
request.u.Create.ProviderSocketOut = &providerSocket;
KSTATUS st = NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
if (!KSUCCESS(st))
return st;
if (!providerSocket)
@@ -76,28 +652,49 @@ KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKE
st = NetSocketAllocate(provider, providerSocket, domain, type, protocol, out);
if (!KSUCCESS(st))
provider->SocketClose(providerSocket);
NetSocketCloseProvider(provider, providerSocket);
return st;
}
// Implements the net Socket Bind operation.
// Binds a socket address through the stack device IRP path.
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Bind(providerSocket, address, addressSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpBind;
request.u.Bind.Socket = providerSocket;
request.u.Bind.Address = address;
request.u.Bind.AddressSize = addressSize;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Listen operation.
// Marks a stream socket as listening through the stack device IRP path.
KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Listen(providerSocket, backlog) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpListen;
request.u.Listen.Socket = providerSocket;
request.u.Listen.Backlog = backlog;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Accept operation.
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
// Accepts a provider socket through IRP and wraps the result in a process socket object.
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut,
SYS_SOCKET_ADDRESS_K* remoteOut,
IO_ASYNC_REQUEST_OBJECT* async) {
if (!acceptedOut)
return STATUS_INVALID_PARAMETER;
*acceptedOut = NULL;
@@ -109,7 +706,15 @@ KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_
return st;
void* acceptedProviderSocket = NULL;
st = provider->Accept(providerSocket, &acceptedProviderSocket, remoteOut);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpAccept;
request.u.Accept.Socket = providerSocket;
request.u.Accept.AcceptedOut = &acceptedProviderSocket;
request.u.Accept.RemoteOut = remoteOut;
st = NetSubmitSocketIrp(provider, &request, async);
if (!KSUCCESS(st))
return st;
if (!acceptedProviderSocket)
@@ -122,19 +727,32 @@ KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_
socket->Protocol,
acceptedOut);
if (!KSUCCESS(st))
provider->SocketClose(acceptedProviderSocket);
NetSocketCloseProvider(provider, acceptedProviderSocket);
return st;
}
// Implements the net Socket Connect operation.
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
// Connects a socket through the stack device IRP path.
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address,
size_t addressSize, IO_ASYNC_REQUEST_OBJECT* async) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Connect(providerSocket, address, addressSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpConnect;
request.u.Connect.Socket = providerSocket;
request.u.Connect.Address = address;
request.u.Connect.AddressSize = addressSize;
return NetSubmitSocketIrp(provider, &request, async);
}
// Implements the net Socket Send operation.
// Sends stream bytes through the stack device IRP path.
KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut) {
if (sentOut)
*sentOut = 0;
@@ -146,11 +764,25 @@ KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length,
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Send(providerSocket, buffer, length, flags, sentOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSend;
request.u.Send.Socket = providerSocket;
request.u.Send.Buffer = buffer;
request.u.Send.Length = length;
request.u.Send.Flags = flags;
request.u.Send.SentOut = sentOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Receive operation.
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut) {
// Receives stream bytes through the stack device IRP path.
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length,
uint32_t flags, size_t* receivedOut,
IO_ASYNC_REQUEST_OBJECT* async) {
if (receivedOut)
*receivedOut = 0;
if (!buffer && length)
@@ -161,9 +793,23 @@ KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uin
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Receive(providerSocket, buffer, length, flags, receivedOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpReceive;
request.u.Receive.Socket = providerSocket;
request.u.Receive.Buffer = buffer;
request.u.Receive.Length = length;
request.u.Receive.Flags = flags;
request.u.Receive.ReceivedOut = receivedOut;
return NetSubmitSocketIrp(provider, &request, async);
}
// Sends datagram bytes through the stack device IRP path.
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags,
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
if (sentOut)
@@ -180,11 +826,27 @@ KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->SendTo(providerSocket, buffer, length, flags, remote, remoteSize, sentOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSendTo;
request.u.SendTo.Socket = providerSocket;
request.u.SendTo.Buffer = buffer;
request.u.SendTo.Length = length;
request.u.SendTo.Flags = flags;
request.u.SendTo.Remote = remote;
request.u.SendTo.RemoteSize = remoteSize;
request.u.SendTo.SentOut = sentOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
// Receives datagram bytes through the stack device IRP path.
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length,
uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut,
size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async) {
if (receivedOut)
*receivedOut = 0;
if (!buffer && length)
@@ -199,34 +861,80 @@ KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length,
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->ReceiveFrom(providerSocket, buffer, length, flags, remoteOut, receivedOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpReceiveFrom;
request.u.ReceiveFrom.Socket = providerSocket;
request.u.ReceiveFrom.Buffer = buffer;
request.u.ReceiveFrom.Length = length;
request.u.ReceiveFrom.Flags = flags;
request.u.ReceiveFrom.RemoteOut = remoteOut;
request.u.ReceiveFrom.ReceivedOut = receivedOut;
return NetSubmitSocketIrp(provider, &request, async);
}
// Implements the net Socket Shutdown operation.
// Shuts down one or both socket directions through the stack device IRP path.
KSTATUS NetSocketShutdown(SOCKET_OBJECT* socket, uint32_t how) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Shutdown(providerSocket, how) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpShutdown;
request.u.Shutdown.Socket = providerSocket;
request.u.Shutdown.How = how;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Set Option operation.
// Sets a socket option through the stack device IRP path.
KSTATUS NetSocketSetOption(SOCKET_OBJECT* socket, uint32_t option, const void* value, size_t valueSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->SetOption(providerSocket, option, value, valueSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSetOption;
request.u.SetOption.Socket = providerSocket;
request.u.SetOption.Option = option;
request.u.SetOption.Value = value;
request.u.SetOption.ValueSize = valueSize;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Get Option operation.
// Reads a socket option through the stack device IRP path.
KSTATUS NetSocketGetOption(SOCKET_OBJECT* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->GetOption(providerSocket, option, value, valueSize, actualSizeOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpGetOption;
request.u.GetOption.Socket = providerSocket;
request.u.GetOption.Option = option;
request.u.GetOption.Value = value;
request.u.GetOption.ValueSize = valueSize;
request.u.GetOption.ActualSizeOut = actualSizeOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Poll operation.
// Polls socket readiness through the stack device IRP path.
KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returnedEventsOut) {
if (!returnedEventsOut)
return STATUS_INVALID_PARAMETER;
@@ -235,10 +943,20 @@ KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returned
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Poll(providerSocket, events, returnedEventsOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpPoll;
request.u.Poll.Socket = providerSocket;
request.u.Poll.Events = events;
request.u.Poll.ReturnedEventsOut = returnedEventsOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Resolve Host operation.
// Resolves a host name through the stack device IRP path.
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
if (!name || !addressOut)
return STATUS_INVALID_PARAMETER;
@@ -247,5 +965,12 @@ KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K*
if (!provider)
return STATUS_NOT_SUPPORTED;
return provider->ResolveHost(name, family, addressOut);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpResolveHost;
request.u.ResolveHost.Name = name;
request.u.ResolveHost.Family = family;
request.u.ResolveHost.AddressOut = addressOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
+6 -6
View File
@@ -6,6 +6,7 @@
#include <ntlite/ddi/net_device.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_spinlock.h>
#include <ntlite/ddi/io_driver.h>
static const NET_STACK_PROVIDER* gNetStackProvider = 0;
static SPINLOCK gNetStackLock = SPINLOCK_INIT;
@@ -15,13 +16,12 @@ static KSTATUS NetValidateStackProvider(const NET_STACK_PROVIDER* p) {
if (!p || p->Version != NET_STACK_PROVIDER_VERSION || !p->Name)
return STATUS_INVALID_PARAMETER;
if (!p->AttachDevice || !p->ReceiveFrame || !p->SocketCreate || !p->SocketClose ||
!p->Bind || !p->Listen || !p->Accept || !p->Connect ||
!p->Send || !p->Receive || !p->SendTo || !p->ReceiveFrom ||
!p->Shutdown || !p->SetOption || !p->GetOption || !p->Poll ||
!p->ResolveHost) {
if (!p->StackDevice || !p->AttachDevice || !p->ReceiveFrame)
return STATUS_INVALID_PARAMETER;
if (!p->StackDevice->DriverObject ||
!p->StackDevice->DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL])
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
+1
View File
@@ -27,6 +27,7 @@ static uint32_t KiObjectPoolTag(OBJECT_TYPE type) {
case ObjectTypeInputQueue: return MM_POOL_TAG('i', 'n', 'p', 'q');
case ObjectTypeImageSection: return MM_POOL_TAG('i', 'm', 's', 'e');
case ObjectTypeTimer: return MM_POOL_TAG('t', 'm', 'r', ' ');
case ObjectTypeModule: return MM_POOL_TAG('m', 'o', 'd', 'u');
default: return MM_POOL_TAG('o', 'b', 'j', ' ');
}
}
+151 -24
View File
@@ -4,7 +4,8 @@
// net_stack.h - provider interface between kernel socket facade and network stack drivers.
// The kernel owns process handles, user-memory copying, NET_DEVICE registration,
// and SOCKET_OBJECT lifetime. A stack driver owns protocol implementation.
// and SOCKET_OBJECT lifetime. A stack driver owns protocol implementation behind
// a DEVICE_OBJECT so socket operations enter the same IRP pipe as other I/O.
#include <stdint.h>
#include <stddef.h>
@@ -12,39 +13,165 @@
#include <ntlite/ddi/net_device.h>
#include <ntlite/ddi/net_abi.h>
#define NET_STACK_PROVIDER_VERSION 1u
struct DEVICE_OBJECT;
#define NET_STACK_PROVIDER_VERSION 2u
#define NET_IOCTL_SOCKET 0x4E455401u
typedef enum NET_SOCKET_IRP_OPERATION {
NetSocketIrpInvalid = 0,
NetSocketIrpCreate = 1,
NetSocketIrpClose = 2,
NetSocketIrpBind = 3,
NetSocketIrpListen = 4,
NetSocketIrpAccept = 5,
NetSocketIrpConnect = 6,
NetSocketIrpSend = 7,
NetSocketIrpReceive = 8,
NetSocketIrpSendTo = 9,
NetSocketIrpReceiveFrom = 10,
NetSocketIrpShutdown = 11,
NetSocketIrpSetOption = 12,
NetSocketIrpGetOption = 13,
NetSocketIrpPoll = 14,
NetSocketIrpResolveHost = 15,
NetSocketIrpOperationCount,
} NET_SOCKET_IRP_OPERATION;
typedef struct NET_SOCKET_IRP_REQUEST {
uint32_t Size;
uint32_t Operation;
volatile uint8_t* CancelRequested;
union {
struct {
uint32_t Domain;
uint32_t Type;
uint32_t Protocol;
void** ProviderSocketOut;
} Create;
struct {
void* Socket;
} Close;
struct {
void* Socket;
const SYS_SOCKET_ADDRESS_K* Address;
size_t AddressSize;
} Bind;
struct {
void* Socket;
uint32_t Backlog;
} Listen;
struct {
void* Socket;
void** AcceptedOut;
SYS_SOCKET_ADDRESS_K* RemoteOut;
} Accept;
struct {
void* Socket;
const SYS_SOCKET_ADDRESS_K* Address;
size_t AddressSize;
} Connect;
struct {
void* Socket;
const void* Buffer;
size_t Length;
uint32_t Flags;
size_t* SentOut;
} Send;
struct {
void* Socket;
void* Buffer;
size_t Length;
uint32_t Flags;
size_t* ReceivedOut;
} Receive;
struct {
void* Socket;
const void* Buffer;
size_t Length;
uint32_t Flags;
const SYS_SOCKET_ADDRESS_K* Remote;
size_t RemoteSize;
size_t* SentOut;
} SendTo;
struct {
void* Socket;
void* Buffer;
size_t Length;
uint32_t Flags;
SYS_SOCKET_ADDRESS_K* RemoteOut;
size_t* ReceivedOut;
} ReceiveFrom;
struct {
void* Socket;
uint32_t How;
} Shutdown;
struct {
void* Socket;
uint32_t Option;
const void* Value;
size_t ValueSize;
} SetOption;
struct {
void* Socket;
uint32_t Option;
void* Value;
size_t ValueSize;
size_t* ActualSizeOut;
} GetOption;
struct {
void* Socket;
uint32_t Events;
uint32_t* ReturnedEventsOut;
} Poll;
struct {
const char* Name;
uint32_t Family;
SYS_SOCKET_ADDRESS_K* AddressOut;
} ResolveHost;
} u;
} NET_SOCKET_IRP_REQUEST;
typedef struct NET_IRP_STATS {
uint64_t Submitted;
uint64_t Completed;
uint64_t CompletedSynchronously;
uint64_t CompletedAsynchronously;
uint64_t Pending;
uint64_t CancelRequested;
uint64_t Cancelled;
uint64_t WouldBlock;
uint64_t Timeout;
uint64_t Failed;
uint64_t ByOperation[NetSocketIrpOperationCount];
} NET_IRP_STATS;
typedef struct NET_STACK_PROVIDER NET_STACK_PROVIDER;
struct NET_STACK_PROVIDER {
uint32_t Version;
const char* Name;
struct DEVICE_OBJECT* StackDevice;
KSTATUS (*AttachDevice)(NET_DEVICE* device);
KSTATUS (*ReceiveFrame)(NET_DEVICE* device, const void* frame, size_t length);
KSTATUS (*SocketCreate)(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket);
void (*SocketClose)(void* socket);
KSTATUS (*Bind)(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS (*Listen)(void* socket, uint32_t backlog);
KSTATUS (*Accept)(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
KSTATUS (*Connect)(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS (*Send)(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
KSTATUS (*Receive)(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
KSTATUS (*SendTo)(void* socket, const void* buffer, size_t length, uint32_t flags,
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
KSTATUS (*ReceiveFrom)(void* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
KSTATUS (*Shutdown)(void* socket, uint32_t how);
KSTATUS (*SetOption)(void* socket, uint32_t option, const void* value, size_t valueSize);
KSTATUS (*GetOption)(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
KSTATUS (*Poll)(void* socket, uint32_t events, uint32_t* returnedEventsOut);
KSTATUS (*ResolveHost)(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
};
__declspec(dllexport) KSTATUS NetRegisterStackProvider(const NET_STACK_PROVIDER* provider);
__declspec(dllexport) const NET_STACK_PROVIDER* NetGetStackProvider(void);
__declspec(dllexport) void NetQueryIrpStats(NET_IRP_STATS* out);
+140
View File
@@ -431,6 +431,8 @@ typedef struct SYS_PROCESS_INFO {
uint32_t ThreadCount;
uint32_t HandleCount;
// ModuleCount is the number of DLL-like process modules recorded by the loader.
// The main executable image is reported separately through ImageBase/ImageSize.
uint32_t ModuleCount;
uint32_t CurrentDrive;
@@ -452,6 +454,54 @@ uint32_t SysGetProcessList(SYS_PROCESS_INFO* processesOut, size_t maxCount, size
uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut);
uint32_t SysGetThreadList(uint32_t pid, SYS_THREAD_INFO* threadsOut, size_t maxCount, size_t* countOut);
#define SYS_MODULE_NAME_MAX 64u
#define SYS_MODULE_INFO_VERSION 1u
#define SYS_LOAD_MODULE_FLAG_NONE 0u
#define SYS_MODULE_FLAG_PUBLIC_REF 0x00000001u
#define SYS_MODULE_FLAG_IMPORT_PINNED 0x00000002u
#define SYS_MODULE_FLAG_UNLOADABLE 0x00000004u
typedef struct SYS_MODULE_INFO {
uint32_t Size;
uint32_t Version;
uint32_t Flags;
uint32_t Reserved;
uintptr_t Base;
size_t ImageSize;
uintptr_t EntryPoint;
uint32_t PublicRefCount;
uint32_t ImportRefCount;
uint32_t TotalRefCount;
uint32_t Reserved2;
char Name[SYS_MODULE_NAME_MAX];
} SYS_MODULE_INFO;
typedef struct SYS_LOAD_MODULE_REQUEST {
uint32_t Size;
uint32_t Flags;
SYS_STRING_VIEW Path;
uint32_t* ModuleHandleOut;
SYS_MODULE_INFO* InfoOut;
} SYS_LOAD_MODULE_REQUEST;
typedef struct SYS_GET_MODULE_EXPORT_REQUEST {
uint32_t Size;
uint32_t ModuleHandle;
SYS_STRING_VIEW Name;
uintptr_t* AddressOut;
} SYS_GET_MODULE_EXPORT_REQUEST;
uint32_t SysLoadModule(const SYS_LOAD_MODULE_REQUEST* request);
uint32_t SysUnloadModule(uint32_t moduleHandle);
uint32_t SysGetModuleInfo(uint32_t moduleHandle, SYS_MODULE_INFO* infoOut);
uint32_t SysGetModuleExport(const SYS_GET_MODULE_EXPORT_REQUEST* request);
uint32_t SysGetModuleList(SYS_MODULE_INFO* modulesOut, size_t maxCount, size_t* countOut);
uint32_t SysCreateThread(const SYS_CREATE_THREAD_REQUEST* request);
uint32_t SysGetCurrentTid(void);
uint32_t SysGetCurrentPid(void);
@@ -722,6 +772,60 @@ typedef struct SYS_POLLFD {
uint32_t ReturnedEvents;
} SYS_POLLFD;
#define SYS_ASYNC_SOCKET_FLAG_NONE 0x00000000u
#define SYS_ASYNC_SOCKET_OP_BIND 1u
#define SYS_ASYNC_SOCKET_OP_LISTEN 2u
#define SYS_ASYNC_SOCKET_OP_ACCEPT 3u
#define SYS_ASYNC_SOCKET_OP_CONNECT 4u
#define SYS_ASYNC_SOCKET_OP_SEND 5u
#define SYS_ASYNC_SOCKET_OP_RECV 6u
#define SYS_ASYNC_SOCKET_OP_SEND_TO 7u
#define SYS_ASYNC_SOCKET_OP_RECV_FROM 8u
#define SYS_ASYNC_SOCKET_OP_SHUTDOWN 9u
#define SYS_ASYNC_SOCKET_OP_SET_OPT 10u
#define SYS_ASYNC_SOCKET_OP_GET_OPT 11u
#define SYS_ASYNC_SOCKET_OP_POLL 12u
// Async socket requests return operation-specific byte counts through Iosb.
// Information is the byte count for send/receive, the accepted handle for
// accept, returned poll events for poll, and the actual option byte count for
// get-option. RequestHandleOut receives a waitable/cancellable async I/O handle
// when the request is queued. A zero handle means the operation failed before
// queueing.
typedef struct SYS_ASYNC_SOCKET_REQUEST {
uint32_t Size;
uint32_t SocketHandle;
uint32_t EventHandle;
uint32_t Operation;
uint32_t Flags;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* Buffer;
size_t Length;
const SYS_SOCKET_ADDRESS* Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
SYS_IO_STATUS_BLOCK* Iosb;
SYS_USER_APC_ROUTINE ApcRoutine;
void* ApcContext;
uint32_t* RequestHandleOut;
} SYS_ASYNC_SOCKET_REQUEST;
uint32_t SysCreateSocket(uint32_t domain, uint32_t type, uint32_t protocol, uint32_t* handleOut);
uint32_t SysBindSocket(uint32_t socket, const SYS_SOCKET_ADDRESS* address, size_t addressSize);
uint32_t SysListenSocket(uint32_t socket, uint32_t backlog);
@@ -736,6 +840,7 @@ uint32_t SysResolveHost(const SYS_RESOLVE_HOST_REQUEST* request);
uint32_t SysSetSocketOption(uint32_t socket, const SYS_SOCKET_OPTION_REQUEST* request);
uint32_t SysGetSocketOption(uint32_t socket, const SYS_SOCKET_OPTION_REQUEST* request);
uint32_t SysPoll(SYS_POLLFD* fds, size_t count, uint32_t timeoutMs, size_t* readyOut);
uint32_t SysSocketIoAsync(const SYS_ASYNC_SOCKET_REQUEST* request);
typedef struct SYS_MEMORY_STATUS {
uint64_t FreePhysicalBytes;
@@ -760,6 +865,7 @@ typedef struct SYS_MEMORY_STATUS {
#define SYS_KERNEL_STATS_SURFACE 9u
#define SYS_KERNEL_STATS_FILESYSTEM 10u
#define SYS_KERNEL_STATS_DISK 11u
#define SYS_KERNEL_STATS_NET_IRP 12u
// Public stats payloads start with Size and Version. Size is the byte count
// returned for that payload, including any trailing records. Version describes
@@ -1007,6 +1113,40 @@ typedef struct SYS_DISK_STATS {
uint64_t MaxReadaheadSectors;
} SYS_DISK_STATS;
#define SYS_NET_IRP_OPERATION_COUNT 16u
#define SYS_NET_IRP_OP_INVALID 0u
#define SYS_NET_IRP_OP_CREATE 1u
#define SYS_NET_IRP_OP_CLOSE 2u
#define SYS_NET_IRP_OP_BIND 3u
#define SYS_NET_IRP_OP_LISTEN 4u
#define SYS_NET_IRP_OP_ACCEPT 5u
#define SYS_NET_IRP_OP_CONNECT 6u
#define SYS_NET_IRP_OP_SEND 7u
#define SYS_NET_IRP_OP_RECEIVE 8u
#define SYS_NET_IRP_OP_SEND_TO 9u
#define SYS_NET_IRP_OP_RECEIVE_FROM 10u
#define SYS_NET_IRP_OP_SHUTDOWN 11u
#define SYS_NET_IRP_OP_SET_OPTION 12u
#define SYS_NET_IRP_OP_GET_OPTION 13u
#define SYS_NET_IRP_OP_POLL 14u
#define SYS_NET_IRP_OP_RESOLVE_HOST 15u
typedef struct SYS_NET_IRP_STATS {
uint32_t Size;
uint32_t Version;
uint64_t Submitted;
uint64_t Completed;
uint64_t CompletedSynchronously;
uint64_t CompletedAsynchronously;
uint64_t Pending;
uint64_t CancelRequested;
uint64_t Cancelled;
uint64_t WouldBlock;
uint64_t Timeout;
uint64_t Failed;
uint64_t ByOperation[SYS_NET_IRP_OPERATION_COUNT];
} SYS_NET_IRP_STATS;
typedef struct SYS_SWAP_STATS {
uint32_t Size;
uint32_t Version;
-2
View File
@@ -26,7 +26,6 @@ extern int waitpid(int pid, int *status, int options);
extern void exit(int status);
extern void clearscreen(void);
extern void clearkeyboard(void);
extern void PosixFdInit(void);
static int hIn = 0; static int hOut = 1;
@@ -176,7 +175,6 @@ static int runCommand(const char* cmd) {
// Runs the requested operation.
int main(void) {
PosixFdInit();
const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD];
+11
View File
@@ -84,6 +84,12 @@ extern int errno; // defined in posix_fd.c; single global in v1
#define MS_ASYNC 0x01
#define MS_INVALIDATE 0x02
// Dynamic loader flags.
#define RTLD_LAZY 0x0001
#define RTLD_NOW 0x0002
#define RTLD_LOCAL 0x0004
#define RTLD_GLOBAL 0x0008
// Scalar types.
typedef int32_t pid_t;
typedef int32_t off_t;
@@ -131,6 +137,11 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot, int flags,
__declspec(dllexport) int munmap(void* addr, size_t length);
__declspec(dllexport) int msync(void* addr, size_t length, int flags);
__declspec(dllexport) void* dlopen(const char* filename, int flags);
__declspec(dllexport) void* dlsym(void* handle, const char* symbol);
__declspec(dllexport) int dlclose(void* handle);
__declspec(dllexport) char* dlerror(void);
// Sockets.
typedef uint16_t sa_family_t;
typedef uint16_t in_port_t;
+118
View File
@@ -0,0 +1,118 @@
// POSIX compatibility support for dynamic module loading.
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#include "posix.h"
#include "posix_path.h"
#include "posix_fd.h"
static char gDlError[160];
static int gDlErrorPending;
// Clears the process-local dynamic-loader error string.
static void DlClearError(void) {
gDlError[0] = '\0';
gDlErrorPending = 0;
}
// Stores one process-local dynamic-loader error string.
static void DlSetError(const char* message) {
if (!message) message = "dynamic loader error";
strncpy(gDlError, message, sizeof(gDlError) - 1);
gDlError[sizeof(gDlError) - 1] = '\0';
gDlErrorPending = 1;
}
// Converts one native loader status to errno and a dlerror message.
static void DlSetStatusError(const char* operation, uint32_t status) {
(void)status;
errno = PosixStatusToErrno(status);
DlSetError(operation);
}
// Loads one DLL-like user module through the native module loader.
__declspec(dllexport) void* dlopen(const char* filename, int flags) {
(void)flags;
DlClearError();
if (!filename || !filename[0]) {
errno = EINVAL;
DlSetError("dlopen requires a module path");
return 0;
}
char native[512];
const char* path = PosixTranslatePath(filename, native, sizeof(native));
if (!path || strcmp(path, SYNTH_DEVNULL) == 0 || strcmp(path, SYNTH_DEVZERO) == 0) {
errno = EINVAL;
DlSetError("dlopen path is not a loadable module");
return 0;
}
uint32_t handle = 0;
SYS_LOAD_MODULE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Path = SysSvFromCstr(path);
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS || handle == 0) {
DlSetStatusError("dlopen failed", status);
return 0;
}
return (void*)(uintptr_t)handle;
}
// Resolves one named export from a native module handle.
__declspec(dllexport) void* dlsym(void* handle, const char* symbol) {
DlClearError();
if (!handle || !symbol || !symbol[0]) {
errno = EINVAL;
DlSetError("dlsym requires a module handle and symbol name");
return 0;
}
uintptr_t address = 0;
SYS_GET_MODULE_EXPORT_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.ModuleHandle = (uint32_t)(uintptr_t)handle;
req.Name = SysSvFromCstr(symbol);
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
if (status != STATUS_SUCCESS || address == 0) {
DlSetStatusError("dlsym failed", status);
return 0;
}
return (void*)address;
}
// Releases one module handle returned by dlopen.
__declspec(dllexport) int dlclose(void* handle) {
DlClearError();
if (!handle) {
errno = EINVAL;
DlSetError("dlclose requires a module handle");
return -1;
}
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)handle);
if (status != STATUS_SUCCESS) {
DlSetStatusError("dlclose failed", status);
return -1;
}
return 0;
}
// Returns and clears the most recent dynamic-loader error string.
__declspec(dllexport) char* dlerror(void) {
if (!gDlErrorPending || !gDlError[0]) return 0;
gDlErrorPending = 0;
return gDlError;
}
+1 -1
View File
@@ -24,7 +24,7 @@ typedef struct {
#define FD_MAX 64
#define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS
// Called from _start before main(); sets up fds 0/1/2 for this process.
// Called from PosixStart before main(); sets up fds 0/1/2 for this process.
__declspec(dllexport) void PosixFdInit(void);
// Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure.
+39 -8
View File
@@ -1102,15 +1102,27 @@ __declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName
return lpModuleName ? NULL : WIN32_HMODULE_EXE;
}
// Loads library A.
// Loads a user-mode DLL through the native module loader.
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32;
gLastError = 2; // ERROR_FILE_NOT_FOUND
return NULL;
if (!lpLibFileName) { gLastError = 87; return NULL; }
uint32_t handle = 0;
SYS_LOAD_MODULE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Path = SysSvFromCstr(lpLibFileName);
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) {
gLastError = 126;
return NULL;
}
return (HANDLE)(uintptr_t)handle;
}
// Minimal GetProcAddress for code that probes kernel32 functions already present
// in this win32.dll image. Full dynamic DLL loading remains a kernel-loader job.
// GetProcAddress handles both built-in win32 exports and native module handles.
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule);
__declspec(dllexport) void Sleep(DWORD dwMilliseconds);
__declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
@@ -1129,7 +1141,7 @@ __declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
COORD dwCursorPosition);
// Returns proc Address.
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
if (hModule != WIN32_HMODULE_WIN32 || !lpProcName) { gLastError = 127; return NULL; }
if (!hModule || !lpProcName) { gLastError = 127; return NULL; }
// Ordinal probes are represented as small integer pointers. Do not treat
// them as strings while debugging misses.
@@ -1139,6 +1151,19 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
return NULL;
}
if (hModule != WIN32_HMODULE_WIN32) {
uintptr_t address = 0;
SYS_GET_MODULE_EXPORT_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.ModuleHandle = (uint32_t)(uintptr_t)hModule;
req.Name = SysSvFromCstr(lpProcName);
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
if (status != STATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
return (void*)address;
}
#define GP(name) if (strcmp(lpProcName, #name) == 0) return (void*)&name
GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress);
GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect);
@@ -1154,9 +1179,15 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
return NULL;
}
// Releases library.
// Releases one native module handle returned by LoadLibraryA.
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
(void)hLibModule;
if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE)
return TRUE;
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)hLibModule);
if (status != STATUS_SUCCESS) {
gLastError = 126;
return FALSE;
}
return TRUE;
}
+13
View File
@@ -0,0 +1,13 @@
// POSIX process startup. This file owns process-local POSIX personality initialization before main runs.
extern int main(void);
extern void exit(int status);
extern void PosixFdInit(void);
// Initializes POSIX process state and exits with main's return value.
void PosixStart(void) {
PosixFdInit();
int status = main();
exit(status);
for (;;) {}
}
+121 -115
View File
@@ -1,134 +1,140 @@
; Syscall numbers shared by MASM user-mode stubs.
; Process, threads, waits, and APC.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_OPEN_PROCESS EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_GET_THREAD_LIST EQU 9
SYS_GET_CURRENT_TID EQU 10
SYS_GET_CURRENT_PID EQU 11
SYS_SET_THREAD_PRIORITY EQU 12
SYS_YIELD EQU 13
SYS_SLEEP EQU 14
SYS_WAIT_SINGLE EQU 15
SYS_WAIT_MANY EQU 16
SYS_SET_USER_APC_DISPATCHER EQU 17
SYS_APC_RETURN EQU 18
; Process, threads, modules, waits, and APC.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_OPEN_PROCESS EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_GET_THREAD_LIST EQU 9
SYS_LOAD_MODULE EQU 10
SYS_UNLOAD_MODULE EQU 11
SYS_GET_MODULE_INFO EQU 12
SYS_GET_MODULE_EXPORT EQU 13
SYS_GET_MODULE_LIST EQU 14
SYS_GET_CURRENT_TID EQU 15
SYS_GET_CURRENT_PID EQU 16
SYS_SET_THREAD_PRIORITY EQU 17
SYS_YIELD EQU 18
SYS_SLEEP EQU 19
SYS_WAIT_SINGLE EQU 20
SYS_WAIT_MANY EQU 21
SYS_SET_USER_APC_DISPATCHER EQU 22
SYS_APC_RETURN EQU 23
; Virtual memory, mappings, and handles.
SYS_ALLOC_VM EQU 19
SYS_FREE_VM EQU 20
SYS_PROTECT_VM EQU 21
SYS_QUERY_VM EQU 22
SYS_CLOSE_HANDLE EQU 23
SYS_DUPLICATE_HANDLE EQU 24
SYS_CREATE_FILE_MAPPING EQU 25
SYS_MAP_FILE EQU 26
SYS_ALLOC_VM EQU 24
SYS_FREE_VM EQU 25
SYS_PROTECT_VM EQU 26
SYS_QUERY_VM EQU 27
SYS_CLOSE_HANDLE EQU 28
SYS_DUPLICATE_HANDLE EQU 29
SYS_CREATE_FILE_MAPPING EQU 30
SYS_MAP_FILE EQU 31
; Synchronization objects.
SYS_CREATE_MUTEX EQU 27
SYS_TRY_ACQUIRE_MUTEX EQU 28
SYS_RELEASE_MUTEX EQU 29
SYS_CREATE_SEM EQU 30
SYS_TRY_WAIT_SEM EQU 31
SYS_SIGNAL_SEM EQU 32
SYS_CREATE_EVENT EQU 33
SYS_SIGNAL_EVENT EQU 34
SYS_CLEAR_EVENT EQU 35
SYS_CREATE_MUTEX EQU 32
SYS_TRY_ACQUIRE_MUTEX EQU 33
SYS_RELEASE_MUTEX EQU 34
SYS_CREATE_SEM EQU 35
SYS_TRY_WAIT_SEM EQU 36
SYS_SIGNAL_SEM EQU 37
SYS_CREATE_EVENT EQU 38
SYS_SIGNAL_EVENT EQU 39
SYS_CLEAR_EVENT EQU 40
; Queues and named queues.
SYS_CREATE_QUEUE EQU 36
SYS_SEND_QUEUE EQU 37
SYS_RECV_QUEUE EQU 38
SYS_PUBLISH_QUEUE EQU 39
SYS_OPEN_NAMED_QUEUE EQU 40
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 41
SYS_CREATE_QUEUE EQU 41
SYS_SEND_QUEUE EQU 42
SYS_RECV_QUEUE EQU 43
SYS_PUBLISH_QUEUE EQU 44
SYS_OPEN_NAMED_QUEUE EQU 45
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 46
; Files, directories, and current directory.
SYS_OPEN_FILE EQU 42
SYS_READ_FILE EQU 43
SYS_WRITE_FILE EQU 44
SYS_READ_FILE_ASYNC EQU 45
SYS_WRITE_FILE_ASYNC EQU 46
SYS_CANCEL_IO EQU 47
SYS_SEEK_FILE EQU 48
SYS_GET_FILE_POS EQU 49
SYS_GET_FILE_SIZE EQU 50
SYS_GET_FILE_STAT EQU 51
SYS_STAT_FILE EQU 52
SYS_TRUNCATE_FILE EQU 53
SYS_FLUSH_FILE EQU 54
SYS_DELETE_FILE EQU 55
SYS_RENAME_FILE EQU 56
SYS_LOCK_FILE EQU 57
SYS_UNLOCK_FILE EQU 58
SYS_OPEN_DIR EQU 59
SYS_READ_DIR EQU 60
SYS_CREATE_DIR EQU 61
SYS_REMOVE_DIR EQU 62
SYS_SET_CWD EQU 63
SYS_REAL_PATH EQU 64
SYS_OPEN_FILE EQU 47
SYS_READ_FILE EQU 48
SYS_WRITE_FILE EQU 49
SYS_READ_FILE_ASYNC EQU 50
SYS_WRITE_FILE_ASYNC EQU 51
SYS_CANCEL_IO EQU 52
SYS_SEEK_FILE EQU 53
SYS_GET_FILE_POS EQU 54
SYS_GET_FILE_SIZE EQU 55
SYS_GET_FILE_STAT EQU 56
SYS_STAT_FILE EQU 57
SYS_TRUNCATE_FILE EQU 58
SYS_FLUSH_FILE EQU 59
SYS_DELETE_FILE EQU 60
SYS_RENAME_FILE EQU 61
SYS_LOCK_FILE EQU 62
SYS_UNLOCK_FILE EQU 63
SYS_OPEN_DIR EQU 64
SYS_READ_DIR EQU 65
SYS_CREATE_DIR EQU 66
SYS_REMOVE_DIR EQU 67
SYS_SET_CWD EQU 68
SYS_REAL_PATH EQU 69
; Console, display, and surfaces.
SYS_WRITE_CONSOLE EQU 65
SYS_GET_CONSOLE_INFO EQU 66
SYS_SET_CONSOLE_CURSOR EQU 67
SYS_FILL_CONSOLE_CHARACTER EQU 68
SYS_FILL_CONSOLE_ATTRIBUTE EQU 69
SYS_PUT_SCREEN_CHAR EQU 70
SYS_CLEAR_SCREEN EQU 71
SYS_GET_SCREEN_SIZE EQU 72
SYS_QUERY_DISPLAY_INFO EQU 73
SYS_CREATE_SURFACE EQU 74
SYS_MAP_SURFACE EQU 75
SYS_QUERY_SURFACE EQU 76
SYS_PRESENT_SURFACE EQU 77
SYS_WRITE_CONSOLE EQU 70
SYS_GET_CONSOLE_INFO EQU 71
SYS_SET_CONSOLE_CURSOR EQU 72
SYS_FILL_CONSOLE_CHARACTER EQU 73
SYS_FILL_CONSOLE_ATTRIBUTE EQU 74
SYS_PUT_SCREEN_CHAR EQU 75
SYS_CLEAR_SCREEN EQU 76
SYS_GET_SCREEN_SIZE EQU 77
SYS_QUERY_DISPLAY_INFO EQU 78
SYS_CREATE_SURFACE EQU 79
SYS_MAP_SURFACE EQU 80
SYS_QUERY_SURFACE EQU 81
SYS_PRESENT_SURFACE EQU 82
; Input.
SYS_POLL_KEYBOARD EQU 78
SYS_IS_KEY_DOWN EQU 79
SYS_READ_KEYBOARD_CHAR EQU 80
SYS_READ_KEY EQU 81
SYS_GET_KEY_MODIFIERS EQU 82
SYS_OPEN_INPUT_QUEUE EQU 83
SYS_READ_INPUT_EVENT EQU 84
SYS_PEEK_INPUT_EVENT EQU 85
SYS_PEEK_MOUSE EQU 86
SYS_READ_MOUSE EQU 87
SYS_GET_MOUSE_POS EQU 88
SYS_POLL_KEYBOARD EQU 83
SYS_IS_KEY_DOWN EQU 84
SYS_READ_KEYBOARD_CHAR EQU 85
SYS_READ_KEY EQU 86
SYS_GET_KEY_MODIFIERS EQU 87
SYS_OPEN_INPUT_QUEUE EQU 88
SYS_READ_INPUT_EVENT EQU 89
SYS_PEEK_INPUT_EVENT EQU 90
SYS_PEEK_MOUSE EQU 91
SYS_READ_MOUSE EQU 92
SYS_GET_MOUSE_POS EQU 93
; Time, debug, and system information.
SYS_CREATE_TIMER EQU 89
SYS_SET_TIMER EQU 90
SYS_CANCEL_TIMER EQU 91
SYS_GET_TICK EQU 92
SYS_GET_TICK_FREQUENCY EQU 93
SYS_GET_SYSTEM_TIME EQU 94
SYS_PRINT_DEBUG EQU 95
SYS_QUERY_TRAP_FRAME EQU 96
SYS_GET_MEMORY_STATUS EQU 97
SYS_QUERY_KERNEL_STATS EQU 98
SYS_CREATE_TIMER EQU 94
SYS_SET_TIMER EQU 95
SYS_CANCEL_TIMER EQU 96
SYS_GET_TICK EQU 97
SYS_GET_TICK_FREQUENCY EQU 98
SYS_GET_SYSTEM_TIME EQU 99
SYS_PRINT_DEBUG EQU 100
SYS_QUERY_TRAP_FRAME EQU 101
SYS_GET_MEMORY_STATUS EQU 102
SYS_QUERY_KERNEL_STATS EQU 103
; Native sockets and polling.
SYS_CREATE_SOCKET EQU 99
SYS_BIND_SOCKET EQU 100
SYS_LISTEN_SOCKET EQU 101
SYS_ACCEPT_SOCKET EQU 102
SYS_CONNECT_SOCKET EQU 103
SYS_SEND_SOCKET EQU 104
SYS_RECV_SOCKET EQU 105
SYS_SHUTDOWN_SOCKET EQU 106
SYS_SEND_TO_SOCKET EQU 107
SYS_RECV_FROM_SOCKET EQU 108
SYS_RESOLVE_HOST EQU 109
SYS_SET_SOCKET_OPTION EQU 110
SYS_GET_SOCKET_OPTION EQU 111
SYS_POLL EQU 112
SYS_CREATE_SOCKET EQU 104
SYS_SOCKET_IO_ASYNC EQU 105
SYS_BIND_SOCKET EQU 106
SYS_LISTEN_SOCKET EQU 107
SYS_ACCEPT_SOCKET EQU 108
SYS_CONNECT_SOCKET EQU 109
SYS_SEND_SOCKET EQU 110
SYS_RECV_SOCKET EQU 111
SYS_SHUTDOWN_SOCKET EQU 112
SYS_SEND_TO_SOCKET EQU 113
SYS_RECV_FROM_SOCKET EQU 114
SYS_RESOLVE_HOST EQU 115
SYS_SET_SOCKET_OPTION EQU 116
SYS_GET_SOCKET_OPTION EQU 117
SYS_POLL EQU 118
SYS_MAX EQU 113
SYS_MAX EQU 119
+11 -5
View File
@@ -89,7 +89,12 @@ SYSCALL_STUB2 OpenProcess, SYS_OPEN_PROCESS
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID
SYSCALL_STUB2 GetThreadList, SYS_GET_THREAD_LIST
SYSCALL_STUB2 GetThreadList, SYS_GET_THREAD_LIST
SYSCALL_STUB2 LoadModule, SYS_LOAD_MODULE
SYSCALL_STUB2 UnloadModule, SYS_UNLOAD_MODULE
SYSCALL_STUB2 GetModuleInfo, SYS_GET_MODULE_INFO
SYSCALL_STUB2 GetModuleExport, SYS_GET_MODULE_EXPORT
SYSCALL_STUB2 GetModuleList, SYS_GET_MODULE_LIST
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
@@ -239,6 +244,7 @@ SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
; Native sockets
; ---------------------------------------------------------------------------
SYSCALL_STUB2 CreateSocket, SYS_CREATE_SOCKET
SYSCALL_STUB2 SocketIoAsync, SYS_SOCKET_IO_ASYNC
SYSCALL_STUB2 BindSocket, SYS_BIND_SOCKET
SYSCALL_STUB2 ListenSocket, SYS_LISTEN_SOCKET
SYSCALL_STUB2 AcceptSocket, SYS_ACCEPT_SOCKET
@@ -248,10 +254,10 @@ SYSCALL_STUB6 RecvSocket, SYS_RECV_SOCKET
SYSCALL_STUB2 ShutdownSocket, SYS_SHUTDOWN_SOCKET
SYSCALL_STUB2 SendToSocket, SYS_SEND_TO_SOCKET
SYSCALL_STUB2 RecvFromSocket, SYS_RECV_FROM_SOCKET
SYSCALL_STUB2 ResolveHost, SYS_RESOLVE_HOST
SYSCALL_STUB2 SetSocketOption, SYS_SET_SOCKET_OPTION
SYSCALL_STUB2 GetSocketOption, SYS_GET_SOCKET_OPTION
SYSCALL_STUB2 Poll, SYS_POLL
SYSCALL_STUB2 ResolveHost, SYS_RESOLVE_HOST
SYSCALL_STUB2 SetSocketOption, SYS_SET_SOCKET_OPTION
SYSCALL_STUB2 GetSocketOption, SYS_GET_SOCKET_OPTION
SYSCALL_STUB2 Poll, SYS_POLL
SYSCALL_STUB2 GetMemoryStatus, SYS_GET_MEMORY_STATUS
SYSCALL_STUB2 QueryKernelStats, SYS_QUERY_KERNEL_STATS
@@ -0,0 +1,156 @@
// User-mode smoke test for public dynamic module loading.
#include <stdint.h>
#include <stddef.h>
#include <string.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 = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer.
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("module_load_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("module_load_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Checks whether one public module snapshot has the expected shape.
static void check_module_info(const SYS_MODULE_INFO* info) {
if (!info) fail("null module info");
if (info->Size != sizeof(*info)) fail("module info size mismatch");
if (info->Version != SYS_MODULE_INFO_VERSION) fail("module info version mismatch");
if (info->Base == 0 || info->ImageSize == 0) fail("module has empty mapping");
if ((info->Flags & SYS_MODULE_FLAG_PUBLIC_REF) == 0) fail("module missing public reference flag");
if (info->PublicRefCount == 0 || info->TotalRefCount == 0) fail("module reference counts missing");
if (strcmp(info->Name, "posix.dll") != 0) fail("module name mismatch");
}
// Returns whether the current module list contains the specified base address.
static int module_list_contains(uintptr_t base) {
size_t count = 0;
uint32_t status = SysGetModuleList(0, 0, &count);
if (status != STATUS_SUCCESS) fail_status("query module count", status);
if (count == 0) return 0;
if (count > 32) fail("module count unexpectedly large");
SYS_MODULE_INFO modules[32];
memset(modules, 0, sizeof(modules));
status = SysGetModuleList(modules, count, &count);
if (status != STATUS_SUCCESS) fail_status("query module list", status);
for (size_t i = 0; i < count; i++) {
if (modules[i].Base == base)
return 1;
}
return 0;
}
// Verifies export lookup through a module handle.
static void check_export_lookup(void) {
uint32_t handle = 0;
SYS_LOAD_MODULE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Path = SysSvFromCstr("syscall.dll");
req.ModuleHandleOut = &handle;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) fail_status("load syscall.dll", status);
if (handle == 0) fail("syscall load returned null handle");
uintptr_t address = 0;
SYS_GET_MODULE_EXPORT_REQUEST exportReq;
memset(&exportReq, 0, sizeof(exportReq));
exportReq.Size = sizeof(exportReq);
exportReq.ModuleHandle = handle;
exportReq.Name = SysSvFromCstr("SysGetCurrentPid");
exportReq.AddressOut = &address;
status = SysGetModuleExport(&exportReq);
if (status != STATUS_SUCCESS) fail_status("lookup SysGetCurrentPid", status);
if (address == 0) fail("export address is zero");
status = SysUnloadModule(handle);
if (status != STATUS_SUCCESS) fail_status("unload syscall.dll handle", status);
}
// Runs the public dynamic module loader smoke test.
int main(void) {
print("module_load_smoke: start\n");
check_export_lookup();
uint32_t handle = 0;
SYS_MODULE_INFO info;
memset(&info, 0, sizeof(info));
SYS_LOAD_MODULE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Path = SysSvFromCstr("posix.dll");
req.ModuleHandleOut = &handle;
req.InfoOut = &info;
uint32_t status = SysLoadModule(&req);
if (status != STATUS_SUCCESS) fail_status("load posix.dll", status);
if (handle == 0) fail("load returned null handle");
check_module_info(&info);
if (!module_list_contains(info.Base)) fail("loaded module absent from list");
SYS_MODULE_INFO byHandle;
memset(&byHandle, 0, sizeof(byHandle));
status = SysGetModuleInfo(handle, &byHandle);
if (status != STATUS_SUCCESS) fail_status("get module info", status);
check_module_info(&byHandle);
if (byHandle.Base != info.Base) fail("module handle base mismatch");
status = SysUnloadModule(handle);
if (status != STATUS_SUCCESS) fail_status("unload module", status);
memset(&byHandle, 0, sizeof(byHandle));
status = SysGetModuleInfo(handle, &byHandle);
if (status != STATUS_INVALID_HANDLE) fail_status("query unloaded handle", status);
print("module_load_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -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;
}
@@ -0,0 +1,65 @@
// User-mode smoke test for POSIX dynamic module loading.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#define RTLD_NOW 0x0002
#define RTLD_LOCAL 0x0004
typedef int64_t ssize_t;
extern ssize_t write(int fd, const void* buf, size_t count);
extern void exit(int status);
extern void* dlopen(const char* filename, int flags);
extern void* dlsym(void* handle, const char* symbol);
extern int dlclose(void* handle);
extern char* dlerror(void);
// Writes one NUL-terminated string to stdout.
static void print(const char* s) {
write(1, s, strlen(s));
}
// Terminates the current process.
static void exit_now(int code) {
for (;;) exit(code);
}
// Fails the smoke test with one message.
static void fail(const char* message) {
print("posix_module_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Verifies that dlerror reports and clears one loader error.
static void check_dlerror_clear(void) {
void* missing = dlsym(0, "definitely_missing");
if (missing) fail("dlsym unexpectedly succeeded");
char* first = dlerror();
if (!first || !first[0]) fail("dlerror did not report the failure");
char* second = dlerror();
if (second) fail("dlerror did not clear the failure");
}
// Runs the POSIX dynamic loader smoke test.
int main(void) {
print("posix_module_smoke: start\n");
check_dlerror_clear();
void* module = dlopen("syscall.dll", RTLD_NOW | RTLD_LOCAL);
if (!module) fail("dlopen syscall.dll failed");
typedef uint32_t (__stdcall *GETPID_FN)(void);
GETPID_FN getpid_native = (GETPID_FN)dlsym(module, "SysGetCurrentPid");
if (!getpid_native) fail("dlsym SysGetCurrentPid failed");
if (getpid_native() == 0) fail("resolved SysGetCurrentPid returned zero");
if (dlclose(module) != 0) fail("dlclose syscall.dll failed");
print("posix_module_smoke: PASS\n");
exit_now(0);
return 0;
}