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

+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,