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:
1 parent
ed3d065abd
commit
1f5477cf66
32 files changed
+3713
-551
No files matched your search
+10
-3
@@ -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"},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
@@ -28,6 +28,7 @@ typedef enum OBJECT_TYPE {
|
||||
ObjectTypeInputQueue = 17,
|
||||
ObjectTypeImageSection = 18,
|
||||
ObjectTypeTimer = 19,
|
||||
ObjectTypeModule = 20,
|
||||
} OBJECT_TYPE;
|
||||
|
||||
typedef struct OBJECT {
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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, ®isteredBase);
|
||||
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
@@ -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 +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;
|
||||
}
|
||||
|
||||
@@ -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', ' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user