redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests

This commit is contained in:
maksw committed 2026-08-20 12:33:17 +02:00
1 parent 4add6789ba
commit f882d339fb
232 files changed
+20934 -8898

No files matched your search

+753
View File
@@ -0,0 +1,753 @@
// Kernel capability handles. This file owns process-local handle identity, grant delegation, and revocation.
#include <string.h>
#include <intrin.h>
#include <kernel/ke_handle.h>
#include <kernel/ke_process.h>
#include <kernel/ke_named_queue.h>
#include <kernel/ke_debug.h>
#include <kernel/io_file.h>
#include <kernel/ke_audio.h>
#include <kernel/mm_pool.h>
#include <ntlite/syscall.h>
#include <ntlite/security.h>
#include <ntlite/port.h>
typedef struct CAPABILITY_GRANT {
volatile long RefCount;
volatile long Revoked;
uint32_t Depth;
SYS_RIGHTS MaximumAccess;
OBJECT* Target;
struct CAPABILITY_GRANT* Parent;
} CAPABILITY_GRANT;
typedef struct KE_HANDLE_ENTRY {
struct KE_HANDLE_ENTRY* Next;
SYS_HANDLE Handle;
SYS_RIGHTS Access;
CAPABILITY_GRANT* Grant;
} KE_HANDLE_ENTRY;
typedef struct KE_CAPTURED_HANDLE {
CAPABILITY_GRANT* Grant;
SYS_RIGHTS Access;
} KE_CAPTURED_HANDLE;
_Static_assert((KE_HANDLE_BUCKET_COUNT & (KE_HANDLE_BUCKET_COUNT - 1u)) == 0,
"handle bucket count must be a power of two");
// Returns the process-local hash bucket for one opaque handle value.
static uint32_t KiHandleBucket(SYS_HANDLE handle) {
return (handle ^ (handle >> 16) ^ (handle >> 8)) & (KE_HANDLE_BUCKET_COUNT - 1u);
}
// Returns one entry while the handle-table lock is held.
static KE_HANDLE_ENTRY* KiFindHandleLocked(KE_HANDLE_TABLE* table, SYS_HANDLE handle, KE_HANDLE_ENTRY*** linkOut) {
if (linkOut) *linkOut = (KE_HANDLE_ENTRY**)0;
if (!table || handle == SYS_HANDLE_INVALID) return (KE_HANDLE_ENTRY*)0;
uint32_t bucket = KiHandleBucket(handle);
KE_HANDLE_ENTRY** link = &table->Buckets[bucket];
while (*link) {
if ((*link)->Handle == handle) {
if (linkOut) *linkOut = link;
return *link;
}
link = &(*link)->Next;
}
return (KE_HANDLE_ENTRY*)0;
}
// Acquires one grant reference while its owner still stabilizes the pointer.
static void KiReferenceGrant(CAPABILITY_GRANT* grant) {
if (!grant) return;
long refs = _InterlockedIncrement(&grant->RefCount);
if (refs <= 1) {
KePanic(KE_PANIC_CODE_GENERIC,
"capability: invalid grant reference grant=0x%016llX refs=%ld\n",
(uint64_t)(uintptr_t)grant,
refs);
}
}
// Releases one grant and recursively drops its immutable parent chain.
static void KiDereferenceGrant(CAPABILITY_GRANT* grant) {
while (grant) {
long refs = _InterlockedDecrement(&grant->RefCount);
if (refs > 0) return;
if (refs < 0) {
KePanic(KE_PANIC_CODE_GENERIC,
"capability: grant reference underflow grant=0x%016llX refs=%ld\n",
(uint64_t)(uintptr_t)grant,
refs);
}
CAPABILITY_GRANT* parent = grant->Parent;
if (!parent && grant->Target)
ObDereferenceObject(grant->Target);
MmFreePool(grant);
grant = parent;
}
}
// Returns whether a grant and every ancestor remain unrevoked.
static int KiGrantIsActive(const CAPABILITY_GRANT* grant) {
const CAPABILITY_GRANT* current = grant;
uint32_t depth = 0;
while (current) {
if (_InterlockedCompareExchange((volatile long*)&current->Revoked, 0, 0) != 0)
return 0;
current = current->Parent;
if (++depth > KE_CAPABILITY_MAX_DEPTH)
return 0;
}
return 1;
}
// Creates a root grant and takes the target object's lifetime reference.
static KSTATUS KiCreateRootGrant(OBJECT* object, SYS_RIGHTS maximumAccess, CAPABILITY_GRANT** grantOut) {
if (!object || !grantOut || maximumAccess == 0) return NTLSTATUS_INVALID_PARAMETER;
*grantOut = (CAPABILITY_GRANT*)0;
CAPABILITY_GRANT* grant = (CAPABILITY_GRANT*)MmAllocatePoolTag(sizeof(CAPABILITY_GRANT),
MM_POOL_TAG('c', 'a', 'p', 'g'));
if (!grant) return NTLSTATUS_NO_MEMORY;
memset(grant, 0, sizeof(*grant));
if (!ObTryReferenceObject(object)) {
MmFreePool(grant);
return NTLSTATUS_OBJECT_DEAD;
}
grant->RefCount = 1;
grant->Depth = 0;
grant->MaximumAccess = maximumAccess;
grant->Target = object;
grant->Parent = (CAPABILITY_GRANT*)0;
*grantOut = grant;
return NTLSTATUS_SUCCESS;
}
// Creates one attenuated child grant and retains its parent.
static KSTATUS KiCreateChildGrant(CAPABILITY_GRANT* parent,
SYS_RIGHTS maximumAccess,
CAPABILITY_GRANT** grantOut) {
if (!parent || !grantOut || maximumAccess == 0) return NTLSTATUS_INVALID_PARAMETER;
*grantOut = (CAPABILITY_GRANT*)0;
if (parent->Depth + 1u >= KE_CAPABILITY_MAX_DEPTH)
return NTLSTATUS_LIMIT_REACHED;
if ((maximumAccess & ~parent->MaximumAccess) != 0)
return NTLSTATUS_ACCESS_DENIED;
if (!KiGrantIsActive(parent))
return NTLSTATUS_CAPABILITY_REVOKED;
CAPABILITY_GRANT* grant = (CAPABILITY_GRANT*)MmAllocatePoolTag(sizeof(CAPABILITY_GRANT),
MM_POOL_TAG('c', 'a', 'p', 'g'));
if (!grant) return NTLSTATUS_NO_MEMORY;
memset(grant, 0, sizeof(*grant));
KiReferenceGrant(parent);
grant->RefCount = 1;
grant->Depth = parent->Depth + 1u;
grant->MaximumAccess = maximumAccess;
grant->Target = parent->Target;
grant->Parent = parent;
if (!KiGrantIsActive(parent)) {
KiDereferenceGrant(grant);
return NTLSTATUS_CAPABILITY_REVOKED;
}
*grantOut = grant;
return NTLSTATUS_SUCCESS;
}
// Records type-specific ownership after a process handle becomes visible.
static void KiHandleOpened(OBJECT* object) {
if (object && object->Type == ObjectTypeAudioStream)
KeAudioStreamHandleOpened((KE_AUDIO_STREAM*)object);
}
// Releases type-specific ownership before a process handle disappears.
static void KiHandleClosed(OBJECT* object) {
if (object && object->Type == ObjectTypeAudioStream)
KeAudioStreamHandleClosed((KE_AUDIO_STREAM*)object);
}
// Inserts one retained grant into a process and consumes that grant reference on success.
static KSTATUS KiInsertGrantHandle(PROCESS* process,
CAPABILITY_GRANT* grant,
SYS_RIGHTS access,
SYS_HANDLE* handleOut) {
if (!process || !grant || !handleOut || access == 0) return NTLSTATUS_INVALID_PARAMETER;
if ((access & ~grant->MaximumAccess) != 0) return NTLSTATUS_ACCESS_DENIED;
if (!KiGrantIsActive(grant)) return NTLSTATUS_CAPABILITY_REVOKED;
KE_HANDLE_ENTRY* entry = (KE_HANDLE_ENTRY*)MmAllocatePoolTag(sizeof(KE_HANDLE_ENTRY),
MM_POOL_TAG('h', 'n', 'd', 'l'));
if (!entry) return NTLSTATUS_NO_MEMORY;
memset(entry, 0, sizeof(*entry));
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
if (table->Rundown) {
KeReleaseSpinlock(&table->Lock, flags);
MmFreePool(entry);
return NTLSTATUS_OBJECT_DEAD;
}
SYS_HANDLE handle = table->NextHandle;
if (handle == SYS_HANDLE_INVALID) {
KeReleaseSpinlock(&table->Lock, flags);
MmFreePool(entry);
return NTLSTATUS_ID_EXHAUSTED;
}
table->NextHandle = handle + 1u;
entry->Handle = handle;
entry->Access = access;
entry->Grant = grant;
uint32_t bucket = KiHandleBucket(handle);
entry->Next = table->Buckets[bucket];
table->Buckets[bucket] = entry;
table->Count++;
KiHandleOpened(grant->Target);
KeReleaseSpinlock(&table->Lock, flags);
*handleOut = handle;
return NTLSTATUS_SUCCESS;
}
// Initializes an empty process-local handle namespace.
void KeInitializeHandleTable(KE_HANDLE_TABLE* table) {
if (!table) return;
memset(table, 0, sizeof(*table));
table->NextHandle = 1u;
table->Lock = (SPINLOCK)SPINLOCK_INIT;
}
// Returns the object-type-specific rights mask accepted by the handle core.
SYS_RIGHTS KeGetValidHandleRights(OBJECT_TYPE type) {
const SYS_RIGHTS management = SYS_RIGHT_MANAGE;
switch (type) {
case ObjectTypeProcess:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT |
SYS_PROCESS_RIGHT_TERMINATE | SYS_PROCESS_RIGHT_INSERT_HANDLE |
SYS_PROCESS_RIGHT_BUILD;
case ObjectTypeThread:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_THREAD_RIGHT_RESUME;
case ObjectTypeMutex:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_MUTEX_RIGHT_RELEASE;
case ObjectTypeSemaphore:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_SEMAPHORE_RIGHT_SIGNAL;
case ObjectTypeEvent:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_EVENT_RIGHT_SIGNAL;
case ObjectTypeQueue:
return management | SYS_RIGHT_QUERY | SYS_QUEUE_RIGHT_SEND |
SYS_QUEUE_RIGHT_RECEIVE | SYS_QUEUE_RIGHT_PUBLISH;
case ObjectTypeFile:
return management | SYS_RIGHT_QUERY | SYS_FILE_RIGHT_READ_DATA |
SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_LOCK |
SYS_FILE_RIGHT_MAP_READ | SYS_FILE_RIGHT_MAP_WRITE;
case ObjectTypeDir:
return management | SYS_RIGHT_QUERY | SYS_DIR_RIGHT_ENUMERATE | SYS_DIR_RIGHT_OPEN |
SYS_DIR_RIGHT_MODIFY;
case ObjectTypeAsyncIo:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_ASYNC_IO_RIGHT_CANCEL;
case ObjectTypeSocket:
return management | SYS_RIGHT_QUERY | SYS_SOCKET_RIGHT_BIND |
SYS_SOCKET_RIGHT_CONNECT | SYS_SOCKET_RIGHT_LISTEN |
SYS_SOCKET_RIGHT_ACCEPT | SYS_SOCKET_RIGHT_SEND |
SYS_SOCKET_RIGHT_RECEIVE | SYS_SOCKET_RIGHT_SHUTDOWN |
SYS_SOCKET_RIGHT_POLL | SYS_SOCKET_RIGHT_SET_OPTION;
case ObjectTypeFileMapping:
return management | SYS_RIGHT_QUERY | SYS_FILE_MAPPING_RIGHT_MAP;
case ObjectTypeSurface:
return management | SYS_RIGHT_QUERY | SYS_SURFACE_RIGHT_MAP | SYS_SURFACE_RIGHT_PRESENT;
case ObjectTypeInputQueue:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_INPUT_QUEUE_RIGHT_READ;
case ObjectTypeTimer:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_TIMER_RIGHT_SET;
case ObjectTypeDisplayClock:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
case ObjectTypeAudioStream:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT |
SYS_AUDIO_STREAM_RIGHT_WRITE | SYS_AUDIO_STREAM_RIGHT_CONTROL;
case ObjectTypeSecurityContext:
return management | SYS_RIGHT_QUERY |
SYS_SECURITY_CONTEXT_RIGHT_DERIVE | SYS_SECURITY_CONTEXT_RIGHT_ASSIGN;
case ObjectTypePort:
return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT |
SYS_PORT_RIGHT_CONNECT | SYS_PORT_RIGHT_ACCEPT |
SYS_PORT_RIGHT_SEND | SYS_PORT_RIGHT_RECEIVE |
SYS_PORT_RIGHT_CALL | SYS_PORT_RIGHT_REPLY |
SYS_PORT_RIGHT_DISCONNECT;
case ObjectTypeVolume:
return management | SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY;
case ObjectTypeVolumeManager:
return management | SYS_RIGHT_QUERY |
SYS_VOLUME_MANAGER_RIGHT_ENUMERATE | SYS_VOLUME_MANAGER_RIGHT_OPEN |
SYS_VOLUME_MANAGER_RIGHT_WAIT;
case ObjectTypeDevice:
case ObjectTypeDriver:
case ObjectTypeUsbDevice:
case ObjectTypeUsbPipe:
case ObjectTypeImageSection:
case ObjectTypeAudioEndpoint:
return management | SYS_RIGHT_QUERY;
default:
return 0;
}
}
// Creates one root capability handle for an object.
KSTATUS KeCreateHandle(PROCESS* process, OBJECT* object, SYS_RIGHTS access, SYS_HANDLE* handleOut) {
if (!process || !object || !handleOut || access == 0) return NTLSTATUS_INVALID_PARAMETER;
SYS_RIGHTS valid = KeGetValidHandleRights(object->Type);
if (!valid || (access & ~valid) != 0) return NTLSTATUS_INVALID_PARAMETER;
CAPABILITY_GRANT* grant = (CAPABILITY_GRANT*)0;
KSTATUS status = KiCreateRootGrant(object, access, &grant);
if (!KSUCCESS(status)) return status;
status = KiInsertGrantHandle(process, grant, access, handleOut);
if (!KSUCCESS(status)) KiDereferenceGrant(grant);
return status;
}
// References the target object after validating type, revocation, and rights.
KSTATUS KeReferenceHandle(PROCESS* process,
SYS_HANDLE handle,
OBJECT_TYPE expectedType,
SYS_RIGHTS requiredRights,
OBJECT** objectOut) {
if (!process || !objectOut) return NTLSTATUS_INVALID_PARAMETER;
*objectOut = (OBJECT*)0;
if (handle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, handle, (KE_HANDLE_ENTRY***)0);
if (!entry) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
if (requiredRights && (entry->Access & requiredRights) != requiredRights) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
if (!KiGrantIsActive(entry->Grant)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_CAPABILITY_REVOKED;
}
OBJECT* object = entry->Grant->Target;
if (!object || (expectedType != 0 && object->Type != expectedType)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
if (!ObTryReferenceObject(object)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_OBJECT_DEAD;
}
KeReleaseSpinlock(&table->Lock, flags);
*objectOut = object;
return NTLSTATUS_SUCCESS;
}
// Closes one process-local handle without reusing its numeric identity.
KSTATUS KeCloseHandle(PROCESS* process, SYS_HANDLE handle) {
if (!process || handle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY** link = (KE_HANDLE_ENTRY**)0;
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, handle, &link);
if (!entry || !link) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
*link = entry->Next;
if (table->Count == 0) {
KeReleaseSpinlock(&table->Lock, flags);
KePanic(KE_PANIC_CODE_GENERIC, "capability: handle count underflow pid=%u\n", process->ProcessId);
}
table->Count--;
KeReleaseSpinlock(&table->Lock, flags);
KSTATUS closeStatus = NTLSTATUS_SUCCESS;
OBJECT* object = entry->Grant->Target;
if (object && object->Type == ObjectTypeFile)
closeStatus = IoFlushFile((FILE_OBJECT*)object);
KeNamedQueueUnpublishHandle(process, handle, object);
KiHandleClosed(object);
KiDereferenceGrant(entry->Grant);
MmFreePool(entry);
return closeStatus;
}
// Duplicates one alias of the same capability grant into another process.
KSTATUS KeDuplicateHandle(PROCESS* sourceProcess,
SYS_HANDLE sourceHandle,
PROCESS* targetProcess,
SYS_RIGHTS desiredRights,
uint32_t duplicateFlags,
SYS_HANDLE* targetHandleOut) {
if (!sourceProcess || !targetProcess || !targetHandleOut)
return NTLSTATUS_INVALID_PARAMETER;
if (sourceHandle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
const uint32_t validFlags = SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS;
if ((duplicateFlags & ~validFlags) != 0) return NTLSTATUS_INVALID_PARAMETER;
if ((duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) && desiredRights != 0)
return NTLSTATUS_INVALID_PARAMETER;
if (!(duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) && desiredRights == 0)
return NTLSTATUS_INVALID_PARAMETER;
CAPABILITY_GRANT* grant = (CAPABILITY_GRANT*)0;
SYS_RIGHTS sourceAccess = 0;
KE_HANDLE_TABLE* table = &sourceProcess->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, sourceHandle, (KE_HANDLE_ENTRY***)0);
if (!entry) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
if (!KiGrantIsActive(entry->Grant)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_CAPABILITY_REVOKED;
}
SYS_RIGHTS requiredManagement = SYS_RIGHT_DUPLICATE;
if (sourceProcess != targetProcess)
requiredManagement |= SYS_RIGHT_TRANSFER;
if ((entry->Access & requiredManagement) != requiredManagement) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
sourceAccess = entry->Access;
SYS_RIGHTS validRights = KeGetValidHandleRights(entry->Grant->Target->Type);
if (!(duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) &&
(desiredRights & ~validRights) != 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_PARAMETER;
}
if (!(duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS) &&
(desiredRights & ~sourceAccess) != 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
grant = entry->Grant;
KiReferenceGrant(grant);
KeReleaseSpinlock(&table->Lock, flags);
SYS_RIGHTS targetAccess = (duplicateFlags & SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS)
? sourceAccess : desiredRights;
KSTATUS status = KiInsertGrantHandle(targetProcess, grant, targetAccess, targetHandleOut);
if (!KSUCCESS(status)) KiDereferenceGrant(grant);
return status;
}
// Derives one child grant whose maximum rights are an attenuated subset.
KSTATUS KeDeriveHandle(PROCESS* process,
SYS_HANDLE sourceHandle,
SYS_RIGHTS desiredRights,
SYS_HANDLE* derivedHandleOut) {
if (!process || !derivedHandleOut || desiredRights == 0)
return NTLSTATUS_INVALID_PARAMETER;
if (sourceHandle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
CAPABILITY_GRANT* parent = (CAPABILITY_GRANT*)0;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, sourceHandle, (KE_HANDLE_ENTRY***)0);
if (!entry) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
if (!KiGrantIsActive(entry->Grant)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_CAPABILITY_REVOKED;
}
if ((entry->Access & SYS_RIGHT_DERIVE) == 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
SYS_RIGHTS validRights = KeGetValidHandleRights(entry->Grant->Target->Type);
if ((desiredRights & ~validRights) != 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_PARAMETER;
}
if ((desiredRights & ~entry->Access) != 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
parent = entry->Grant;
KiReferenceGrant(parent);
KeReleaseSpinlock(&table->Lock, flags);
CAPABILITY_GRANT* child = (CAPABILITY_GRANT*)0;
KSTATUS status = KiCreateChildGrant(parent, desiredRights, &child);
KiDereferenceGrant(parent);
if (!KSUCCESS(status)) return status;
status = KiInsertGrantHandle(process, child, desiredRights, derivedHandleOut);
if (!KSUCCESS(status)) KiDereferenceGrant(child);
return status;
}
// Revokes one grant and all grants derived from it.
KSTATUS KeRevokeHandle(PROCESS* process, SYS_HANDLE handle) {
if (!process) return NTLSTATUS_INVALID_PARAMETER;
if (handle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, handle, (KE_HANDLE_ENTRY***)0);
if (!entry) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_INVALID_HANDLE;
}
if ((entry->Access & SYS_RIGHT_REVOKE) == 0) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
if (!KiGrantIsActive(entry->Grant)) {
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_CAPABILITY_REVOKED;
}
_InterlockedExchange(&entry->Grant->Revoked, 1);
KeReleaseSpinlock(&table->Lock, flags);
return NTLSTATUS_SUCCESS;
}
// Captures one capability so a later Port delivery does not depend on the source handle staying open.
KSTATUS KeCaptureHandleForTransfer(PROCESS* sourceProcess,
SYS_HANDLE sourceHandle,
SYS_RIGHTS desiredRights,
uint32_t flags,
KE_CAPTURED_HANDLE** capturedOut) {
if (!sourceProcess || !capturedOut)
return NTLSTATUS_INVALID_PARAMETER;
*capturedOut = (KE_CAPTURED_HANDLE*)0;
if (sourceHandle == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_HANDLE;
const uint32_t validFlags = KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS |
KE_HANDLE_TRANSFER_FLAG_DERIVE;
if ((flags & ~validFlags) != 0)
return NTLSTATUS_INVALID_PARAMETER;
if ((flags & KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS) &&
(flags & KE_HANDLE_TRANSFER_FLAG_DERIVE))
return NTLSTATUS_INVALID_PARAMETER;
if ((flags & KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS) && desiredRights != 0)
return NTLSTATUS_INVALID_PARAMETER;
if (!(flags & KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS) && desiredRights == 0)
return NTLSTATUS_INVALID_PARAMETER;
CAPABILITY_GRANT* sourceGrant = (CAPABILITY_GRANT*)0;
SYS_RIGHTS sourceAccess = 0;
KE_HANDLE_TABLE* table = &sourceProcess->HandleTable;
uintptr_t lockFlags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = KiFindHandleLocked(table, sourceHandle, (KE_HANDLE_ENTRY***)0);
if (!entry) {
KeReleaseSpinlock(&table->Lock, lockFlags);
return NTLSTATUS_INVALID_HANDLE;
}
if (!KiGrantIsActive(entry->Grant)) {
KeReleaseSpinlock(&table->Lock, lockFlags);
return NTLSTATUS_CAPABILITY_REVOKED;
}
SYS_RIGHTS required = SYS_RIGHT_TRANSFER;
if (flags & KE_HANDLE_TRANSFER_FLAG_DERIVE)
required |= SYS_RIGHT_DERIVE;
if ((entry->Access & required) != required) {
KeReleaseSpinlock(&table->Lock, lockFlags);
return NTLSTATUS_ACCESS_DENIED;
}
sourceAccess = entry->Access;
SYS_RIGHTS targetAccess = (flags & KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS)
? sourceAccess : desiredRights;
SYS_RIGHTS validRights = KeGetValidHandleRights(entry->Grant->Target->Type);
if ((targetAccess & ~validRights) != 0) {
KeReleaseSpinlock(&table->Lock, lockFlags);
return NTLSTATUS_INVALID_PARAMETER;
}
if ((targetAccess & ~sourceAccess) != 0) {
KeReleaseSpinlock(&table->Lock, lockFlags);
return NTLSTATUS_ACCESS_DENIED;
}
sourceGrant = entry->Grant;
KiReferenceGrant(sourceGrant);
KeReleaseSpinlock(&table->Lock, lockFlags);
CAPABILITY_GRANT* capturedGrant = sourceGrant;
if (flags & KE_HANDLE_TRANSFER_FLAG_DERIVE) {
KSTATUS status = KiCreateChildGrant(sourceGrant, targetAccess, &capturedGrant);
KiDereferenceGrant(sourceGrant);
if (!KSUCCESS(status))
return status;
}
KE_CAPTURED_HANDLE* captured = (KE_CAPTURED_HANDLE*)MmAllocatePoolTag(sizeof(*captured),
MM_POOL_TAG('c', 'a', 'p', 't'));
if (!captured) {
KiDereferenceGrant(capturedGrant);
return NTLSTATUS_NO_MEMORY;
}
captured->Grant = capturedGrant;
captured->Access = targetAccess;
*capturedOut = captured;
return NTLSTATUS_SUCCESS;
}
// Releases one captured capability that did not become a target-process handle.
void KeReleaseCapturedHandle(KE_CAPTURED_HANDLE* captured) {
if (!captured)
return;
if (captured->Grant)
KiDereferenceGrant(captured->Grant);
MmFreePool(captured);
}
// Materializes a complete transfer set atomically with respect to target-process handle visibility.
KSTATUS KeMaterializeCapturedHandles(PROCESS* targetProcess,
KE_CAPTURED_HANDLE** captured,
uint32_t count,
SYS_HANDLE* handlesOut) {
if (!targetProcess || (count && (!captured || !handlesOut)))
return NTLSTATUS_INVALID_PARAMETER;
if (count == 0)
return NTLSTATUS_SUCCESS;
KE_HANDLE_ENTRY** entries = (KE_HANDLE_ENTRY**)MmAllocatePoolTag(sizeof(KE_HANDLE_ENTRY*) * count,
MM_POOL_TAG('h', 'b', 'a', 't'));
if (!entries)
return NTLSTATUS_NO_MEMORY;
memset(entries, 0, sizeof(KE_HANDLE_ENTRY*) * count);
KSTATUS status = NTLSTATUS_SUCCESS;
for (uint32_t i = 0; i < count; ++i) {
if (!captured[i] || !captured[i]->Grant || captured[i]->Access == 0) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
if (!KiGrantIsActive(captured[i]->Grant)) {
status = NTLSTATUS_CAPABILITY_REVOKED;
break;
}
entries[i] = (KE_HANDLE_ENTRY*)MmAllocatePoolTag(sizeof(KE_HANDLE_ENTRY),
MM_POOL_TAG('h', 'n', 'd', 'l'));
if (!entries[i]) {
status = NTLSTATUS_NO_MEMORY;
break;
}
memset(entries[i], 0, sizeof(*entries[i]));
}
if (!KSUCCESS(status)) {
for (uint32_t i = 0; i < count; ++i)
if (entries[i]) MmFreePool(entries[i]);
MmFreePool(entries);
return status;
}
KE_HANDLE_TABLE* table = &targetProcess->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
if (table->Rundown) {
status = NTLSTATUS_OBJECT_DEAD;
} else if (table->NextHandle == SYS_HANDLE_INVALID ||
(uint64_t)table->NextHandle + (uint64_t)count - 1ull > UINT32_MAX) {
status = NTLSTATUS_ID_EXHAUSTED;
} else {
for (uint32_t i = 0; i < count; ++i) {
if (!KiGrantIsActive(captured[i]->Grant)) {
status = NTLSTATUS_CAPABILITY_REVOKED;
break;
}
}
}
if (KSUCCESS(status)) {
for (uint32_t i = 0; i < count; ++i) {
SYS_HANDLE handle = table->NextHandle++;
entries[i]->Handle = handle;
entries[i]->Access = captured[i]->Access;
entries[i]->Grant = captured[i]->Grant;
uint32_t bucket = KiHandleBucket(handle);
entries[i]->Next = table->Buckets[bucket];
table->Buckets[bucket] = entries[i];
table->Count++;
KiHandleOpened(captured[i]->Grant->Target);
handlesOut[i] = handle;
captured[i]->Grant = (CAPABILITY_GRANT*)0;
}
}
KeReleaseSpinlock(&table->Lock, flags);
if (!KSUCCESS(status)) {
for (uint32_t i = 0; i < count; ++i)
MmFreePool(entries[i]);
MmFreePool(entries);
return status;
}
for (uint32_t i = 0; i < count; ++i) {
MmFreePool(captured[i]);
captured[i] = (KE_CAPTURED_HANDLE*)0;
}
MmFreePool(entries);
return NTLSTATUS_SUCCESS;
}
// Returns the number of currently live handles in one process.
uint32_t KeGetHandleCount(PROCESS* process) {
if (!process) return 0;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
uint32_t count = table->Count;
KeReleaseSpinlock(&table->Lock, flags);
return count;
}
// Releases every handle and capability grant owned by one process.
void KeRundownHandleTable(PROCESS* process) {
if (!process) return;
KE_HANDLE_TABLE* table = &process->HandleTable;
uintptr_t rundownFlags = KeAcquireSpinlock(&table->Lock);
table->Rundown = 1u;
KeReleaseSpinlock(&table->Lock, rundownFlags);
for (uint32_t bucket = 0; bucket < KE_HANDLE_BUCKET_COUNT; ++bucket) {
while (true) {
uintptr_t flags = KeAcquireSpinlock(&table->Lock);
KE_HANDLE_ENTRY* entry = table->Buckets[bucket];
if (entry) {
table->Buckets[bucket] = entry->Next;
if (table->Count == 0) {
KeReleaseSpinlock(&table->Lock, flags);
KePanic(KE_PANIC_CODE_GENERIC,
"capability: rundown count underflow pid=%u\n",
process->ProcessId);
}
table->Count--;
}
KeReleaseSpinlock(&table->Lock, flags);
if (!entry) break;
OBJECT* object = entry->Grant->Target;
KeNamedQueueUnpublishHandle(process, entry->Handle, object);
KiHandleClosed(object);
KiDereferenceGrant(entry->Grant);
MmFreePool(entry);
}
}
}