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

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

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
// Kernel async I/O request declarations shared by implementation files and kernel clients.
#pragma once
// io_async.h - waitable asynchronous I/O request objects.
// Async request objects own the common completion, APC, wait, query, and
// cancellation surface used by file and socket I/O.
#include <stddef.h>
#include <stdint.h>
#include <kernel/ke_list.h>
#include <kernel/ob_object.h>
#include <ntlite/status.h>
struct EVENT;
struct KAPC;
struct IRP;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
// Completion status visible to async callers.
typedef struct IO_STATUS_BLOCK {
volatile KSTATUS Status;
volatile size_t Information;
} IO_STATUS_BLOCK;
typedef enum IO_ASYNC_REQUEST_STATE {
IoAsyncRequestQueued = 0,
IoAsyncRequestRunning = 1,
IoAsyncRequestCompleted = 2,
IoAsyncRequestCancelled = 3,
} IO_ASYNC_REQUEST_STATE;
typedef struct IO_ASYNC_REQUEST_OBJECT IO_ASYNC_REQUEST_OBJECT;
typedef void (*IO_ASYNC_REQUEST_CLEANUP_ROUTINE)(IO_ASYNC_REQUEST_OBJECT* request,
void* context);
typedef KSTATUS (*IO_ASYNC_REQUEST_CANCEL_ROUTINE)(IO_ASYNC_REQUEST_OBJECT* request,
void* context);
// Common waitable async I/O request object.
struct IO_ASYNC_REQUEST_OBJECT {
OBJECT Header;
LIST_ENTRY Link;
IO_ASYNC_REQUEST_STATE State;
uint8_t CancelRequested;
struct EVENT* WaitEvent;
struct EVENT* CompletionEvent;
struct KAPC* CompletionApc;
IO_STATUS_BLOCK* Iosb;
IO_STATUS_BLOCK IosbSnapshot;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
struct IRP* Irp;
IO_ASYNC_REQUEST_CLEANUP_ROUTINE CleanupRoutine;
void* CleanupContext;
IO_ASYNC_REQUEST_CANCEL_ROUTINE CancelRoutine;
void* CancelContext;
};
__declspec(dllexport) KSTATUS IoCreateAsyncRequest(size_t objectSize,
IO_ASYNC_REQUEST_CLEANUP_ROUTINE cleanupRoutine,
void* cleanupContext,
IO_ASYNC_REQUEST_CANCEL_ROUTINE cancelRoutine,
void* cancelContext,
IO_ASYNC_REQUEST_OBJECT** requestOut);
__declspec(dllexport) OBJECT* IoAsyncRequestObject(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) void IoInitializeAsyncRequestCompletion(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosb,
struct EVENT* completionEvent,
struct KAPC* completionApc,
MM_LOCKED_USER_BUFFER* lockedBuffer,
MM_LOCKED_USER_BUFFER* lockedIosb);
__declspec(dllexport) void IoAsyncRequestSetState(IO_ASYNC_REQUEST_OBJECT* request,
IO_ASYNC_REQUEST_STATE state);
__declspec(dllexport) IO_ASYNC_REQUEST_STATE IoAsyncRequestGetState(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) void IoAsyncRequestRequestCancel(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) uint8_t IoAsyncRequestIsCancelRequested(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) struct EVENT* IoGetAsyncRequestWaitEvent(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoWaitAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
uint32_t flags);
__declspec(dllexport) KSTATUS IoCancelAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoCompleteAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
KSTATUS status,
size_t information);
__declspec(dllexport) KSTATUS IoQueryAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut);
+1 -6
View File
@@ -10,6 +10,7 @@
#include <ntlite/status.h>
#include <kernel/ob_object.h>
#include <kernel/ke_spinlock.h>
#include <kernel/io_async.h>
#include <ntlite/string_view.h>
#include <ntlite/dirent.h>
#include <ntlite/file_stat.h>
@@ -137,12 +138,6 @@ typedef struct IO_FILESYSTEM {
void (*CloseDir)(struct IO_FILESYSTEM* fs, void* dirCtx);
} IO_FILESYSTEM;
// IO_STATUS_BLOCK - completion status visible to async callers.
typedef struct IO_STATUS_BLOCK {
volatile KSTATUS Status;
volatile size_t Information;
} IO_STATUS_BLOCK;
struct EVENT;
struct KAPC;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
+9 -3
View File
@@ -14,8 +14,9 @@
#define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256
#define PROCESS_MODULE_MAX 16
#define PROCESS_CWD_MAX 512
#define PROCESS_MODULE_MAX 16
#define PROCESS_MODULE_NAME_MAX 64
#define PROCESS_CWD_MAX 512
#define KE_PROCESS_FLAG_SYSTEM 0x00000001u
#define KE_PROCESS_FLAG_IDLE 0x00000002u
@@ -38,9 +39,13 @@ typedef struct HANDLE_ENTRY {
} HANDLE_ENTRY;
typedef struct PROCESS_MODULE {
char Name[32];
char Name[PROCESS_MODULE_NAME_MAX];
uintptr_t Base;
size_t Size;
uintptr_t EntryPoint;
uint32_t Flags;
uint32_t PublicRefCount;
uint32_t ImportRefCount;
} PROCESS_MODULE;
typedef struct PROCESS {
@@ -93,6 +98,7 @@ typedef struct PROCESS {
// Per-process loaded modules (user processes only).
PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
uint32_t ModuleCount;
SPINLOCK ModuleLock;
// Signaled (manual-reset) when the process exits.
// Waiters block on this via KeWaitForEvent.
+9 -1
View File
@@ -10,6 +10,7 @@
struct PROCESS;
struct FILE_OBJECT;
struct SYS_MODULE_INFO;
// Map a PE32+ image from a bootloader/driver raw buffer into kernel space.
// User images must be loaded from FILE_OBJECTs so image sections have stable identity.
@@ -38,7 +39,14 @@ KSTATUS LdrProtectImageForProcess(struct PROCESS* process, uintptr_t imageBase,
// Load a DLL for a specific process, resolve it against that process's
// loaded modules, and register it in the process-local registry.
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
#define LDR_MODULE_LOAD_IMPORT 0x00000001u
#define LDR_MODULE_LOAD_PUBLIC 0x00000002u
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uint32_t loadFlags, uintptr_t* baseOut);
KSTATUS LdrUnloadModuleForProcess(struct PROCESS* process, uintptr_t imageBase);
KSTATUS LdrQueryModuleForProcess(struct PROCESS* process, uintptr_t imageBase, struct SYS_MODULE_INFO* infoOut);
KSTATUS LdrCopyProcessModuleInfo(struct PROCESS* process, struct SYS_MODULE_INFO* infoOut, size_t maxCount, size_t* countOut);
void* LdrGetModuleExportForProcess(struct PROCESS* process, uintptr_t imageBase, const char* functionName);
// Called by KernelMain to register all modules from BOOT_INFO so that
// LdrGetExport can find them.
+54 -4
View File
@@ -11,8 +11,13 @@
#include <ntlite/status.h>
#include <kernel/ob_object.h>
#include <ntlite/ddi/net_abi.h>
#include <kernel/io_async.h>
struct NET_STACK_PROVIDER;
struct EVENT;
struct KAPC;
struct PROCESS;
typedef struct MM_LOCKED_USER_BUFFER MM_LOCKED_USER_BUFFER;
typedef struct SOCKET_OBJECT {
OBJECT Header; // must be first
@@ -25,17 +30,62 @@ typedef struct SOCKET_OBJECT {
uint32_t Protocol;
} SOCKET_OBJECT;
typedef struct NET_ASYNC_SOCKET_SUBMIT {
struct PROCESS* OwnerProcess;
SOCKET_OBJECT* Socket;
uint32_t Operation;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* KernelBuffer;
void* UserBuffer;
size_t Length;
SYS_SOCKET_ADDRESS_K Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
void* UserOptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
IO_STATUS_BLOCK* Iosb;
struct EVENT* CompletionEvent;
struct KAPC* CompletionApc;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
MM_LOCKED_USER_BUFFER* LockedAddressOut;
MM_LOCKED_USER_BUFFER* LockedAddressSize;
MM_LOCKED_USER_BUFFER* LockedOptionValue;
MM_LOCKED_USER_BUFFER* LockedOptionActualSize;
MM_LOCKED_USER_BUFFER* LockedHandleOut;
MM_LOCKED_USER_BUFFER* LockedPollReturnedEvents;
} NET_ASYNC_SOCKET_SUBMIT;
typedef struct NET_ASYNC_SOCKET_REQUEST_OBJECT NET_ASYNC_SOCKET_REQUEST_OBJECT;
KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKET_OBJECT** out);
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async);
KSTATUS NetSocketShutdown(SOCKET_OBJECT* socket, uint32_t how);
KSTATUS NetSocketSetOption(SOCKET_OBJECT* socket, uint32_t option, const void* value, size_t valueSize);
KSTATUS NetSocketGetOption(SOCKET_OBJECT* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returnedEventsOut);
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
KSTATUS NetSocketSubmitAsyncKernel(const NET_ASYNC_SOCKET_SUBMIT* submit,
NET_ASYNC_SOCKET_REQUEST_OBJECT** requestOut);
OBJECT* NetAsyncSocketRequestObject(NET_ASYNC_SOCKET_REQUEST_OBJECT* request);
+1
View File
@@ -28,6 +28,7 @@ typedef enum OBJECT_TYPE {
ObjectTypeInputQueue = 17,
ObjectTypeImageSection = 18,
ObjectTypeTimer = 19,
ObjectTypeModule = 20,
} OBJECT_TYPE;
typedef struct OBJECT {
+111 -105
View File
@@ -7,7 +7,7 @@
// R10 and R11 carry args 4-5 and are loaded by six-argument stubs.
// RAX carries the return value.
// Process, threads, waits, and APC.
// Process, threads, modules, waits, and APC.
#define SYS_EXIT_PROCESS 0
#define SYS_EXIT_THREAD 1
#define SYS_CREATE_PROCESS 2
@@ -18,124 +18,130 @@
#define SYS_GET_PROCESS_PID 7
#define SYS_CREATE_THREAD 8
#define SYS_GET_THREAD_LIST 9
#define SYS_GET_CURRENT_TID 10
#define SYS_GET_CURRENT_PID 11
#define SYS_SET_THREAD_PRIORITY 12
#define SYS_YIELD 13
#define SYS_SLEEP 14
#define SYS_WAIT_SINGLE 15
#define SYS_WAIT_MANY 16
#define SYS_SET_USER_APC_DISPATCHER 17
#define SYS_APC_RETURN 18
#define SYS_LOAD_MODULE 10
#define SYS_UNLOAD_MODULE 11
#define SYS_GET_MODULE_INFO 12
#define SYS_GET_MODULE_EXPORT 13
#define SYS_GET_MODULE_LIST 14
#define SYS_GET_CURRENT_TID 15
#define SYS_GET_CURRENT_PID 16
#define SYS_SET_THREAD_PRIORITY 17
#define SYS_YIELD 18
#define SYS_SLEEP 19
#define SYS_WAIT_SINGLE 20
#define SYS_WAIT_MANY 21
#define SYS_SET_USER_APC_DISPATCHER 22
#define SYS_APC_RETURN 23
// Virtual memory, mappings, and handles.
#define SYS_ALLOC_VM 19
#define SYS_FREE_VM 20
#define SYS_PROTECT_VM 21
#define SYS_QUERY_VM 22
#define SYS_CLOSE_HANDLE 23
#define SYS_DUPLICATE_HANDLE 24
#define SYS_CREATE_FILE_MAPPING 25
#define SYS_MAP_FILE 26
#define SYS_ALLOC_VM 24
#define SYS_FREE_VM 25
#define SYS_PROTECT_VM 26
#define SYS_QUERY_VM 27
#define SYS_CLOSE_HANDLE 28
#define SYS_DUPLICATE_HANDLE 29
#define SYS_CREATE_FILE_MAPPING 30
#define SYS_MAP_FILE 31
// Synchronization objects.
#define SYS_CREATE_MUTEX 27
#define SYS_TRY_ACQUIRE_MUTEX 28
#define SYS_RELEASE_MUTEX 29
#define SYS_CREATE_SEM 30
#define SYS_TRY_WAIT_SEM 31
#define SYS_SIGNAL_SEM 32
#define SYS_CREATE_EVENT 33
#define SYS_SIGNAL_EVENT 34
#define SYS_CLEAR_EVENT 35
#define SYS_CREATE_MUTEX 32
#define SYS_TRY_ACQUIRE_MUTEX 33
#define SYS_RELEASE_MUTEX 34
#define SYS_CREATE_SEM 35
#define SYS_TRY_WAIT_SEM 36
#define SYS_SIGNAL_SEM 37
#define SYS_CREATE_EVENT 38
#define SYS_SIGNAL_EVENT 39
#define SYS_CLEAR_EVENT 40
// Queues and named queues.
#define SYS_CREATE_QUEUE 36
#define SYS_SEND_QUEUE 37
#define SYS_RECV_QUEUE 38
#define SYS_PUBLISH_QUEUE 39
#define SYS_OPEN_NAMED_QUEUE 40
#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 41
#define SYS_CREATE_QUEUE 41
#define SYS_SEND_QUEUE 42
#define SYS_RECV_QUEUE 43
#define SYS_PUBLISH_QUEUE 44
#define SYS_OPEN_NAMED_QUEUE 45
#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 46
// Files, directories, and current directory.
#define SYS_OPEN_FILE 42
#define SYS_READ_FILE 43
#define SYS_WRITE_FILE 44
#define SYS_READ_FILE_ASYNC 45
#define SYS_WRITE_FILE_ASYNC 46
#define SYS_CANCEL_IO 47
#define SYS_SEEK_FILE 48
#define SYS_GET_FILE_POS 49
#define SYS_GET_FILE_SIZE 50
#define SYS_GET_FILE_STAT 51
#define SYS_STAT_FILE 52
#define SYS_TRUNCATE_FILE 53
#define SYS_FLUSH_FILE 54
#define SYS_DELETE_FILE 55
#define SYS_RENAME_FILE 56
#define SYS_LOCK_FILE 57
#define SYS_UNLOCK_FILE 58
#define SYS_OPEN_DIR 59
#define SYS_READ_DIR 60
#define SYS_CREATE_DIR 61
#define SYS_REMOVE_DIR 62
#define SYS_SET_CWD 63
#define SYS_REAL_PATH 64
#define SYS_OPEN_FILE 47
#define SYS_READ_FILE 48
#define SYS_WRITE_FILE 49
#define SYS_READ_FILE_ASYNC 50
#define SYS_WRITE_FILE_ASYNC 51
#define SYS_CANCEL_IO 52
#define SYS_SEEK_FILE 53
#define SYS_GET_FILE_POS 54
#define SYS_GET_FILE_SIZE 55
#define SYS_GET_FILE_STAT 56
#define SYS_STAT_FILE 57
#define SYS_TRUNCATE_FILE 58
#define SYS_FLUSH_FILE 59
#define SYS_DELETE_FILE 60
#define SYS_RENAME_FILE 61
#define SYS_LOCK_FILE 62
#define SYS_UNLOCK_FILE 63
#define SYS_OPEN_DIR 64
#define SYS_READ_DIR 65
#define SYS_CREATE_DIR 66
#define SYS_REMOVE_DIR 67
#define SYS_SET_CWD 68
#define SYS_REAL_PATH 69
// Console, display, and surfaces.
#define SYS_WRITE_CONSOLE 65
#define SYS_GET_CONSOLE_INFO 66
#define SYS_SET_CONSOLE_CURSOR 67
#define SYS_FILL_CONSOLE_CHARACTER 68
#define SYS_FILL_CONSOLE_ATTRIBUTE 69
#define SYS_PUT_SCREEN_CHAR 70
#define SYS_CLEAR_SCREEN 71
#define SYS_GET_SCREEN_SIZE 72
#define SYS_QUERY_DISPLAY_INFO 73
#define SYS_CREATE_SURFACE 74
#define SYS_MAP_SURFACE 75
#define SYS_QUERY_SURFACE 76
#define SYS_PRESENT_SURFACE 77
#define SYS_WRITE_CONSOLE 70
#define SYS_GET_CONSOLE_INFO 71
#define SYS_SET_CONSOLE_CURSOR 72
#define SYS_FILL_CONSOLE_CHARACTER 73
#define SYS_FILL_CONSOLE_ATTRIBUTE 74
#define SYS_PUT_SCREEN_CHAR 75
#define SYS_CLEAR_SCREEN 76
#define SYS_GET_SCREEN_SIZE 77
#define SYS_QUERY_DISPLAY_INFO 78
#define SYS_CREATE_SURFACE 79
#define SYS_MAP_SURFACE 80
#define SYS_QUERY_SURFACE 81
#define SYS_PRESENT_SURFACE 82
// Input.
#define SYS_POLL_KEYBOARD 78
#define SYS_IS_KEY_DOWN 79
#define SYS_READ_KEYBOARD_CHAR 80
#define SYS_READ_KEY 81
#define SYS_GET_KEY_MODIFIERS 82
#define SYS_OPEN_INPUT_QUEUE 83
#define SYS_READ_INPUT_EVENT 84
#define SYS_PEEK_INPUT_EVENT 85
#define SYS_PEEK_MOUSE 86
#define SYS_READ_MOUSE 87
#define SYS_GET_MOUSE_POS 88
#define SYS_POLL_KEYBOARD 83
#define SYS_IS_KEY_DOWN 84
#define SYS_READ_KEYBOARD_CHAR 85
#define SYS_READ_KEY 86
#define SYS_GET_KEY_MODIFIERS 87
#define SYS_OPEN_INPUT_QUEUE 88
#define SYS_READ_INPUT_EVENT 89
#define SYS_PEEK_INPUT_EVENT 90
#define SYS_PEEK_MOUSE 91
#define SYS_READ_MOUSE 92
#define SYS_GET_MOUSE_POS 93
// Time, debug, and system information.
#define SYS_CREATE_TIMER 89
#define SYS_SET_TIMER 90
#define SYS_CANCEL_TIMER 91
#define SYS_GET_TICK 92
#define SYS_GET_TICK_FREQUENCY 93
#define SYS_GET_SYSTEM_TIME 94
#define SYS_PRINT_DEBUG 95
#define SYS_QUERY_TRAP_FRAME 96
#define SYS_GET_MEMORY_STATUS 97
#define SYS_QUERY_KERNEL_STATS 98
#define SYS_CREATE_TIMER 94
#define SYS_SET_TIMER 95
#define SYS_CANCEL_TIMER 96
#define SYS_GET_TICK 97
#define SYS_GET_TICK_FREQUENCY 98
#define SYS_GET_SYSTEM_TIME 99
#define SYS_PRINT_DEBUG 100
#define SYS_QUERY_TRAP_FRAME 101
#define SYS_GET_MEMORY_STATUS 102
#define SYS_QUERY_KERNEL_STATS 103
// Native sockets and polling.
#define SYS_CREATE_SOCKET 99
#define SYS_BIND_SOCKET 100
#define SYS_LISTEN_SOCKET 101
#define SYS_ACCEPT_SOCKET 102
#define SYS_CONNECT_SOCKET 103
#define SYS_SEND_SOCKET 104
#define SYS_RECV_SOCKET 105
#define SYS_SHUTDOWN_SOCKET 106
#define SYS_SEND_TO_SOCKET 107
#define SYS_RECV_FROM_SOCKET 108
#define SYS_RESOLVE_HOST 109
#define SYS_SET_SOCKET_OPTION 110
#define SYS_GET_SOCKET_OPTION 111
#define SYS_POLL 112
#define SYS_CREATE_SOCKET 104
#define SYS_SOCKET_IO_ASYNC 105
#define SYS_BIND_SOCKET 106
#define SYS_LISTEN_SOCKET 107
#define SYS_ACCEPT_SOCKET 108
#define SYS_CONNECT_SOCKET 109
#define SYS_SEND_SOCKET 110
#define SYS_RECV_SOCKET 111
#define SYS_SHUTDOWN_SOCKET 112
#define SYS_SEND_TO_SOCKET 113
#define SYS_RECV_FROM_SOCKET 114
#define SYS_RESOLVE_HOST 115
#define SYS_SET_SOCKET_OPTION 116
#define SYS_GET_SOCKET_OPTION 117
#define SYS_POLL 118
#define SYS_MAX 113
#define SYS_MAX 119
+202
View File
@@ -0,0 +1,202 @@
// Kernel asynchronous I/O request object support.
// This file owns the common wait, completion, cancellation, APC, IOSB, and
// locked-buffer lifetime rules for all async I/O families.
#include <string.h>
#include <kernel/io_async.h>
#include <kernel/ke_event.h>
#include <kernel/ke_apc.h>
#include <kernel/mm_virt.h>
#include <ntlite/syscall.h>
// Releases resources owned by the common async request object.
static void KiIoAsyncRequestDestroy(OBJECT* obj) {
IO_ASYNC_REQUEST_OBJECT* request =
CONTAINING_RECORD(obj, IO_ASYNC_REQUEST_OBJECT, Header);
if (request->CleanupRoutine)
request->CleanupRoutine(request, request->CleanupContext);
if (request->WaitEvent) {
ObDereferenceObject(&request->WaitEvent->Header);
request->WaitEvent = (struct EVENT*)0;
}
if (request->CompletionEvent) {
ObDereferenceObject(&request->CompletionEvent->Header);
request->CompletionEvent = (struct EVENT*)0;
}
if (request->LockedBuffer) {
MmUnlockUserBuffer(request->LockedBuffer);
request->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (request->LockedIosb) {
MmUnlockUserBuffer(request->LockedIosb);
request->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Allocates a typed async request object with the common header first.
__declspec(dllexport) KSTATUS IoCreateAsyncRequest(size_t objectSize,
IO_ASYNC_REQUEST_CLEANUP_ROUTINE cleanupRoutine,
void* cleanupContext,
IO_ASYNC_REQUEST_CANCEL_ROUTINE cancelRoutine,
void* cancelContext,
IO_ASYNC_REQUEST_OBJECT** requestOut) {
if (!requestOut) return STATUS_INVALID_PARAMETER;
*requestOut = (IO_ASYNC_REQUEST_OBJECT*)0;
if (objectSize < sizeof(IO_ASYNC_REQUEST_OBJECT)) return STATUS_INVALID_PARAMETER;
IO_ASYNC_REQUEST_OBJECT* request =
(IO_ASYNC_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo, objectSize);
if (!request) return STATUS_NO_MEMORY;
request->Header.Destroy = KiIoAsyncRequestDestroy;
LIST_INIT(&request->Link);
request->State = IoAsyncRequestQueued;
request->CancelRequested = 0;
request->IosbSnapshot.Status = STATUS_PENDING;
request->IosbSnapshot.Information = 0;
request->CleanupRoutine = cleanupRoutine;
request->CleanupContext = cleanupContext;
request->CancelRoutine = cancelRoutine;
request->CancelContext = cancelContext;
KSTATUS status = KeEventCreate(EventManualReset, 0, &request->WaitEvent);
if (!KSUCCESS(status)) {
ObDereferenceObject(&request->Header);
return status;
}
*requestOut = request;
return STATUS_SUCCESS;
}
// Returns the object header embedded in an async request.
__declspec(dllexport) OBJECT* IoAsyncRequestObject(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? &request->Header : (OBJECT*)0;
}
// Attaches caller-visible completion state to a new async request.
__declspec(dllexport) void IoInitializeAsyncRequestCompletion(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosb,
EVENT* completionEvent,
KAPC* completionApc,
MM_LOCKED_USER_BUFFER* lockedBuffer,
MM_LOCKED_USER_BUFFER* lockedIosb) {
if (!request) return;
request->CompletionEvent = completionEvent;
request->CompletionApc = completionApc;
request->Iosb = iosb;
request->LockedBuffer = lockedBuffer;
request->LockedIosb = lockedIosb;
request->IosbSnapshot.Status = STATUS_PENDING;
request->IosbSnapshot.Information = 0;
if (iosb) {
iosb->Status = STATUS_PENDING;
iosb->Information = 0;
}
if (completionEvent)
ObReferenceObject(&completionEvent->Header);
}
// Updates the lifecycle state owned by the request's operation family.
__declspec(dllexport) void IoAsyncRequestSetState(IO_ASYNC_REQUEST_OBJECT* request,
IO_ASYNC_REQUEST_STATE state) {
if (request) request->State = state;
}
// Returns the last lifecycle state published by the operation family.
__declspec(dllexport) IO_ASYNC_REQUEST_STATE IoAsyncRequestGetState(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->State : IoAsyncRequestCancelled;
}
// Marks a request as cancelled so providers can observe it before completion.
__declspec(dllexport) void IoAsyncRequestRequestCancel(IO_ASYNC_REQUEST_OBJECT* request) {
if (request) request->CancelRequested = 1;
}
// Returns whether cancellation has been requested for the async request.
__declspec(dllexport) uint8_t IoAsyncRequestIsCancelRequested(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->CancelRequested : 0;
}
// Returns the wait event owned by an async request.
__declspec(dllexport) EVENT* IoGetAsyncRequestWaitEvent(IO_ASYNC_REQUEST_OBJECT* request) {
return request ? request->WaitEvent : (EVENT*)0;
}
// Waits for an async request to complete according to wait flags.
__declspec(dllexport) KSTATUS IoWaitAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
uint32_t flags) {
if (!request || !request->WaitEvent) return STATUS_INVALID_PARAMETER;
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
return KeWaitForEvent(request->WaitEvent,
(flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
}
// Cancels an async request through its operation-family cancel routine.
__declspec(dllexport) KSTATUS IoCancelAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request) {
if (!request) return STATUS_INVALID_PARAMETER;
if (request->CancelRoutine)
return request->CancelRoutine(request, request->CancelContext);
request->CancelRequested = 1;
if (request->State == IoAsyncRequestCompleted ||
request->State == IoAsyncRequestCancelled)
return STATUS_OBJECT_DEAD;
return STATUS_PENDING;
}
// Completes an async request and publishes all caller-visible completion state.
__declspec(dllexport) KSTATUS IoCompleteAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
KSTATUS status,
size_t information) {
if (!request) return STATUS_INVALID_PARAMETER;
request->IosbSnapshot.Status = status;
request->IosbSnapshot.Information = information;
request->State = (status == STATUS_CANCELLED) ? IoAsyncRequestCancelled
: IoAsyncRequestCompleted;
if (request->Iosb) {
request->Iosb->Status = status;
request->Iosb->Information = information;
}
// User APCs run against the same IOSB state that waiters will observe.
if (request->CompletionApc) {
if (!KeInsertQueueApc(request->CompletionApc,
request->Iosb,
(void*)(uintptr_t)status)) {
if (request->CompletionApc->RundownRoutine)
request->CompletionApc->RundownRoutine(request->CompletionApc);
}
}
if (request->CompletionEvent)
KeSignalEvent(request->CompletionEvent);
if (request->WaitEvent)
KeSignalEvent(request->WaitEvent);
if (request->LockedBuffer) {
MmUnlockUserBuffer(request->LockedBuffer);
request->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (request->LockedIosb) {
MmUnlockUserBuffer(request->LockedIosb);
request->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
return STATUS_SUCCESS;
}
// Copies the last completion snapshot out of an async request.
__declspec(dllexport) KSTATUS IoQueryAsyncRequest(IO_ASYNC_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut) {
if (!request || !iosbOut) return STATUS_INVALID_PARAMETER;
iosbOut->Status = request->IosbSnapshot.Status;
iosbOut->Information = request->IosbSnapshot.Information;
return STATUS_SUCCESS;
}
+77 -166
View File
@@ -32,31 +32,12 @@ typedef enum IO_ASYNC_FILE_OP {
IoAsyncFileWrite = 1,
} IO_ASYNC_FILE_OP;
typedef enum IO_ASYNC_FILE_STATE {
IoAsyncStateQueued = 0,
IoAsyncStateRunning = 1,
IoAsyncStateCompleted = 2,
IoAsyncStateCancelled = 3,
} IO_ASYNC_FILE_STATE;
struct IO_ASYNC_FILE_REQUEST_OBJECT {
OBJECT Header; // ObjectTypeAsyncIo
LIST_ENTRY Link; // protected by gAsyncFileQueueLock
IO_ASYNC_REQUEST_OBJECT Async;
IO_ASYNC_FILE_OP Op;
IO_ASYNC_FILE_STATE State;
uint8_t CancelRequested;
FILE_OBJECT* File; // referenced until object destruction
EVENT* WaitEvent; // internal completion event
EVENT* CompletionEvent; // optional, referenced until object destruction
KAPC* CompletionApc; // caller-owned APC storage
IO_STATUS_BLOCK* Iosb; // caller/user-owned completion block
IO_STATUS_BLOCK IosbSnapshot;
MM_LOCKED_USER_BUFFER* LockedBuffer;
MM_LOCKED_USER_BUFFER* LockedIosb;
void* Buffer; // caller-owned; user buffers must be locked until completion/cancel
FILE_OBJECT* File;
void* Buffer;
size_t Size;
uint64_t Offset;
};
@@ -228,72 +209,45 @@ static void KiIoSignalFileLockWaitersLocked(const IO_FILE_IDENTITY* identity) {
}
}
// Implements the io Async File Request Destroy operation.
static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
// Releases file-specific references owned by an async file request.
static void KiIoAsyncFileRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(obj, IO_ASYNC_FILE_REQUEST_OBJECT, Header);
(IO_ASYNC_FILE_REQUEST_OBJECT*)async;
if (req->WaitEvent) {
ObDereferenceObject(&req->WaitEvent->Header);
req->WaitEvent = (EVENT*)0;
}
if (req->CompletionEvent) {
ObDereferenceObject(&req->CompletionEvent->Header);
req->CompletionEvent = (EVENT*)0;
}
if (req->File) {
ObDereferenceObject(&req->File->Header);
req->File = (FILE_OBJECT*)0;
}
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
}
static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
KSTATUS status,
size_t information) {
if (!req) return;
req->IosbSnapshot.Status = status;
req->IosbSnapshot.Information = information;
// Cancels an async file request from the common async request surface.
static KSTATUS KiIoCancelAsyncFileRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
if (!async) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = (status == STATUS_CANCELLED) ? IoAsyncStateCancelled : IoAsyncStateCompleted;
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
if (state == IoAsyncRequestQueued) {
ListRemove(&async->Link);
IoAsyncRequestRequestCancel(async);
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
ObDereferenceObject(&async->Header);
return STATUS_SUCCESS;
}
if (state == IoAsyncRequestRunning) {
IoAsyncRequestRequestCancel(async);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
if (req->Iosb) {
req->Iosb->Status = status;
req->Iosb->Information = information;
}
// Match IRP completion ordering: queue APC first, then signal the event.
if (req->CompletionApc) {
if (!KeInsertQueueApc(req->CompletionApc, req->Iosb, (void*)(uintptr_t)status)) {
if (req->CompletionApc->RundownRoutine)
req->CompletionApc->RundownRoutine(req->CompletionApc);
}
}
if (req->CompletionEvent)
KeSignalEvent(req->CompletionEvent);
if (req->WaitEvent)
KeSignalEvent(req->WaitEvent);
if (req->LockedBuffer) {
MmUnlockUserBuffer(req->LockedBuffer);
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedIosb) {
MmUnlockUserBuffer(req->LockedIosb);
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
return STATUS_OBJECT_DEAD;
}
// Implements the io Async File Dequeue operation.
@@ -306,12 +260,12 @@ static IO_ASYNC_FILE_REQUEST_OBJECT* KiIoAsyncFileDequeue(void) {
}
IO_ASYNC_FILE_REQUEST_OBJECT* req =
CONTAINING_RECORD(node, IO_ASYNC_FILE_REQUEST_OBJECT, Link);
(IO_ASYNC_FILE_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
if (req->CancelRequested) {
req->State = IoAsyncStateCancelled;
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
} else {
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return req;
@@ -331,9 +285,9 @@ static void KiIoAsyncFileWorker(void* arg) {
IO_ASYNC_FILE_REQUEST_OBJECT* req = KiIoAsyncFileDequeue();
if (!req) continue;
if (req->CancelRequested || req->State == IoAsyncStateCancelled) {
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue/worker reference
if (IoAsyncRequestIsCancelRequested(&req->Async) || IoAsyncRequestGetState(&req->Async) == IoAsyncRequestCancelled) {
IoCompleteAsyncRequest(&req->Async, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
continue;
}
@@ -346,7 +300,7 @@ static void KiIoAsyncFileWorker(void* arg) {
req->Size, &done);
}
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
// The operation may already have reached the filesystem. The
// completion contract still reports cancellation so waiters/APCs do
// not mistake a cancelled request for useful data.
@@ -354,8 +308,8 @@ static void KiIoAsyncFileWorker(void* arg) {
done = 0;
}
KiIoCompleteAsyncFileRequest(req, s, done);
ObDereferenceObject(&req->Header); // queue/worker reference
IoCompleteAsyncRequest(&req->Async, s, done);
ObDereferenceObject(&req->Async.Header); // queue/worker reference
}
}
@@ -404,13 +358,13 @@ static void KiIoNativeFsAsyncComplete(void* completeContext,
(IO_ASYNC_FILE_REQUEST_OBJECT*)completeContext;
if (!req) return;
if (req->CancelRequested) {
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
status = STATUS_CANCELLED;
information = 0;
}
KiIoCompleteAsyncFileRequest(req, status, information);
ObDereferenceObject(&req->Header); // native filesystem provider reference
IoCompleteAsyncRequest(&req->Async, status, information);
ObDereferenceObject(&req->Async.Header); // native filesystem provider reference
}
static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
@@ -443,45 +397,30 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
iosb->Status = STATUS_PENDING;
iosb->Information = 0;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
(IO_ASYNC_FILE_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo,
sizeof(IO_ASYNC_FILE_REQUEST_OBJECT));
if (!req) {
iosb->Status = STATUS_NO_MEMORY;
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
KSTATUS createStatus = IoCreateAsyncRequest(sizeof(IO_ASYNC_FILE_REQUEST_OBJECT),
KiIoAsyncFileRequestCleanup,
(void*)0,
KiIoCancelAsyncFileRequest,
(void*)0,
&async);
if (!KSUCCESS(createStatus)) {
iosb->Status = createStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
return STATUS_NO_MEMORY;
return createStatus;
}
req->Header.Destroy = KiIoAsyncFileRequestDestroy;
LIST_INIT(&req->Link);
IO_ASYNC_FILE_REQUEST_OBJECT* req = (IO_ASYNC_FILE_REQUEST_OBJECT*)async;
req->Op = op;
req->State = IoAsyncStateQueued;
req->CancelRequested = 0;
KSTATUS waitStatus = KeEventCreate(EventManualReset, 0, &req->WaitEvent);
if (!KSUCCESS(waitStatus)) {
iosb->Status = waitStatus;
MmUnlockUserBuffer(lockedBuffer);
MmUnlockUserBuffer(lockedIosb);
ObDereferenceObject(&req->Header);
return waitStatus;
}
req->File = file;
req->CompletionEvent = completionEvent;
req->CompletionApc = completionApc;
req->Iosb = iosb;
req->IosbSnapshot.Status = STATUS_PENDING;
req->IosbSnapshot.Information = 0;
req->LockedBuffer = lockedBuffer;
req->LockedIosb = lockedIosb;
req->Buffer = buffer;
req->Size = size;
req->Offset = offset;
IoInitializeAsyncRequestCompletion(&req->Async, iosb, completionEvent,
completionApc, lockedBuffer, lockedIosb);
ObReferenceObject(&file->Header);
if (completionEvent)
ObReferenceObject(&completionEvent->Header);
IO_FS_ASYNC_COMPLETE complete = KiIoNativeFsAsyncComplete;
KSTATUS ns = STATUS_NOT_SUPPORTED;
@@ -490,10 +429,10 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (hasNative) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
req->State = IoAsyncStateRunning;
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
ObReferenceObject(&req->Header); // native filesystem provider reference
ObReferenceObject(&req->Async.Header); // native filesystem provider reference
if (op == IoAsyncFileRead) {
ns = file->Fs->ReadAsync(file->Fs, file->FileCtx, offset, buffer,
size, complete, req);
@@ -505,34 +444,34 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req;
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
}
ObDereferenceObject(&req->Header); // provider rejected synchronously
ObDereferenceObject(&req->Async.Header); // provider rejected synchronously
iosb->Status = ns;
req->IosbSnapshot.Status = ns;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header); // creator reference
req->Async.IosbSnapshot.Status = ns;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header); // creator reference
return ns;
}
KSTATUS s = KiIoEnsureAsyncFileIoStarted();
if (!KSUCCESS(s)) {
iosb->Status = s;
req->IosbSnapshot.Status = s;
req->IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Header);
req->Async.IosbSnapshot.Status = s;
req->Async.IosbSnapshot.Information = 0;
ObDereferenceObject(&req->Async.Header);
return s;
}
// Queue ownership keeps the request alive until the worker completes or a
// successful queued cancellation completes it from IoCancelAsyncFileRequest.
ObReferenceObject(&req->Header);
ObReferenceObject(&req->Async.Header);
uintptr_t qflags = KeAcquireSpinlock(&gAsyncFileQueueLock);
ListInsertTail(&gAsyncFileQueue, &req->Link);
ListInsertTail(&gAsyncFileQueue, &req->Async.Link);
KeReleaseSpinlock(&gAsyncFileQueueLock, qflags);
KeSignalSemaphore(gAsyncFileSemaphore);
@@ -540,7 +479,7 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
if (requestOut) {
*requestOut = req; // creator reference returned to caller
} else {
ObDereferenceObject(&req->Header);
ObDereferenceObject(&req->Async.Header);
}
return STATUS_PENDING;
@@ -574,56 +513,28 @@ __declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const vo
// Implements the async File Request Object operation.
__declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? &req->Header : (OBJECT*)0;
return req ? &req->Async.Header : (OBJECT*)0;
}
// Returns the wait event owned by an async file request.
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
return req ? req->WaitEvent : (EVENT*)0;
return req ? IoGetAsyncRequestWaitEvent(&req->Async) : (EVENT*)0;
}
// Waits for an async file request to complete according to wait flags.
__declspec(dllexport) KSTATUS IoWaitAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req, uint32_t flags) {
if (!req || !req->WaitEvent) return STATUS_INVALID_PARAMETER;
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
return KeWaitForEvent(req->WaitEvent, (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
return IoWaitAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0, flags);
}
// Cancels async File Request.
// Cancels an async file request through the common async I/O object.
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
if (!req) return STATUS_INVALID_PARAMETER;
IO_ASYNC_FILE_STATE state;
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
state = req->State;
if (state == IoAsyncStateQueued) {
ListRemove(&req->Link);
req->CancelRequested = 1;
req->State = IoAsyncStateCancelled;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
ObDereferenceObject(&req->Header); // queue reference removed above
return STATUS_SUCCESS;
}
if (state == IoAsyncStateRunning) {
req->CancelRequested = 1;
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
return STATUS_OBJECT_DEAD;
return IoCancelAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0);
}
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
IO_STATUS_BLOCK* iosbOut) {
if (!req || !iosbOut) return STATUS_INVALID_PARAMETER;
iosbOut->Status = req->IosbSnapshot.Status;
iosbOut->Information = req->IosbSnapshot.Information;
return STATUS_SUCCESS;
return IoQueryAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0,
iosbOut);
}
// Current Working Directory
+5
View File
@@ -114,7 +114,9 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) {
info->ImageBase = p->ImageBase;
info->ImageSize = p->ImageSize;
info->PebBase = p->UserPebBase;
uintptr_t modFlags = KeAcquireSpinlock(&p->ModuleLock);
info->ModuleCount = p->ModuleCount;
KeReleaseSpinlock(&p->ModuleLock, modFlags);
uintptr_t handleFlags = KeAcquireSpinlock(&p->HandleLock);
uint32_t threadCount = p->ThreadCount;
@@ -288,7 +290,9 @@ static void KiProcessRundownResources(PROCESS* proc) {
MmVadDestroyAllForProcess(proc);
proc->ImageBase = 0;
proc->ImageSize = 0;
uintptr_t modFlags = KeAcquireSpinlock(&proc->ModuleLock);
proc->ModuleCount = 0;
KeReleaseSpinlock(&proc->ModuleLock, modFlags);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
proc->UserImagePathBase = 0;
@@ -360,6 +364,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
proc->ModuleCount = 0;
proc->ModuleLock = (SPINLOCK)SPINLOCK_INIT;
proc->CurrentDirectory[0] = '\0';
proc->CurrentDirectoryLength = 0;
proc->CurrentDrive = KeGetSystemRootDrive();
+699 -10
View File
@@ -62,8 +62,10 @@
#include <kernel/syscall_nr.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/io_file.h>
#include <kernel/io_async.h>
#include <ntlite/ddi/io_driver.h>
#include <kernel/net_socket.h>
#include <ntlite/ddi/net_stack.h>
#include <kernel/ke_trap.h>
#include <kernel/ke_input.h>
#include <kernel/ke_display.h>
@@ -119,6 +121,45 @@ static void KiFileMappingDestroy(OBJECT* obj) {
}
}
typedef struct MODULE_HANDLE_OBJECT {
OBJECT Header;
PROCESS* Owner;
uintptr_t Base;
} MODULE_HANDLE_OBJECT;
// Releasing the last module handle releases the public load reference it owns.
static void KiModuleHandleDestroy(OBJECT* obj) {
MODULE_HANDLE_OBJECT* module = CONTAINING_RECORD(obj, MODULE_HANDLE_OBJECT, Header);
if (module->Owner && module->Base) {
(void)LdrUnloadModuleForProcess(module->Owner, module->Base);
module->Base = 0;
}
if (module->Owner) {
ObDereferenceObject(&module->Owner->Header);
module->Owner = (PROCESS*)0;
}
}
// Creates a user-visible module handle for one public load reference.
static KSTATUS KiCreateModuleHandle(PROCESS* owner, uintptr_t base, uint32_t* handleOut) {
if (!owner || !base || !handleOut) return STATUS_INVALID_PARAMETER;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)ObAllocateObject(ObjectTypeModule, sizeof(MODULE_HANDLE_OBJECT));
if (!module) return STATUS_NO_MEMORY;
module->Header.Destroy = KiModuleHandleDestroy;
module->Owner = owner;
module->Base = base;
ObReferenceObject(&owner->Header);
KSTATUS s = KeAllocateHandleWithRights(owner,
&module->Header,
HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE | HANDLE_RIGHT_DUPLICATE,
handleOut);
ObDereferenceObject(&module->Header);
return s;
}
typedef struct SURFACE_OBJECT {
OBJECT Header;
PROCESS* Owner;
@@ -565,7 +606,7 @@ static KSTATUS KiHandleToWaitEvent(PROCESS* proc, uint32_t handle, EVENT** event
event = CONTAINING_RECORD(obj, EVENT, Header);
break;
case ObjectTypeAsyncIo:
event = IoGetAsyncFileRequestWaitEvent((IO_ASYNC_FILE_REQUEST_OBJECT*)obj);
event = IoGetAsyncRequestWaitEvent((IO_ASYNC_REQUEST_OBJECT*)obj);
break;
case ObjectTypeInputQueue:
event = KeInputQueueWaitEvent(CONTAINING_RECORD(obj, INPUT_QUEUE, Header));
@@ -1921,12 +1962,12 @@ static uint64_t SyscallWriteFileAsync(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallCancelIo(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
IO_ASYNC_FILE_REQUEST_OBJECT* req =
(IO_ASYNC_FILE_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE);
IO_ASYNC_REQUEST_OBJECT* req =
(IO_ASYNC_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE);
if (!req) return STATUS_INVALID_HANDLE;
KSTATUS s = IoCancelAsyncFileRequest(req);
ObDereferenceObject(IoAsyncFileRequestObject(req));
KSTATUS s = IoCancelAsyncRequest(req);
ObDereferenceObject(IoAsyncRequestObject(req));
return s;
}
@@ -3324,6 +3365,181 @@ static uint64_t SyscallGetThreadList(uint64_t a0, uint64_t a1, uint64_t a2,
return STATUS_SUCCESS;
}
// SYS_LOAD_MODULE a0=*SYS_LOAD_MODULE_REQUEST.
static uint64_t SyscallLoadModule(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SYS_LOAD_MODULE_REQUEST req;
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
if (!KSUCCESS(s)) return s;
if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER;
if (req.Flags != SYS_LOAD_MODULE_FLAG_NONE) return STATUS_INVALID_PARAMETER;
if (!req.ModuleHandleOut) return STATUS_INVALID_PARAMETER;
s = MmProbeUserWrite(req.ModuleHandleOut, sizeof(*req.ModuleHandleOut));
if (!KSUCCESS(s)) return s;
if (req.InfoOut) {
s = MmProbeUserWrite(req.InfoOut, sizeof(*req.InfoOut));
if (!KSUCCESS(s)) return s;
}
char pathBuf[SYSCALL_STRING_MAX];
SYS_STRING_VIEW path;
s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true);
if (!KSUCCESS(s)) return s;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
uintptr_t base = 0;
s = LdrLoadModuleForProcess(proc, pathBuf, LDR_MODULE_LOAD_PUBLIC, &base);
if (!KSUCCESS(s)) return s;
uint32_t handle = 0;
s = KiCreateModuleHandle(proc, base, &handle);
if (!KSUCCESS(s)) {
(void)LdrUnloadModuleForProcess(proc, base);
return s;
}
SYS_MODULE_INFO info;
if (req.InfoOut) {
s = LdrQueryModuleForProcess(proc, base, &info);
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
}
s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ModuleHandleOut, handle);
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
if (req.InfoOut) {
s = CopyToUser(req.InfoOut, &info, sizeof(info));
if (!KSUCCESS(s)) {
KeCloseHandle(proc, handle);
return s;
}
}
return STATUS_SUCCESS;
}
// SYS_UNLOAD_MODULE a0=moduleHandle.
static uint64_t SyscallUnloadModule(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
uint32_t handle = (uint32_t)a0;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(handle, ObjectTypeModule, HANDLE_RIGHT_TERMINATE);
if (!module) return STATUS_INVALID_HANDLE;
KeCloseHandle(proc, handle);
ObDereferenceObject(&module->Header);
return STATUS_SUCCESS;
}
// SYS_GET_MODULE_INFO a0=moduleHandle a1=*SYS_MODULE_INFO.
static uint64_t SyscallGetModuleInfo(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_MODULE_INFO));
if (!KSUCCESS(s)) return s;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeModule, HANDLE_RIGHT_QUERY);
if (!module) return STATUS_INVALID_HANDLE;
SYS_MODULE_INFO info;
s = LdrQueryModuleForProcess(module->Owner, module->Base, &info);
ObDereferenceObject(&module->Header);
if (!KSUCCESS(s)) return s;
return CopyToUser((void*)(uintptr_t)a1, &info, sizeof(info));
}
// SYS_GET_MODULE_EXPORT a0=*SYS_GET_MODULE_EXPORT_REQUEST.
static uint64_t SyscallGetModuleExport(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
SYS_GET_MODULE_EXPORT_REQUEST req;
KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req));
if (!KSUCCESS(s)) return s;
if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER;
if (!req.AddressOut) return STATUS_INVALID_PARAMETER;
s = MmProbeUserWrite(req.AddressOut, sizeof(*req.AddressOut));
if (!KSUCCESS(s)) return s;
char nameBuf[SYSCALL_STRING_MAX];
SYS_STRING_VIEW name;
s = KiImportUserTextView(req.Name, nameBuf, sizeof(nameBuf), &name, true);
if (!KSUCCESS(s)) return s;
if (name.Length == 0) return STATUS_INVALID_PARAMETER;
MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(req.ModuleHandle, ObjectTypeModule, HANDLE_RIGHT_QUERY);
if (!module) return STATUS_INVALID_HANDLE;
void* address = LdrGetModuleExportForProcess(module->Owner, module->Base, nameBuf);
ObDereferenceObject(&module->Header);
if (!address) return STATUS_NOT_FOUND;
uintptr_t userAddress = (uintptr_t)address;
return CopyToUser(req.AddressOut, &userAddress, sizeof(userAddress));
}
// SYS_GET_MODULE_LIST a0=*SYS_MODULE_INFO[] a1=maxCount a2=*countOut.
static uint64_t SyscallGetModuleList(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
size_t maxCount = (size_t)a1;
if (maxCount && !a0) return STATUS_INVALID_PARAMETER;
if (a2) {
KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a2, sizeof(size_t));
if (!KSUCCESS(s)) return s;
}
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE;
size_t total = 0;
KSTATUS s = LdrCopyProcessModuleInfo(proc, NULL, 0, &total);
if (s != STATUS_BUFFER_TOO_SMALL && !KSUCCESS(s)) return s;
size_t toCopy = total < maxCount ? total : maxCount;
if (toCopy) {
size_t bytes = 0;
if (!KiMulSize(toCopy, sizeof(SYS_MODULE_INFO), &bytes)) return STATUS_INVALID_PARAMETER;
SYS_MODULE_INFO* tmp = (SYS_MODULE_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('m', 'o', 'd', 'l'));
if (!tmp) return STATUS_NO_MEMORY;
size_t copiedTotal = 0;
s = LdrCopyProcessModuleInfo(proc, tmp, toCopy, &copiedTotal);
if (s == STATUS_BUFFER_TOO_SMALL)
s = STATUS_SUCCESS;
if (KSUCCESS(s))
s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(SYS_MODULE_INFO));
MmFreePool(tmp);
if (!KSUCCESS(s)) return s;
}
if (a2) return KiCopySizeToUser(a2, total);
return STATUS_SUCCESS;
}
// SYS_RENAME_FILE (66) a0=*oldPath a1=*newPath.
static uint64_t SyscallRenameFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
@@ -3459,6 +3675,39 @@ typedef struct SYS_SOCKET_OPTION_REQUEST_K {
size_t* ActualSizeOut;
} SYS_SOCKET_OPTION_REQUEST_K;
typedef struct SYS_ASYNC_SOCKET_REQUEST_K {
uint32_t Size;
uint32_t SocketHandle;
uint32_t EventHandle;
uint32_t Operation;
uint32_t Flags;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* Buffer;
size_t Length;
const SYS_SOCKET_ADDRESS_K* Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
SYS_IO_STATUS_BLOCK_K* Iosb;
SYS_USER_APC_ROUTINE_K ApcRoutine;
void* ApcContext;
uint32_t* RequestHandleOut;
} SYS_ASYNC_SOCKET_REQUEST_K;
typedef struct SYS_MEMORY_STATUS_K {
uint64_t FreePhysicalBytes;
uint64_t KernelPoolBytes;
@@ -3557,7 +3806,7 @@ static uint64_t SyscallAccept(uint64_t a0, uint64_t a1, uint64_t a2,
SOCKET_OBJECT* accepted = NULL;
SYS_SOCKET_ADDRESS_K remote;
KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL);
KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&listener->Header);
if (!KSUCCESS(s)) return s;
@@ -3599,7 +3848,7 @@ static uint64_t SyscallConnect(uint64_t a0, uint64_t a1, uint64_t a2,
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
if (!socket) return STATUS_NOT_FOUND;
s = NetSocketConnect(socket, &address, (size_t)a2);
s = NetSocketConnect(socket, &address, (size_t)a2, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
return s;
}
@@ -3661,7 +3910,7 @@ static uint64_t SyscallRecv(uint64_t a0, uint64_t a1, uint64_t a2,
if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; }
size_t received = 0;
s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received);
s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
if (KSUCCESS(s) && received)
@@ -3772,7 +4021,7 @@ static uint64_t SyscallRecvFrom(uint64_t a0, uint64_t a1, uint64_t a2,
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
size_t received = 0;
s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received);
s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received, (IO_ASYNC_REQUEST_OBJECT*)0);
ObDereferenceObject(&socket->Header);
if (KSUCCESS(s) && received)
@@ -3958,6 +4207,417 @@ static uint64_t SyscallPoll(uint64_t a0, uint64_t a1, uint64_t a2,
return s;
}
// Releases resources prepared for an async socket request before queue ownership starts.
static void SyscallAsyncSocketReleaseSubmitResources(NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!submit)
return;
if (submit->KernelBuffer) {
MmFreePool(submit->KernelBuffer);
submit->KernelBuffer = (void*)0;
}
if (submit->OptionValue) {
MmFreePool(submit->OptionValue);
submit->OptionValue = (void*)0;
}
if (submit->LockedBuffer) {
MmUnlockUserBuffer(submit->LockedBuffer);
submit->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedIosb) {
MmUnlockUserBuffer(submit->LockedIosb);
submit->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedAddressOut) {
MmUnlockUserBuffer(submit->LockedAddressOut);
submit->LockedAddressOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedAddressSize) {
MmUnlockUserBuffer(submit->LockedAddressSize);
submit->LockedAddressSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedOptionValue) {
MmUnlockUserBuffer(submit->LockedOptionValue);
submit->LockedOptionValue = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedOptionActualSize) {
MmUnlockUserBuffer(submit->LockedOptionActualSize);
submit->LockedOptionActualSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedHandleOut) {
MmUnlockUserBuffer(submit->LockedHandleOut);
submit->LockedHandleOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (submit->LockedPollReturnedEvents) {
MmUnlockUserBuffer(submit->LockedPollReturnedEvents);
submit->LockedPollReturnedEvents = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Locks an optional async socket output pointer for worker-thread completion.
static KSTATUS SyscallAsyncSocketLockOptional(PROCESS* proc,
void* ptr,
size_t size,
MM_LOCKED_USER_BUFFER** out) {
if (out)
*out = (MM_LOCKED_USER_BUFFER*)0;
if (!ptr || size == 0)
return STATUS_SUCCESS;
return MmLockUserBuffer(proc, ptr, size, 1, out);
}
// Copies an input address into a kernel-owned async socket request.
static KSTATUS SyscallAsyncSocketCopyInputAddress(const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!req || !submit || !req->Address)
return STATUS_INVALID_PARAMETER;
if (req->AddressSize < sizeof(SYS_SOCKET_ADDRESS_K))
return STATUS_BUFFER_TOO_SMALL;
KSTATUS status = CopyFromUser(&submit->Address, req->Address, sizeof(submit->Address));
if (!KSUCCESS(status))
return status;
submit->AddressSize = req->AddressSize;
return STATUS_SUCCESS;
}
// Validates and locks optional remote-address outputs for async accept/recvfrom.
static KSTATUS SyscallAsyncSocketPrepareAddressOutput(PROCESS* proc,
const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!req || !submit)
return STATUS_INVALID_PARAMETER;
if (!req->AddressOut)
return STATUS_SUCCESS;
if (!req->AddressSizeInOut)
return STATUS_INVALID_PARAMETER;
size_t capacity = 0;
KSTATUS status = CopyFromUser(&capacity, req->AddressSizeInOut, sizeof(capacity));
if (!KSUCCESS(status))
return status;
if (capacity < sizeof(SYS_SOCKET_ADDRESS_K)) {
size_t required = sizeof(SYS_SOCKET_ADDRESS_K);
(void)CopyToUser(req->AddressSizeInOut, &required, sizeof(required));
return STATUS_BUFFER_TOO_SMALL;
}
status = MmLockUserBuffer(proc, req->AddressOut, sizeof(SYS_SOCKET_ADDRESS_K), 1,
&submit->LockedAddressOut);
if (!KSUCCESS(status))
return status;
status = MmLockUserBuffer(proc, req->AddressSizeInOut, sizeof(*req->AddressSizeInOut), 1,
&submit->LockedAddressSize);
if (!KSUCCESS(status))
return status;
submit->AddressOut = req->AddressOut;
submit->AddressSizeInOut = req->AddressSizeInOut;
return STATUS_SUCCESS;
}
// Prepares kernel-owned buffers and locked outputs for one async socket operation.
static KSTATUS SyscallAsyncSocketPrepareOperation(PROCESS* proc,
const SYS_ASYNC_SOCKET_REQUEST_K* req,
NET_ASYNC_SOCKET_SUBMIT* submit) {
if (!proc || !req || !submit)
return STATUS_INVALID_PARAMETER;
submit->Operation = req->Operation;
submit->SocketFlags = req->SocketFlags;
submit->Backlog = req->Backlog;
submit->ShutdownHow = req->ShutdownHow;
submit->Option = req->Option;
submit->PollEvents = req->PollEvents;
submit->Length = req->Length;
switch (req->Operation) {
case SYS_ASYNC_SOCKET_OP_BIND:
case SYS_ASYNC_SOCKET_OP_CONNECT:
return SyscallAsyncSocketCopyInputAddress(req, submit);
case SYS_ASYNC_SOCKET_OP_LISTEN:
case SYS_ASYNC_SOCKET_OP_SHUTDOWN:
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_ACCEPT: {
if (!req->HandleOut)
return STATUS_INVALID_PARAMETER;
uint32_t zero = 0;
KSTATUS status = MmLockUserBuffer(proc, req->HandleOut, sizeof(*req->HandleOut), 1,
&submit->LockedHandleOut);
if (!KSUCCESS(status))
return status;
*req->HandleOut = zero;
submit->HandleOut = req->HandleOut;
return SyscallAsyncSocketPrepareAddressOutput(proc, req, submit);
}
case SYS_ASYNC_SOCKET_OP_SEND:
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
if (req->Length) {
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'n'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length);
}
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_RECV:
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
if (req->Length) {
KSTATUS status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer);
if (!KSUCCESS(status))
return status;
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'n'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
}
submit->UserBuffer = req->Buffer;
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_SEND_TO: {
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
KSTATUS status = SyscallAsyncSocketCopyInputAddress(req, submit);
if (!KSUCCESS(status))
return status;
if (req->Length) {
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'd'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length);
}
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_RECV_FROM: {
if (req->Length && !req->Buffer)
return STATUS_INVALID_PARAMETER;
if (req->Length > SYSCALL_IO_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
KSTATUS status = SyscallAsyncSocketPrepareAddressOutput(proc, req, submit);
if (!KSUCCESS(status))
return status;
if (req->Length) {
status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer);
if (!KSUCCESS(status))
return status;
submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'd'));
if (!submit->KernelBuffer)
return STATUS_NO_MEMORY;
}
submit->UserBuffer = req->Buffer;
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_SET_OPT:
if (!req->OptionValue)
return STATUS_INVALID_PARAMETER;
if (req->OptionValueSize > sizeof(uint64_t))
return STATUS_BUFFER_TOO_SMALL;
submit->OptionValueSize = req->OptionValueSize;
if (req->OptionValueSize) {
submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 's'));
if (!submit->OptionValue)
return STATUS_NO_MEMORY;
return CopyFromUser(submit->OptionValue, req->OptionValue, req->OptionValueSize);
}
return STATUS_SUCCESS;
case SYS_ASYNC_SOCKET_OP_GET_OPT: {
if (!req->OptionValue)
return STATUS_INVALID_PARAMETER;
if (req->OptionValueSize > sizeof(uint64_t))
return STATUS_BUFFER_TOO_SMALL;
submit->OptionValueSize = req->OptionValueSize;
submit->UserOptionValue = req->OptionValue;
KSTATUS status = MmLockUserBuffer(proc, req->OptionValue, req->OptionValueSize, 1,
&submit->LockedOptionValue);
if (!KSUCCESS(status))
return status;
if (req->OptionActualSizeOut) {
status = MmLockUserBuffer(proc, req->OptionActualSizeOut, sizeof(*req->OptionActualSizeOut), 1,
&submit->LockedOptionActualSize);
if (!KSUCCESS(status))
return status;
submit->OptionActualSizeOut = req->OptionActualSizeOut;
}
if (req->OptionValueSize) {
submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 'g'));
if (!submit->OptionValue)
return STATUS_NO_MEMORY;
}
return STATUS_SUCCESS;
}
case SYS_ASYNC_SOCKET_OP_POLL: {
if (req->PollReturnedEventsOut) {
KSTATUS status = MmLockUserBuffer(proc, req->PollReturnedEventsOut,
sizeof(*req->PollReturnedEventsOut), 1,
&submit->LockedPollReturnedEvents);
if (!KSUCCESS(status))
return status;
*req->PollReturnedEventsOut = 0;
submit->PollReturnedEventsOut = req->PollReturnedEventsOut;
}
return STATUS_SUCCESS;
}
default:
return STATUS_INVALID_PARAMETER;
}
}
// SYS_SOCKET_IO_ASYNC a0=*SYS_ASYNC_SOCKET_REQUEST.
static uint64_t SyscallSocketIoAsync(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0)
return STATUS_INVALID_PARAMETER;
SYS_ASYNC_SOCKET_REQUEST_K req;
KSTATUS status = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req));
if (!KSUCCESS(status))
return status;
if (req.Size != sizeof(req) || req.Flags != SYS_ASYNC_SOCKET_FLAG_NONE || !req.Iosb)
return STATUS_INVALID_PARAMETER;
if (req.RequestHandleOut) {
status = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut));
if (!KSUCCESS(status))
return status;
}
PROCESS* proc = KeGetCurrentProcess();
if (!proc)
return STATUS_FAILURE;
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.SocketHandle,
ObjectTypeSocket,
HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE);
if (!socket)
return STATUS_NOT_FOUND;
EVENT* event = (EVENT*)0;
if (req.EventHandle) {
event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL);
if (!event) {
ObDereferenceObject(&socket->Header);
return STATUS_NOT_FOUND;
}
}
KAPC* completionApc = (KAPC*)0;
SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0;
if (req.ApcRoutine) {
THREAD* thread = KeGetCurrentThread();
if (!thread || !thread->UserApcDispatcher) {
if (event) ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return STATUS_INVALID_PARAMETER;
}
apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePoolTag(sizeof(SYS_USER_ASYNC_APC_CONTEXT), MM_POOL_TAG('u', 'a', 'p', 'c'));
if (!apcCtx) {
if (event) ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return STATUS_NO_MEMORY;
}
memset(apcCtx, 0, sizeof(*apcCtx));
ObReferenceObject(&thread->Header);
apcCtx->TargetThread = thread;
apcCtx->UserRoutine = req.ApcRoutine;
apcCtx->UserContext = req.ApcContext;
KeInitializeApc(&apcCtx->KernelApc,
thread,
SyscallUserAsyncKernelRoutine,
SyscallUserAsyncNormalRoutine,
apcCtx);
KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown);
completionApc = &apcCtx->KernelApc;
}
NET_ASYNC_SOCKET_SUBMIT submit;
memset(&submit, 0, sizeof(submit));
submit.OwnerProcess = proc;
submit.Socket = socket;
submit.CompletionEvent = event;
submit.CompletionApc = completionApc;
submit.Iosb = (IO_STATUS_BLOCK*)req.Iosb;
status = MmLockUserBuffer(proc, req.Iosb, sizeof(*req.Iosb), 1, &submit.LockedIosb);
if (!KSUCCESS(status))
goto fail;
status = SyscallAsyncSocketPrepareOperation(proc, &req, &submit);
if (!KSUCCESS(status))
goto fail;
NET_ASYNC_SOCKET_REQUEST_OBJECT* asyncReq = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
status = NetSocketSubmitAsyncKernel(&submit, &asyncReq);
if (status == STATUS_PENDING) {
memset(&submit, 0, sizeof(submit));
}
if (!KSUCCESS(status) && status != STATUS_PENDING)
goto fail;
if (req.RequestHandleOut && asyncReq) {
uint32_t requestHandle = 0;
KSTATUS hs = KeAllocateHandleWithRights(proc,
NetAsyncSocketRequestObject(asyncReq),
HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE,
&requestHandle);
if (!KSUCCESS(hs)) {
(void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq);
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
status = hs;
goto fail_after_submit;
}
KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle));
if (!KSUCCESS(cs)) {
(void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq);
KeCloseHandle(proc, requestHandle);
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
status = cs;
goto fail_after_submit;
}
} else if (req.RequestHandleOut) {
uint32_t zeroHandle = 0;
KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle));
if (!KSUCCESS(cs)) {
status = cs;
goto fail_after_submit;
}
}
if (asyncReq)
ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq));
if (event)
ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return status;
fail:
SyscallAsyncSocketReleaseSubmitResources(&submit);
if (apcCtx)
SyscallUserAsyncApcContextFree(apcCtx);
fail_after_submit:
if (event)
ObDereferenceObject(&event->Header);
ObDereferenceObject(&socket->Header);
return status;
}
// SYS_GET_MEMORY_STATUS a0=*SYS_MEMORY_STATUS.
static uint64_t SyscallGetMemoryStatus(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
@@ -4332,6 +4992,29 @@ static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2,
return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um));
}
case SYS_KERNEL_STATS_NET_IRP: {
NET_IRP_STATS km;
SYS_NET_IRP_STATS um;
NetQueryIrpStats(&km);
memset(&um, 0, sizeof(um));
um.Size = sizeof(um);
um.Version = SYS_STATS_VERSION;
um.Submitted = km.Submitted;
um.Completed = km.Completed;
um.CompletedSynchronously = km.CompletedSynchronously;
um.CompletedAsynchronously = km.CompletedAsynchronously;
um.Pending = km.Pending;
um.CancelRequested = km.CancelRequested;
um.Cancelled = km.Cancelled;
um.WouldBlock = km.WouldBlock;
um.Timeout = km.Timeout;
um.Failed = km.Failed;
for (uint32_t i = 0; i < SYS_NET_IRP_OPERATION_COUNT && i < NetSocketIrpOperationCount; ++i)
um.ByOperation[i] = km.ByOperation[i];
return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um));
}
case SYS_KERNEL_STATS_CPU: {
struct {
SYS_CPU_STATS Header;
@@ -4381,7 +5064,7 @@ typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
// Process, threads, waits, and APC.
// Process, threads, modules, waits, and APC.
[SYS_EXIT_PROCESS] = SyscallExitProcess,
[SYS_EXIT_THREAD] = SyscallExitThread,
[SYS_CREATE_PROCESS] = SyscallCreateProcess,
@@ -4392,6 +5075,11 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
[SYS_GET_PROCESS_PID] = SyscallGetProcessPid,
[SYS_CREATE_THREAD] = SyscallCreateThread,
[SYS_GET_THREAD_LIST] = SyscallGetThreadList,
[SYS_LOAD_MODULE] = SyscallLoadModule,
[SYS_UNLOAD_MODULE] = SyscallUnloadModule,
[SYS_GET_MODULE_INFO] = SyscallGetModuleInfo,
[SYS_GET_MODULE_EXPORT] = SyscallGetModuleExport,
[SYS_GET_MODULE_LIST] = SyscallGetModuleList,
[SYS_GET_CURRENT_TID] = SyscallGetCurrentTid,
[SYS_GET_CURRENT_PID] = SyscallGetCurrentPid,
[SYS_SET_THREAD_PRIORITY] = SyscallSetThreadPriority,
@@ -4498,6 +5186,7 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
// Native sockets and polling.
[SYS_CREATE_SOCKET] = SyscallSocket,
[SYS_SOCKET_IO_ASYNC] = SyscallSocketIoAsync,
[SYS_BIND_SOCKET] = SyscallBind,
[SYS_LISTEN_SOCKET] = SyscallListen,
[SYS_ACCEPT_SOCKET] = SyscallAccept,
+185 -19
View File
@@ -10,6 +10,7 @@
#include <kernel/mm_virt.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_system_root.h>
#include <ntlite/syscall.h>
#ifndef LDR_DEBUG_VERBOSE
#define LDR_DEBUG_VERBOSE 0
@@ -326,6 +327,24 @@ void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const cha
return KiGetExportAtBase(base, functionName);
}
// Looks up an export in a specific process module mapping.
void* LdrGetModuleExportForProcess(PROCESS* process, uintptr_t imageBase, const char* functionName) {
if (!process || !imageBase || !functionName) return (void*)0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
int found = 0;
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (process->Modules[i].Base == imageBase) {
found = 1;
break;
}
}
KeReleaseSpinlock(&process->ModuleLock, flags);
if (!found) return (void*)0;
return KiGetExportAtBase(imageBase, functionName);
}
// Implements the ldr Get Image Size operation.
size_t LdrGetImageSize(uintptr_t base) {
if (!base) return 0;
@@ -559,7 +578,7 @@ static KSTATUS LdrResolveImportsCommon(
if (!modBase) {
KSTATUS s =
LdrLoadModuleForProcess(process, dllName, &modBase);
LdrLoadModuleForProcess(process, dllName, LDR_MODULE_LOAD_IMPORT, &modBase);
if (!KSUCCESS(s)) {
DbgPrint(
@@ -674,38 +693,101 @@ KSTATUS LdrLoadImageFileForProcess(PROCESS* process, FILE_OBJECT* file, uintptr_
return STATUS_SUCCESS;
}
// Implements the ldr Register Process Module operation.
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) {
if (!process || !name || !base) return STATUS_INVALID_PARAMETER;
// Converts a process module entry into the public module ABI.
static void KiFillProcessModuleInfo(const PROCESS_MODULE* mod, SYS_MODULE_INFO* info) {
memset(info, 0, sizeof(*info));
info->Size = sizeof(*info);
info->Version = SYS_MODULE_INFO_VERSION;
info->Flags = mod->Flags;
if (mod->PublicRefCount)
info->Flags |= SYS_MODULE_FLAG_PUBLIC_REF;
if (mod->ImportRefCount)
info->Flags |= SYS_MODULE_FLAG_IMPORT_PINNED;
if (mod->PublicRefCount && mod->ImportRefCount == 0)
info->Flags |= SYS_MODULE_FLAG_UNLOADABLE;
info->Base = mod->Base;
info->ImageSize = mod->Size;
info->EntryPoint = mod->EntryPoint;
info->PublicRefCount = mod->PublicRefCount;
info->ImportRefCount = mod->ImportRefCount;
info->TotalRefCount = mod->PublicRefCount + mod->ImportRefCount;
strncpy(info->Name, mod->Name, sizeof(info->Name) - 1);
info->Name[sizeof(info->Name) - 1] = '\0';
}
// Adds one reference of the requested class to an existing process module.
static void KiReferenceProcessModule(PROCESS_MODULE* mod, uint32_t loadFlags) {
if (loadFlags & LDR_MODULE_LOAD_PUBLIC)
mod->PublicRefCount++;
if (loadFlags & LDR_MODULE_LOAD_IMPORT)
mod->ImportRefCount++;
}
// Finds a process module by canonical name while the caller owns ModuleLock.
static PROCESS_MODULE* KiFindProcessModuleByNameLocked(PROCESS* process, const char* name) {
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (_stricmp(process->Modules[i].Name, name) == 0) {
return STATUS_SUCCESS;
}
if (_stricmp(process->Modules[i].Name, name) == 0)
return &process->Modules[i];
}
return (PROCESS_MODULE*)0;
}
// Finds a process module by image base while the caller owns ModuleLock.
static PROCESS_MODULE* KiFindProcessModuleByBaseLocked(PROCESS* process, uintptr_t base) {
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (process->Modules[i].Base == base)
return &process->Modules[i];
}
return (PROCESS_MODULE*)0;
}
// Registers a mapped process module or records a reference to an existing one.
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base, uint32_t loadFlags, uintptr_t* registeredBaseOut) {
if (!process || !name || !base || !loadFlags) return STATUS_INVALID_PARAMETER;
if (registeredBaseOut) *registeredBaseOut = 0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, name);
if (existing) {
KiReferenceProcessModule(existing, loadFlags);
if (registeredBaseOut) *registeredBaseOut = existing->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_ALREADY_EXISTS;
}
if (process->ModuleCount >= PROCESS_MODULE_MAX) return STATUS_NO_MEMORY;
if (process->ModuleCount >= PROCESS_MODULE_MAX) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NO_MEMORY;
}
PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++];
strncpy(mod->Name, name, 31);
mod->Name[31] = '\0';
memset(mod, 0, sizeof(*mod));
strncpy(mod->Name, name, sizeof(mod->Name) - 1);
mod->Name[sizeof(mod->Name) - 1] = '\0';
mod->Base = base;
mod->Size = LdrGetMappedImageSize(process, base);
mod->EntryPoint = LdrGetEntryPoint(base);
KiReferenceProcessModule(mod, loadFlags);
if (registeredBaseOut) *registeredBaseOut = mod->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
// Implements the ldr Load Module For Process operation.
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) {
if (!process || !filename) return STATUS_INVALID_PARAMETER;
// Loads a DLL-like user module into a process and records the requested reference.
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uint32_t loadFlags, uintptr_t* baseOut) {
if (!process || !filename || !loadFlags) return STATUS_INVALID_PARAMETER;
filename = KiCanonicalModuleName(filename);
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (_stricmp(process->Modules[i].Name, filename) == 0) {
if (baseOut) *baseOut = process->Modules[i].Base;
return STATUS_SUCCESS;
}
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, filename);
if (existing) {
KiReferenceProcessModule(existing, loadFlags);
if (baseOut) *baseOut = existing->Base;
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
KeReleaseSpinlock(&process->ModuleLock, flags);
FILE_OBJECT* f;
KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
@@ -750,7 +832,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return s;
}
s = LdrRegisterProcessModule(process, filename, imageBase);
uintptr_t registeredBase = 0;
s = LdrRegisterProcessModule(process, filename, imageBase, loadFlags, &registeredBase);
if (s == STATUS_ALREADY_EXISTS) {
KiFreeLoadedImageForProcess(process, imageBase, true);
imageBase = registeredBase;
s = STATUS_SUCCESS;
}
if (!KSUCCESS(s)) {
KiFreeLoadedImageForProcess(process, imageBase, true);
return s;
@@ -762,6 +850,84 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return STATUS_SUCCESS;
}
// Releases one public module load reference and unmaps the image when no references remain.
KSTATUS LdrUnloadModuleForProcess(PROCESS* process, uintptr_t imageBase) {
if (!process || !imageBase) return STATUS_INVALID_PARAMETER;
char name[PROCESS_MODULE_NAME_MAX];
uintptr_t base = 0;
int shouldUnmap = 0;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
if (!mod) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NOT_FOUND;
}
if (mod->PublicRefCount == 0) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_INVALID_PARAMETER;
}
mod->PublicRefCount--;
if (mod->PublicRefCount == 0 && mod->ImportRefCount == 0) {
base = mod->Base;
strncpy(name, mod->Name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
uint32_t index = (uint32_t)(mod - process->Modules);
if (index + 1 < process->ModuleCount) {
memmove(&process->Modules[index],
&process->Modules[index + 1],
(process->ModuleCount - index - 1) * sizeof(process->Modules[0]));
}
process->ModuleCount--;
shouldUnmap = 1;
}
KeReleaseSpinlock(&process->ModuleLock, flags);
if (shouldUnmap) {
LdrDbg("LDR: unload module pid=%u name=%s base=%016llx\n",
process->ProcessId, name, (unsigned long long)base);
KiFreeLoadedImageForProcess(process, base, true);
}
return STATUS_SUCCESS;
}
// Queries one loaded process module by image base.
KSTATUS LdrQueryModuleForProcess(PROCESS* process, uintptr_t imageBase, SYS_MODULE_INFO* infoOut) {
if (!process || !imageBase || !infoOut) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
if (!mod) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_NOT_FOUND;
}
KiFillProcessModuleInfo(mod, infoOut);
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_SUCCESS;
}
// Copies a bounded snapshot of the process module table.
KSTATUS LdrCopyProcessModuleInfo(PROCESS* process, SYS_MODULE_INFO* infoOut, size_t maxCount, size_t* countOut) {
if (!process || !countOut) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
size_t count = process->ModuleCount;
*countOut = count;
size_t toCopy = count;
if (toCopy > maxCount) toCopy = maxCount;
if (toCopy && !infoOut) {
KeReleaseSpinlock(&process->ModuleLock, flags);
return STATUS_INVALID_PARAMETER;
}
for (size_t i = 0; i < toCopy; i++)
KiFillProcessModuleInfo(&process->Modules[i], &infoOut[i]);
KeReleaseSpinlock(&process->ModuleLock, flags);
return (maxCount < count) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
// Loads a boot/kernel PE image from a raw memory buffer into kernel space.
static KSTATUS KiLdrLoadKernelImageFromBuffer(const void* buffer, size_t size, uintptr_t* baseOut) {
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
+761 -36
View File
@@ -2,25 +2,591 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <kernel/net_socket.h>
#include <ntlite/ddi/net_stack.h>
#include <kernel/ke_event.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_process.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ke_thread.h>
#include <kernel/mm_virt.h>
#include <kernel/ob_object.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/net_stack.h>
#include <ntlite/syscall.h>
#define NET_SOCKET_IO_MAX_BYTES (1024u * 1024u)
#define NET_SOCKET_UDP_MAX_BYTES 65507u
#define NET_SOCKET_RAW_MAX_BYTES 65535u
// Implements the net Socket Destroy operation.
static SPINLOCK gNetIrpStatsLock = SPINLOCK_INIT;
static NET_IRP_STATS gNetIrpStats;
// Records that a socket operation entered the IRP boundary.
static void NetIrpStatsSubmit(uint32_t operation) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Submitted++;
if (operation < NetSocketIrpOperationCount)
gNetIrpStats.ByOperation[operation]++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records that a submitted socket IRP returned pending to the socket facade.
static void NetIrpStatsPending(void) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Pending++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records the final status of a socket IRP after synchronous wait completion.
static void NetIrpStatsComplete(KSTATUS status, bool asynchronous) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.Completed++;
if (asynchronous)
gNetIrpStats.CompletedAsynchronously++;
else
gNetIrpStats.CompletedSynchronously++;
if (status == STATUS_CANCELLED)
gNetIrpStats.Cancelled++;
else if (status == STATUS_WOULD_BLOCK)
gNetIrpStats.WouldBlock++;
else if (status == STATUS_TIMEOUT)
gNetIrpStats.Timeout++;
else if (KFAILED(status))
gNetIrpStats.Failed++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Returns a stable snapshot of socket IRP diagnostics.
__declspec(dllexport) void NetQueryIrpStats(NET_IRP_STATS* out) {
if (!out)
return;
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
*out = gNetIrpStats;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
// Records that a public async socket request requested cancellation.
static void NetIrpStatsCancelRequested(void) {
uintptr_t flags = KeAcquireSpinlock(&gNetIrpStatsLock);
gNetIrpStats.CancelRequested++;
KeReleaseSpinlock(&gNetIrpStatsLock, flags);
}
struct NET_ASYNC_SOCKET_REQUEST_OBJECT {
IO_ASYNC_REQUEST_OBJECT Async;
PROCESS* OwnerProcess;
SOCKET_OBJECT* Socket;
uint32_t Operation;
uint32_t SocketFlags;
uint32_t Backlog;
uint32_t ShutdownHow;
uint32_t Option;
uint32_t PollEvents;
void* KernelBuffer;
void* UserBuffer;
size_t Length;
SYS_SOCKET_ADDRESS_K Address;
size_t AddressSize;
SYS_SOCKET_ADDRESS_K* AddressOut;
size_t* AddressSizeInOut;
void* OptionValue;
void* UserOptionValue;
size_t OptionValueSize;
size_t* OptionActualSizeOut;
uint32_t* HandleOut;
uint32_t* PollReturnedEventsOut;
MM_LOCKED_USER_BUFFER* LockedAddressOut;
MM_LOCKED_USER_BUFFER* LockedAddressSize;
MM_LOCKED_USER_BUFFER* LockedOptionValue;
MM_LOCKED_USER_BUFFER* LockedOptionActualSize;
MM_LOCKED_USER_BUFFER* LockedHandleOut;
MM_LOCKED_USER_BUFFER* LockedPollReturnedEvents;
};
static SPINLOCK gAsyncSocketInitLock = SPINLOCK_INIT;
static int gAsyncSocketInitState;
static SPINLOCK gAsyncSocketQueueLock = SPINLOCK_INIT;
static LIST_ENTRY gAsyncSocketQueue;
static SEMAPHORE* gAsyncSocketSemaphore;
// Releases operation-specific user locks that the common async object does not own.
static void NetAsyncSocketReleaseExtraLocks(NET_ASYNC_SOCKET_REQUEST_OBJECT* req) {
if (!req)
return;
if (req->LockedAddressOut) {
MmUnlockUserBuffer(req->LockedAddressOut);
req->LockedAddressOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedAddressSize) {
MmUnlockUserBuffer(req->LockedAddressSize);
req->LockedAddressSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedOptionValue) {
MmUnlockUserBuffer(req->LockedOptionValue);
req->LockedOptionValue = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedOptionActualSize) {
MmUnlockUserBuffer(req->LockedOptionActualSize);
req->LockedOptionActualSize = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedHandleOut) {
MmUnlockUserBuffer(req->LockedHandleOut);
req->LockedHandleOut = (MM_LOCKED_USER_BUFFER*)0;
}
if (req->LockedPollReturnedEvents) {
MmUnlockUserBuffer(req->LockedPollReturnedEvents);
req->LockedPollReturnedEvents = (MM_LOCKED_USER_BUFFER*)0;
}
}
// Releases heap storage no longer needed after a socket operation has finished.
static void NetAsyncSocketReleaseKernelStorage(NET_ASYNC_SOCKET_REQUEST_OBJECT* req) {
if (!req)
return;
if (req->KernelBuffer) {
MmFreePool(req->KernelBuffer);
req->KernelBuffer = (void*)0;
}
if (req->OptionValue) {
MmFreePool(req->OptionValue);
req->OptionValue = (void*)0;
}
}
// Releases socket-specific references owned by an async socket request.
static void NetAsyncSocketRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
NET_ASYNC_SOCKET_REQUEST_OBJECT* req =
(NET_ASYNC_SOCKET_REQUEST_OBJECT*)async;
NetAsyncSocketReleaseExtraLocks(req);
NetAsyncSocketReleaseKernelStorage(req);
if (req->Socket) {
ObDereferenceObject(&req->Socket->Header);
req->Socket = (SOCKET_OBJECT*)0;
}
if (req->OwnerProcess) {
ObDereferenceObject(&req->OwnerProcess->Header);
req->OwnerProcess = (PROCESS*)0;
}
}
// Cancels an async socket request from the shared async I/O handle.
static KSTATUS NetCancelAsyncSocketRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
(void)context;
if (!async)
return STATUS_INVALID_PARAMETER;
NetIrpStatsCancelRequested();
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
if (state == IoAsyncRequestQueued) {
ListRemove(&async->Link);
IoAsyncRequestRequestCancel(async);
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
ObDereferenceObject(&async->Header);
return STATUS_SUCCESS;
}
if (state == IoAsyncRequestRunning) {
IoAsyncRequestRequestCancel(async);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return STATUS_PENDING;
}
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return STATUS_OBJECT_DEAD;
}
// Removes one queued async socket request for the worker.
static NET_ASYNC_SOCKET_REQUEST_OBJECT* NetAsyncSocketDequeue(void) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
LIST_ENTRY* node = ListPopHead(&gAsyncSocketQueue);
if (!node) {
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
}
NET_ASYNC_SOCKET_REQUEST_OBJECT* req =
(NET_ASYNC_SOCKET_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
if (IoAsyncRequestIsCancelRequested(&req->Async))
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
else
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
KeReleaseSpinlock(&gAsyncSocketQueueLock, flags);
return req;
}
// Publishes a remote address output for accept and recvfrom.
static size_t NetAsyncSocketPublishAddress(NET_ASYNC_SOCKET_REQUEST_OBJECT* req,
const SYS_SOCKET_ADDRESS_K* address) {
if (!req || !req->AddressOut || !address)
return 0;
memcpy(req->AddressOut, address, sizeof(*address));
if (req->AddressSizeInOut)
*req->AddressSizeInOut = sizeof(*address);
return sizeof(*address);
}
// Executes one queued async socket operation on the worker thread.
static void NetAsyncSocketExecute(NET_ASYNC_SOCKET_REQUEST_OBJECT* req,
KSTATUS* statusOut,
size_t* informationOut) {
KSTATUS status = STATUS_INVALID_PARAMETER;
size_t information = 0;
if (!req || !statusOut || !informationOut)
return;
switch (req->Operation) {
case SYS_ASYNC_SOCKET_OP_BIND:
status = NetSocketBind(req->Socket, &req->Address, req->AddressSize);
break;
case SYS_ASYNC_SOCKET_OP_LISTEN:
status = NetSocketListen(req->Socket, req->Backlog);
break;
case SYS_ASYNC_SOCKET_OP_ACCEPT: {
SOCKET_OBJECT* accepted = (SOCKET_OBJECT*)0;
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
status = NetSocketAccept(req->Socket, &accepted, req->AddressOut ? &remote : (SYS_SOCKET_ADDRESS_K*)0, &req->Async);
if (KSUCCESS(status)) {
uint32_t handle = 0;
status = KeAllocateHandleWithRights(req->OwnerProcess,
&accepted->Header,
HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY,
&handle);
ObDereferenceObject(&accepted->Header);
if (KSUCCESS(status)) {
if (req->HandleOut)
*req->HandleOut = handle;
information = handle;
(void)NetAsyncSocketPublishAddress(req, &remote);
}
}
break;
}
case SYS_ASYNC_SOCKET_OP_CONNECT:
status = NetSocketConnect(req->Socket, &req->Address, req->AddressSize, &req->Async);
break;
case SYS_ASYNC_SOCKET_OP_SEND: {
size_t sent = 0;
status = NetSocketSend(req->Socket, req->KernelBuffer, req->Length, req->SocketFlags, &sent);
information = sent;
break;
}
case SYS_ASYNC_SOCKET_OP_RECV: {
size_t received = 0;
status = NetSocketReceive(req->Socket, req->KernelBuffer, req->Length, req->SocketFlags, &received, &req->Async);
if (KSUCCESS(status) && received)
memcpy(req->UserBuffer, req->KernelBuffer, received);
information = received;
break;
}
case SYS_ASYNC_SOCKET_OP_SEND_TO: {
size_t sent = 0;
status = NetSocketSendTo(req->Socket, req->KernelBuffer, req->Length,
req->SocketFlags, &req->Address, req->AddressSize, &sent);
information = sent;
break;
}
case SYS_ASYNC_SOCKET_OP_RECV_FROM: {
size_t received = 0;
SYS_SOCKET_ADDRESS_K remote;
memset(&remote, 0, sizeof(remote));
status = NetSocketReceiveFrom(req->Socket, req->KernelBuffer, req->Length,
req->SocketFlags,
req->AddressOut ? &remote : (SYS_SOCKET_ADDRESS_K*)0,
&received,
&req->Async);
if (KSUCCESS(status) && received)
memcpy(req->UserBuffer, req->KernelBuffer, received);
if (KSUCCESS(status))
(void)NetAsyncSocketPublishAddress(req, &remote);
information = received;
break;
}
case SYS_ASYNC_SOCKET_OP_SHUTDOWN:
status = NetSocketShutdown(req->Socket, req->ShutdownHow);
break;
case SYS_ASYNC_SOCKET_OP_SET_OPT:
status = NetSocketSetOption(req->Socket, req->Option, req->OptionValue, req->OptionValueSize);
break;
case SYS_ASYNC_SOCKET_OP_GET_OPT: {
size_t actual = 0;
status = NetSocketGetOption(req->Socket, req->Option, req->OptionValue, req->OptionValueSize, &actual);
if (KSUCCESS(status) && actual && req->UserOptionValue)
memcpy(req->UserOptionValue, req->OptionValue, actual);
if ((KSUCCESS(status) || status == STATUS_BUFFER_TOO_SMALL) && req->OptionActualSizeOut)
*req->OptionActualSizeOut = actual;
information = actual;
break;
}
case SYS_ASYNC_SOCKET_OP_POLL: {
uint32_t returnedEvents = 0;
status = NetSocketPoll(req->Socket, req->PollEvents, &returnedEvents);
if (req->PollReturnedEventsOut)
*req->PollReturnedEventsOut = returnedEvents;
information = returnedEvents;
break;
}
default:
status = STATUS_INVALID_PARAMETER;
break;
}
*statusOut = status;
*informationOut = information;
}
// Worker for public async socket requests.
static void NetAsyncSocketWorker(void* arg) {
(void)arg;
for (;;) {
KSTATUS waitStatus = KeWaitSemaphore(gAsyncSocketSemaphore);
if (!KSUCCESS(waitStatus)) {
KeYield();
continue;
}
NET_ASYNC_SOCKET_REQUEST_OBJECT* req = NetAsyncSocketDequeue();
if (!req)
continue;
KSTATUS status = STATUS_CANCELLED;
size_t information = 0;
if (!IoAsyncRequestIsCancelRequested(&req->Async) &&
IoAsyncRequestGetState(&req->Async) != IoAsyncRequestCancelled) {
NetAsyncSocketExecute(req, &status, &information);
}
NetAsyncSocketReleaseExtraLocks(req);
NetAsyncSocketReleaseKernelStorage(req);
IoCompleteAsyncRequest(&req->Async, status, information);
ObDereferenceObject(&req->Async.Header);
}
}
// Starts the async socket worker on first use.
static KSTATUS NetEnsureAsyncSocketWorkerStarted(void) {
uintptr_t flags = KeAcquireSpinlock(&gAsyncSocketInitLock);
if (gAsyncSocketInitState == 2) {
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
return STATUS_SUCCESS;
}
if (gAsyncSocketInitState == 1) {
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
while (gAsyncSocketInitState == 1)
KeYield();
return (gAsyncSocketInitState == 2) ? STATUS_SUCCESS : STATUS_FAILURE;
}
gAsyncSocketInitState = 1;
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
LIST_INIT(&gAsyncSocketQueue);
KSTATUS status = KeSemaphoreCreate(0, 0x7fffffff, &gAsyncSocketSemaphore);
if (KSUCCESS(status)) {
THREAD* worker = (THREAD*)0;
status = KeCreateKernelThread((uintptr_t)NetAsyncSocketWorker,
0,
PriorityNormal,
&worker);
(void)worker;
}
flags = KeAcquireSpinlock(&gAsyncSocketInitLock);
gAsyncSocketInitState = KSUCCESS(status) ? 2 : 0;
KeReleaseSpinlock(&gAsyncSocketInitLock, flags);
return status;
}
// Submits a public async socket request to the socket worker.
KSTATUS NetSocketSubmitAsyncKernel(const NET_ASYNC_SOCKET_SUBMIT* submit,
NET_ASYNC_SOCKET_REQUEST_OBJECT** requestOut) {
if (requestOut)
*requestOut = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0;
if (!submit || !submit->OwnerProcess || !submit->Socket || !submit->Iosb)
return STATUS_INVALID_PARAMETER;
KSTATUS status = NetEnsureAsyncSocketWorkerStarted();
if (!KSUCCESS(status))
return status;
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
status = IoCreateAsyncRequest(sizeof(NET_ASYNC_SOCKET_REQUEST_OBJECT),
NetAsyncSocketRequestCleanup,
(void*)0,
NetCancelAsyncSocketRequest,
(void*)0,
&async);
if (!KSUCCESS(status))
return status;
NET_ASYNC_SOCKET_REQUEST_OBJECT* req = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)async;
req->OwnerProcess = submit->OwnerProcess;
req->Socket = submit->Socket;
req->Operation = submit->Operation;
req->SocketFlags = submit->SocketFlags;
req->Backlog = submit->Backlog;
req->ShutdownHow = submit->ShutdownHow;
req->Option = submit->Option;
req->PollEvents = submit->PollEvents;
req->KernelBuffer = submit->KernelBuffer;
req->UserBuffer = submit->UserBuffer;
req->Length = submit->Length;
req->Address = submit->Address;
req->AddressSize = submit->AddressSize;
req->AddressOut = submit->AddressOut;
req->AddressSizeInOut = submit->AddressSizeInOut;
req->OptionValue = submit->OptionValue;
req->UserOptionValue = submit->UserOptionValue;
req->OptionValueSize = submit->OptionValueSize;
req->OptionActualSizeOut = submit->OptionActualSizeOut;
req->HandleOut = submit->HandleOut;
req->PollReturnedEventsOut = submit->PollReturnedEventsOut;
req->LockedAddressOut = submit->LockedAddressOut;
req->LockedAddressSize = submit->LockedAddressSize;
req->LockedOptionValue = submit->LockedOptionValue;
req->LockedOptionActualSize = submit->LockedOptionActualSize;
req->LockedHandleOut = submit->LockedHandleOut;
req->LockedPollReturnedEvents = submit->LockedPollReturnedEvents;
ObReferenceObject(&req->OwnerProcess->Header);
ObReferenceObject(&req->Socket->Header);
IoInitializeAsyncRequestCompletion(&req->Async,
submit->Iosb,
submit->CompletionEvent,
submit->CompletionApc,
submit->LockedBuffer,
submit->LockedIosb);
ObReferenceObject(&req->Async.Header);
uintptr_t qflags = KeAcquireSpinlock(&gAsyncSocketQueueLock);
ListInsertTail(&gAsyncSocketQueue, &req->Async.Link);
KeReleaseSpinlock(&gAsyncSocketQueueLock, qflags);
KeSignalSemaphore(gAsyncSocketSemaphore);
if (requestOut)
*requestOut = req;
else
ObDereferenceObject(&req->Async.Header);
return STATUS_PENDING;
}
// Returns the object header embedded in a socket async request.
OBJECT* NetAsyncSocketRequestObject(NET_ASYNC_SOCKET_REQUEST_OBJECT* request) {
return request ? &request->Async.Header : (OBJECT*)0;
}
// Submits a socket provider request through the active stack DEVICE_OBJECT.
static KSTATUS NetSubmitSocketIrp(const NET_STACK_PROVIDER* provider,
NET_SOCKET_IRP_REQUEST* request,
IO_ASYNC_REQUEST_OBJECT* async) {
if (!provider || !provider->StackDevice || !request)
return STATUS_INVALID_PARAMETER;
if (request->Size != sizeof(*request))
return STATUS_INVALID_PARAMETER;
if (request->Operation == NetSocketIrpInvalid ||
request->Operation >= NetSocketIrpOperationCount)
return STATUS_INVALID_PARAMETER;
request->CancelRequested = async ? &async->CancelRequested : (volatile uint8_t*)0;
EVENT* event = NULL;
KSTATUS st = KeEventCreate(EventAutoReset, 0, &event);
if (!KSUCCESS(st))
return st;
IRP irp;
IoInitializeIrp(&irp, 1);
IoBuildDeviceIoControlIrp(&irp,
IRP_MJ_INTERNAL_DEVICE_CONTROL,
NET_IOCTL_SOCKET,
request,
sizeof(*request),
NULL,
0);
irp.CompletionEvent = event;
NetIrpStatsSubmit(request->Operation);
st = IoSubmitIrp(provider->StackDevice, &irp);
bool asynchronous = false;
if (st == STATUS_PENDING) {
asynchronous = true;
NetIrpStatsPending();
(void)KeWaitForEvent(event, 0);
st = irp.Status;
}
NetIrpStatsComplete(st, asynchronous);
ObDereferenceObject(&event->Header);
return st;
}
// Closes a provider socket through the IRP path while object teardown is still safe.
static void NetSocketCloseProvider(const NET_STACK_PROVIDER* provider, void* providerSocket) {
if (!provider || !providerSocket)
return;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpClose;
request.u.Close.Socket = providerSocket;
(void)NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Releases provider state owned by a process socket object.
static void NetSocketDestroy(OBJECT* object) {
SOCKET_OBJECT* socket = (SOCKET_OBJECT*)object;
if (socket->Provider && socket->ProviderSocket) {
socket->Provider->SocketClose(socket->ProviderSocket);
NetSocketCloseProvider(socket->Provider, socket->ProviderSocket);
socket->ProviderSocket = NULL;
}
}
// Allocates a process-visible socket object around provider-owned state.
static KSTATUS NetSocketAllocate(const NET_STACK_PROVIDER* provider,
void* providerSocket,
uint32_t domain,
@@ -44,6 +610,7 @@ static KSTATUS NetSocketAllocate(const NET_STACK_PROVIDER* provider,
return STATUS_SUCCESS;
}
// Returns the provider and provider-private state behind a live socket object.
static KSTATUS NetSocketGetProviderSocket(SOCKET_OBJECT* socket,
const NET_STACK_PROVIDER** providerOut,
void** providerSocketOut) {
@@ -57,7 +624,7 @@ static KSTATUS NetSocketGetProviderSocket(SOCKET_OBJECT* socket,
return STATUS_SUCCESS;
}
// Implements the net Socket Create operation.
// Creates a socket by submitting a create request to the active stack device.
KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKET_OBJECT** out) {
if (!out)
return STATUS_INVALID_PARAMETER;
@@ -68,7 +635,16 @@ KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKE
return STATUS_NOT_SUPPORTED;
void* providerSocket = NULL;
KSTATUS st = provider->SocketCreate(domain, type, protocol, &providerSocket);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpCreate;
request.u.Create.Domain = domain;
request.u.Create.Type = type;
request.u.Create.Protocol = protocol;
request.u.Create.ProviderSocketOut = &providerSocket;
KSTATUS st = NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
if (!KSUCCESS(st))
return st;
if (!providerSocket)
@@ -76,28 +652,49 @@ KSTATUS NetSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, SOCKE
st = NetSocketAllocate(provider, providerSocket, domain, type, protocol, out);
if (!KSUCCESS(st))
provider->SocketClose(providerSocket);
NetSocketCloseProvider(provider, providerSocket);
return st;
}
// Implements the net Socket Bind operation.
// Binds a socket address through the stack device IRP path.
KSTATUS NetSocketBind(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Bind(providerSocket, address, addressSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpBind;
request.u.Bind.Socket = providerSocket;
request.u.Bind.Address = address;
request.u.Bind.AddressSize = addressSize;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Listen operation.
// Marks a stream socket as listening through the stack device IRP path.
KSTATUS NetSocketListen(SOCKET_OBJECT* socket, uint32_t backlog) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Listen(providerSocket, backlog) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpListen;
request.u.Listen.Socket = providerSocket;
request.u.Listen.Backlog = backlog;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Accept operation.
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
// Accepts a provider socket through IRP and wraps the result in a process socket object.
KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut,
SYS_SOCKET_ADDRESS_K* remoteOut,
IO_ASYNC_REQUEST_OBJECT* async) {
if (!acceptedOut)
return STATUS_INVALID_PARAMETER;
*acceptedOut = NULL;
@@ -109,7 +706,15 @@ KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_
return st;
void* acceptedProviderSocket = NULL;
st = provider->Accept(providerSocket, &acceptedProviderSocket, remoteOut);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpAccept;
request.u.Accept.Socket = providerSocket;
request.u.Accept.AcceptedOut = &acceptedProviderSocket;
request.u.Accept.RemoteOut = remoteOut;
st = NetSubmitSocketIrp(provider, &request, async);
if (!KSUCCESS(st))
return st;
if (!acceptedProviderSocket)
@@ -122,19 +727,32 @@ KSTATUS NetSocketAccept(SOCKET_OBJECT* socket, SOCKET_OBJECT** acceptedOut, SYS_
socket->Protocol,
acceptedOut);
if (!KSUCCESS(st))
provider->SocketClose(acceptedProviderSocket);
NetSocketCloseProvider(provider, acceptedProviderSocket);
return st;
}
// Implements the net Socket Connect operation.
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
// Connects a socket through the stack device IRP path.
KSTATUS NetSocketConnect(SOCKET_OBJECT* socket, const SYS_SOCKET_ADDRESS_K* address,
size_t addressSize, IO_ASYNC_REQUEST_OBJECT* async) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Connect(providerSocket, address, addressSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpConnect;
request.u.Connect.Socket = providerSocket;
request.u.Connect.Address = address;
request.u.Connect.AddressSize = addressSize;
return NetSubmitSocketIrp(provider, &request, async);
}
// Implements the net Socket Send operation.
// Sends stream bytes through the stack device IRP path.
KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut) {
if (sentOut)
*sentOut = 0;
@@ -146,11 +764,25 @@ KSTATUS NetSocketSend(SOCKET_OBJECT* socket, const void* buffer, size_t length,
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Send(providerSocket, buffer, length, flags, sentOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSend;
request.u.Send.Socket = providerSocket;
request.u.Send.Buffer = buffer;
request.u.Send.Length = length;
request.u.Send.Flags = flags;
request.u.Send.SentOut = sentOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Receive operation.
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut) {
// Receives stream bytes through the stack device IRP path.
KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length,
uint32_t flags, size_t* receivedOut,
IO_ASYNC_REQUEST_OBJECT* async) {
if (receivedOut)
*receivedOut = 0;
if (!buffer && length)
@@ -161,9 +793,23 @@ KSTATUS NetSocketReceive(SOCKET_OBJECT* socket, void* buffer, size_t length, uin
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Receive(providerSocket, buffer, length, flags, receivedOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpReceive;
request.u.Receive.Socket = providerSocket;
request.u.Receive.Buffer = buffer;
request.u.Receive.Length = length;
request.u.Receive.Flags = flags;
request.u.Receive.ReceivedOut = receivedOut;
return NetSubmitSocketIrp(provider, &request, async);
}
// Sends datagram bytes through the stack device IRP path.
KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length, uint32_t flags,
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
if (sentOut)
@@ -180,11 +826,27 @@ KSTATUS NetSocketSendTo(SOCKET_OBJECT* socket, const void* buffer, size_t length
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->SendTo(providerSocket, buffer, length, flags, remote, remoteSize, sentOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSendTo;
request.u.SendTo.Socket = providerSocket;
request.u.SendTo.Buffer = buffer;
request.u.SendTo.Length = length;
request.u.SendTo.Flags = flags;
request.u.SendTo.Remote = remote;
request.u.SendTo.RemoteSize = remoteSize;
request.u.SendTo.SentOut = sentOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
// Receives datagram bytes through the stack device IRP path.
KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length,
uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut,
size_t* receivedOut, IO_ASYNC_REQUEST_OBJECT* async) {
if (receivedOut)
*receivedOut = 0;
if (!buffer && length)
@@ -199,34 +861,80 @@ KSTATUS NetSocketReceiveFrom(SOCKET_OBJECT* socket, void* buffer, size_t length,
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->ReceiveFrom(providerSocket, buffer, length, flags, remoteOut, receivedOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpReceiveFrom;
request.u.ReceiveFrom.Socket = providerSocket;
request.u.ReceiveFrom.Buffer = buffer;
request.u.ReceiveFrom.Length = length;
request.u.ReceiveFrom.Flags = flags;
request.u.ReceiveFrom.RemoteOut = remoteOut;
request.u.ReceiveFrom.ReceivedOut = receivedOut;
return NetSubmitSocketIrp(provider, &request, async);
}
// Implements the net Socket Shutdown operation.
// Shuts down one or both socket directions through the stack device IRP path.
KSTATUS NetSocketShutdown(SOCKET_OBJECT* socket, uint32_t how) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Shutdown(providerSocket, how) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpShutdown;
request.u.Shutdown.Socket = providerSocket;
request.u.Shutdown.How = how;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Set Option operation.
// Sets a socket option through the stack device IRP path.
KSTATUS NetSocketSetOption(SOCKET_OBJECT* socket, uint32_t option, const void* value, size_t valueSize) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->SetOption(providerSocket, option, value, valueSize) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpSetOption;
request.u.SetOption.Socket = providerSocket;
request.u.SetOption.Option = option;
request.u.SetOption.Value = value;
request.u.SetOption.ValueSize = valueSize;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Get Option operation.
// Reads a socket option through the stack device IRP path.
KSTATUS NetSocketGetOption(SOCKET_OBJECT* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut) {
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->GetOption(providerSocket, option, value, valueSize, actualSizeOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpGetOption;
request.u.GetOption.Socket = providerSocket;
request.u.GetOption.Option = option;
request.u.GetOption.Value = value;
request.u.GetOption.ValueSize = valueSize;
request.u.GetOption.ActualSizeOut = actualSizeOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Socket Poll operation.
// Polls socket readiness through the stack device IRP path.
KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returnedEventsOut) {
if (!returnedEventsOut)
return STATUS_INVALID_PARAMETER;
@@ -235,10 +943,20 @@ KSTATUS NetSocketPoll(SOCKET_OBJECT* socket, uint32_t events, uint32_t* returned
const NET_STACK_PROVIDER* provider = NULL;
void* providerSocket = NULL;
KSTATUS st = NetSocketGetProviderSocket(socket, &provider, &providerSocket);
return KSUCCESS(st) ? provider->Poll(providerSocket, events, returnedEventsOut) : st;
if (!KSUCCESS(st))
return st;
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpPoll;
request.u.Poll.Socket = providerSocket;
request.u.Poll.Events = events;
request.u.Poll.ReturnedEventsOut = returnedEventsOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
// Implements the net Resolve Host operation.
// Resolves a host name through the stack device IRP path.
KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
if (!name || !addressOut)
return STATUS_INVALID_PARAMETER;
@@ -247,5 +965,12 @@ KSTATUS NetResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K*
if (!provider)
return STATUS_NOT_SUPPORTED;
return provider->ResolveHost(name, family, addressOut);
NET_SOCKET_IRP_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Operation = NetSocketIrpResolveHost;
request.u.ResolveHost.Name = name;
request.u.ResolveHost.Family = family;
request.u.ResolveHost.AddressOut = addressOut;
return NetSubmitSocketIrp(provider, &request, (IO_ASYNC_REQUEST_OBJECT*)0);
}
+6 -6
View File
@@ -6,6 +6,7 @@
#include <ntlite/ddi/net_device.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_spinlock.h>
#include <ntlite/ddi/io_driver.h>
static const NET_STACK_PROVIDER* gNetStackProvider = 0;
static SPINLOCK gNetStackLock = SPINLOCK_INIT;
@@ -15,13 +16,12 @@ static KSTATUS NetValidateStackProvider(const NET_STACK_PROVIDER* p) {
if (!p || p->Version != NET_STACK_PROVIDER_VERSION || !p->Name)
return STATUS_INVALID_PARAMETER;
if (!p->AttachDevice || !p->ReceiveFrame || !p->SocketCreate || !p->SocketClose ||
!p->Bind || !p->Listen || !p->Accept || !p->Connect ||
!p->Send || !p->Receive || !p->SendTo || !p->ReceiveFrom ||
!p->Shutdown || !p->SetOption || !p->GetOption || !p->Poll ||
!p->ResolveHost) {
if (!p->StackDevice || !p->AttachDevice || !p->ReceiveFrame)
return STATUS_INVALID_PARAMETER;
if (!p->StackDevice->DriverObject ||
!p->StackDevice->DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL])
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
+1
View File
@@ -27,6 +27,7 @@ static uint32_t KiObjectPoolTag(OBJECT_TYPE type) {
case ObjectTypeInputQueue: return MM_POOL_TAG('i', 'n', 'p', 'q');
case ObjectTypeImageSection: return MM_POOL_TAG('i', 'm', 's', 'e');
case ObjectTypeTimer: return MM_POOL_TAG('t', 'm', 'r', ' ');
case ObjectTypeModule: return MM_POOL_TAG('m', 'o', 'd', 'u');
default: return MM_POOL_TAG('o', 'b', 'j', ' ');
}
}