/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ex/handle.c * PURPOSE: Generic Executive Handle Tables * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Thomas Weidenmueller */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* GLOBALS *******************************************************************/ LIST_ENTRY HandleTableListHead; EX_PUSH_LOCK HandleTableListLock; #define SizeOfHandle(x) (sizeof(HANDLE) * (x)) #define INDEX_TO_HANDLE_VALUE(x) ((x) << HANDLE_TAG_BITS) /* PRIVATE FUNCTIONS *********************************************************/ #ifdef _WIN64 #define strtoulptr strtoull #else #define strtoulptr strtoul #endif CODE_SEG("INIT") VOID NTAPI ExpInitializeHandleTables(VOID) { /* Initialize the list of handle tables and the lock */ InitializeListHead(&HandleTableListHead); ExInitializePushLock(&HandleTableListLock); } PHANDLE_TABLE_ENTRY NTAPI ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN EXHANDLE Handle) { ULONG TableLevel; ULONG_PTR TableBase; PHANDLE_TABLE_ENTRY HandleArray, Entry; PVOID *PointerArray; /* Clear the tag bits */ Handle.TagBits = 0; /* Check if the handle is in the allocated range */ if (Handle.Value >= HandleTable->NextHandleNeedingPool) { return NULL; } /* Get the table code */ TableBase = HandleTable->TableCode; /* Extract the table level and actual table base */ TableLevel = (ULONG)(TableBase & 3); TableBase &= ~3; PointerArray = (PVOID*)TableBase; HandleArray = (PHANDLE_TABLE_ENTRY)TableBase; /* Check what level we're running at */ switch (TableLevel) { case 2: /* Get the mid level pointer array */ PointerArray = PointerArray[Handle.HighIndex]; ASSERT(PointerArray != NULL); /* Fall through */ case 1: /* Get the handle array */ HandleArray = PointerArray[Handle.MidIndex]; ASSERT(HandleArray != NULL); /* Fall through */ case 0: /* Get the entry using the low index */ Entry = &HandleArray[Handle.LowIndex]; /* All done */ break; default: ASSERT(FALSE); Entry = NULL; } /* Return the handle entry */ return Entry; } PVOID NTAPI ExpAllocateTablePagedPool(IN PEPROCESS Process OPTIONAL, IN SIZE_T Size) { PVOID Buffer; NTSTATUS Status; /* Do the allocation */ Buffer = ExAllocatePoolWithTag(PagedPool, Size, TAG_OBJECT_TABLE); if (Buffer) { /* Clear the memory */ RtlZeroMemory(Buffer, Size); /* Check if we have a process to charge quota */ if (Process) { /* Charge quota */ Status = PsChargeProcessPagedPoolQuota(Process, Size); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE); return NULL; } } } /* Return the allocated memory */ return Buffer; } PVOID NTAPI ExpAllocateTablePagedPoolNoZero(IN PEPROCESS Process OPTIONAL, IN SIZE_T Size) { PVOID Buffer; NTSTATUS Status; /* Do the allocation */ Buffer = ExAllocatePoolWithTag(PagedPool, Size, TAG_OBJECT_TABLE); if (Buffer) { /* Check if we have a process to charge quota */ if (Process) { /* Charge quota */ Status = PsChargeProcessPagedPoolQuota(Process, Size); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE); return NULL; } } } /* Return the allocated memory */ return Buffer; } VOID NTAPI ExpFreeTablePagedPool(IN PEPROCESS Process OPTIONAL, IN PVOID Buffer, IN SIZE_T Size) { /* Free the buffer */ ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE); if (Process) { /* Release quota */ PsReturnProcessPagedPoolQuota(Process, Size); } } VOID NTAPI ExpFreeLowLevelTable(IN PEPROCESS Process, IN PHANDLE_TABLE_ENTRY TableEntry) { /* Check if we have an entry */ if (TableEntry[0].Object) { /* Free the entry */ ExpFreeTablePagedPool(Process, TableEntry[0].Object, LOW_LEVEL_ENTRIES * sizeof(HANDLE_TABLE_ENTRY_INFO)); } /* Free the table */ ExpFreeTablePagedPool(Process, TableEntry, PAGE_SIZE); } VOID NTAPI ExpFreeHandleTable(IN PHANDLE_TABLE HandleTable) { PEPROCESS Process = HandleTable->QuotaProcess; ULONG i, j; ULONG_PTR TableCode = HandleTable->TableCode; ULONG_PTR TableBase = TableCode & ~3; ULONG TableLevel = (ULONG)(TableCode & 3); PHANDLE_TABLE_ENTRY Level1, *Level2, **Level3; PAGED_CODE(); /* Check which level we're at */ if (TableLevel == 0) { /* Select the first level table base and just free it */ Level1 = (PVOID)TableBase; ExpFreeLowLevelTable(Process, Level1); } else if (TableLevel == 1) { /* Select the second level table base */ Level2 = (PVOID)TableBase; /* Loop each mid level entry */ for (i = 0; i < MID_LEVEL_ENTRIES; i++) { /* Leave if we've reached the last entry */ if (!Level2[i]) break; /* Free the second level table */ ExpFreeLowLevelTable(Process, Level2[i]); } /* Free the second level table */ ExpFreeTablePagedPool(Process, Level2, PAGE_SIZE); } else { /* Select the third level table base */ Level3 = (PVOID)TableBase; /* Loop each high level entry */ for (i = 0; i < HIGH_LEVEL_ENTRIES; i++) { /* Leave if we've reached the last entry */ if (!Level3[i]) break; /* Loop each mid level entry */ for (j = 0; j < MID_LEVEL_ENTRIES; j++) { /* Leave if we've reached the last entry */ if (!Level3[i][j]) break; /* Free the second level table */ ExpFreeLowLevelTable(Process, Level3[i][j]); } /* Free the third level table entry */ ExpFreeTablePagedPool(Process, Level3[i], PAGE_SIZE); } /* Free the third level table */ ExpFreeTablePagedPool(Process, Level3, SizeOfHandle(HIGH_LEVEL_ENTRIES)); } /* Free the actual table and check if we need to release quota */ ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE); if (Process) { /* Release the quota it was taking up */ PsReturnProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE)); } } VOID NTAPI ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN EXHANDLE Handle, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { ULONG OldValue, *Free; ULONG LockIndex; PAGED_CODE(); /* Sanity checks */ ASSERT(HandleTableEntry->Object == NULL); ASSERT(HandleTableEntry == ExpLookupHandleTableEntry(HandleTable, Handle)); /* Decrement the handle count */ InterlockedDecrement(&HandleTable->HandleCount); /* Mark the handle as free */ Handle.TagBits = 0; /* Check if we're FIFO */ if (!HandleTable->StrictFIFO) { /* Select a lock index */ LockIndex = Handle.Index % 4; /* Select which entry to use */ Free = (HandleTable->HandleTableLock[LockIndex].Locked) ? &HandleTable->FirstFree : &HandleTable->LastFree; } else { /* No need to worry about locking, take the last entry */ Free = &HandleTable->LastFree; } /* Start value change loop */ for (;;) { /* Get the current value and write */ OldValue = *Free; HandleTableEntry->NextFreeTableEntry = OldValue; if (InterlockedCompareExchange((PLONG)Free, Handle.AsULONG, OldValue) == OldValue) { /* Break out, we're done. Make sure the handle value makes sense */ ASSERT((OldValue & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool); break; } } } PHANDLE_TABLE NTAPI ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL, IN BOOLEAN NewTable) { PHANDLE_TABLE HandleTable; PHANDLE_TABLE_ENTRY HandleTableTable, HandleEntry; ULONG i; NTSTATUS Status; PAGED_CODE(); /* Allocate the table */ HandleTable = ExAllocatePoolWithTag(PagedPool, sizeof(HANDLE_TABLE), TAG_OBJECT_TABLE); if (!HandleTable) return NULL; /* Check if we have a process */ if (Process) { /* Charge quota */ Status = PsChargeProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE)); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE); return NULL; } } /* Clear the table */ RtlZeroMemory(HandleTable, sizeof(HANDLE_TABLE)); /* Now allocate the first level structures */ HandleTableTable = ExpAllocateTablePagedPoolNoZero(Process, PAGE_SIZE); if (!HandleTableTable) { /* Failed, free the table */ ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE); /* Return the quota it was taking up */ if (Process) { PsReturnProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE)); } return NULL; } /* Write the pointer to our first level structures */ HandleTable->TableCode = (ULONG_PTR)HandleTableTable; /* Initialize the first entry */ HandleEntry = &HandleTableTable[0]; HandleEntry->NextFreeTableEntry = -2; HandleEntry->Value = 0; /* Check if this is a new table */ if (NewTable) { /* Go past the root entry */ HandleEntry++; /* Loop every low level entry */ for (i = 1; i < (LOW_LEVEL_ENTRIES - 1); i++) { /* Set up the free data */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = INDEX_TO_HANDLE_VALUE(i + 1); /* Move to the next entry */ HandleEntry++; } /* Terminate the last entry */ HandleEntry->Value = 0; HandleEntry->NextFreeTableEntry = 0; HandleTable->FirstFree = INDEX_TO_HANDLE_VALUE(1); } /* Set the next handle needing pool after our allocated page from above */ HandleTable->NextHandleNeedingPool = INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES); /* Setup the rest of the handle table data */ HandleTable->QuotaProcess = Process; HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId; HandleTable->Flags = 0; /* Loop all the handle table locks */ for (i = 0; i < 4; i++) { /* Initialize the handle table lock */ ExInitializePushLock(&HandleTable->HandleTableLock[i]); } /* Initialize the contention event lock and return the lock */ ExInitializePushLock(&HandleTable->HandleContentionEvent); return HandleTable; } PHANDLE_TABLE_ENTRY NTAPI ExpAllocateLowLevelTable(IN PHANDLE_TABLE HandleTable, IN BOOLEAN DoInit) { ULONG i, Base; PHANDLE_TABLE_ENTRY Low, HandleEntry; /* Allocate the low level table */ Low = ExpAllocateTablePagedPoolNoZero(HandleTable->QuotaProcess, PAGE_SIZE); if (!Low) return NULL; /* Setup the initial entry */ HandleEntry = &Low[0]; HandleEntry->NextFreeTableEntry = -2; HandleEntry->Value = 0; /* Check if we're initializing */ if (DoInit) { /* Go to the next entry and the base entry */ HandleEntry++; Base = HandleTable->NextHandleNeedingPool + INDEX_TO_HANDLE_VALUE(2); /* Loop each entry */ for (i = Base; i < Base + INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES - 2); i += INDEX_TO_HANDLE_VALUE(1)) { /* Free this entry and move on to the next one */ HandleEntry->NextFreeTableEntry = i; HandleEntry->Value = 0; HandleEntry++; } /* Terminate the last entry */ HandleEntry->NextFreeTableEntry = 0; HandleEntry->Value = 0; } /* Return the low level table */ return Low; } PHANDLE_TABLE_ENTRY* NTAPI ExpAllocateMidLevelTable(IN PHANDLE_TABLE HandleTable, IN BOOLEAN DoInit, OUT PHANDLE_TABLE_ENTRY *LowTableEntry) { PHANDLE_TABLE_ENTRY *Mid, Low; /* Allocate the mid level table */ Mid = ExpAllocateTablePagedPool(HandleTable->QuotaProcess, PAGE_SIZE); if (!Mid) return NULL; /* Allocate a new low level for it */ Low = ExpAllocateLowLevelTable(HandleTable, DoInit); if (!Low) { /* We failed, free the mid table */ ExpFreeTablePagedPool(HandleTable->QuotaProcess, Mid, PAGE_SIZE); return NULL; } /* Link the tables and return the pointer */ Mid[0] = Low; *LowTableEntry = Low; return Mid; } BOOLEAN NTAPI ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable, IN BOOLEAN DoInit) { ULONG i, j, Index; PHANDLE_TABLE_ENTRY Low = NULL, *Mid, **High, *SecondLevel, **ThirdLevel; ULONG NewFree, FirstFree; PVOID Value; ULONG_PTR TableCode = HandleTable->TableCode; ULONG_PTR TableBase = TableCode & ~3; ULONG TableLevel = (ULONG)(TableCode & 3); PAGED_CODE(); /* Check how many levels we already have */ if (TableLevel == 0) { /* Allocate a mid level, since we only have a low level */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) return FALSE; /* Link up the tables */ Mid[1] = Mid[0]; Mid[0] = (PVOID)TableBase; /* Write the new level and attempt to change the table code */ TableBase = ((ULONG_PTR)Mid) | 1; Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase); } else if (TableLevel == 1) { /* Setup the 2nd level table */ SecondLevel = (PVOID)TableBase; /* Get if the next index can fit in the table */ i = HandleTable->NextHandleNeedingPool / INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES); if (i < MID_LEVEL_ENTRIES) { /* We need to allocate a new table */ Low = ExpAllocateLowLevelTable(HandleTable, DoInit); if (!Low) return FALSE; /* Update the table */ Value = InterlockedExchangePointer((PVOID*)&SecondLevel[i], Low); ASSERT(Value == NULL); } else { /* We need a new high level table */ High = ExpAllocateTablePagedPool(HandleTable->QuotaProcess, SizeOfHandle(HIGH_LEVEL_ENTRIES)); if (!High) return FALSE; /* Allocate a new mid level table as well */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) { /* We failed, free the high level table as well */ ExpFreeTablePagedPool(HandleTable->QuotaProcess, High, SizeOfHandle(HIGH_LEVEL_ENTRIES)); return FALSE; } /* Link up the tables */ High[0] = (PVOID)TableBase; High[1] = Mid; /* Write the new table and change the table code */ TableBase = ((ULONG_PTR)High) | 2; Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase); } } else if (TableLevel == 2) { /* Setup the 3rd level table */ ThirdLevel = (PVOID)TableBase; /* Get the index and check if it can fit */ i = HandleTable->NextHandleNeedingPool / INDEX_TO_HANDLE_VALUE(MAX_MID_INDEX); if (i >= HIGH_LEVEL_ENTRIES) return FALSE; /* Check if there's no mid-level table */ if (!ThirdLevel[i]) { /* Allocate a new mid level table */ Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low); if (!Mid) return FALSE; /* Update the table pointer */ Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i], Mid); ASSERT(Value == NULL); } else { /* We have one, check at which index we should insert our entry */ Index = (HandleTable->NextHandleNeedingPool / INDEX_TO_HANDLE_VALUE(1)) - i * MAX_MID_INDEX; j = Index / LOW_LEVEL_ENTRIES; /* Allocate a new low level */ Low = ExpAllocateLowLevelTable(HandleTable, DoInit); if (!Low) return FALSE; /* Update the table pointer */ Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i][j], Low); ASSERT(Value == NULL); } } else { /* Something is really broken */ ASSERT(FALSE); } /* Update the index of the next handle */ Index = InterlockedExchangeAdd((PLONG) &HandleTable->NextHandleNeedingPool, INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES)); /* Check if need to initialize the table */ if (DoInit) { /* Create a new index number */ Index += INDEX_TO_HANDLE_VALUE(1); /* Start free index change loop */ for (;;) { /* Setup the first free index */ FirstFree = HandleTable->FirstFree; Low[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = FirstFree; /* Change the index */ NewFree = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, Index, FirstFree); if (NewFree == FirstFree) break; } } /* All done */ return TRUE; } ULONG NTAPI ExpMoveFreeHandles(IN PHANDLE_TABLE HandleTable) { ULONG LastFree, i; /* Clear the last free index */ LastFree = InterlockedExchange((PLONG) &HandleTable->LastFree, 0); /* Check if we had no index */ if (!LastFree) return LastFree; /* Acquire the locks we need */ for (i = 1; i < 4; i++) { /* Acquire this lock exclusively */ ExWaitOnPushLock(&HandleTable->HandleTableLock[i]); } /* Check if we're not strict FIFO */ if (!HandleTable->StrictFIFO) { /* Update the first free index */ if (!InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, LastFree, 0)) { /* We're done, exit */ return LastFree; } } /* We are strict FIFO, we need to reverse the entries */ ASSERT(FALSE); return LastFree; } PHANDLE_TABLE_ENTRY NTAPI ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, OUT PEXHANDLE NewHandle) { ULONG OldValue, NewValue, NewValue1; PHANDLE_TABLE_ENTRY Entry; EXHANDLE Handle, OldHandle; BOOLEAN Result; ULONG i; /* Start allocation loop */ for (;;) { /* Get the current link */ OldValue = HandleTable->FirstFree; while (!OldValue) { /* No free entries remain, lock the handle table */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&HandleTable->HandleTableLock[0]); /* Check the value again */ OldValue = HandleTable->FirstFree; if (OldValue) { /* Another thread has already created a new level, bail out */ ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]); KeLeaveCriticalRegion(); break; } /* Now move any free handles */ OldValue = ExpMoveFreeHandles(HandleTable); if (OldValue) { /* Another thread has already moved them, bail out */ ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]); KeLeaveCriticalRegion(); break; } /* We're the first one through, so do the actual allocation */ Result = ExpAllocateHandleTableEntrySlow(HandleTable, TRUE); /* Unlock the table and get the value now */ ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]); KeLeaveCriticalRegion(); OldValue = HandleTable->FirstFree; /* Check if allocation failed */ if (!Result) { /* Check if nobody else went through here */ if (!OldValue) { /* We're still the only thread around, so fail */ NewHandle->GenericHandleOverlay = NULL; return NULL; } } } /* We made it, write the current value */ Handle.Value = (OldValue & FREE_HANDLE_MASK); /* Lookup the entry for this handle */ Entry = ExpLookupHandleTableEntry(HandleTable, Handle); /* Get an available lock and acquire it */ OldHandle.Value = OldValue; i = OldHandle.Index % 4; KeEnterCriticalRegion(); ExAcquirePushLockShared(&HandleTable->HandleTableLock[i]); /* Check if the value changed after acquiring the lock */ if (OldValue != *(volatile ULONG*)&HandleTable->FirstFree) { /* It did, so try again */ ExReleasePushLockShared(&HandleTable->HandleTableLock[i]); KeLeaveCriticalRegion(); continue; } /* Now get the next value and do the compare */ NewValue = *(volatile ULONG*)&Entry->NextFreeTableEntry; NewValue1 = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, NewValue, OldValue); /* The change was done, so release the lock */ ExReleasePushLockShared(&HandleTable->HandleTableLock[i]); KeLeaveCriticalRegion(); /* Check if the compare was successful */ if (NewValue1 == OldValue) { /* Make sure that the new handle is in range, and break out */ ASSERT((NewValue & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool); break; } else { /* The compare failed, make sure we expected it */ ASSERT((NewValue1 & FREE_HANDLE_MASK) != (OldValue & FREE_HANDLE_MASK)); } } /* Increase the number of handles */ InterlockedIncrement(&HandleTable->HandleCount); /* Return the handle and the entry */ *NewHandle = Handle; return Entry; } PHANDLE_TABLE NTAPI ExCreateHandleTable(IN PEPROCESS Process OPTIONAL) { PHANDLE_TABLE HandleTable; PAGED_CODE(); /* Allocate the handle table */ HandleTable = ExpAllocateHandleTable(Process, TRUE); if (!HandleTable) return NULL; /* Acquire the handle table lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&HandleTableListLock); /* Insert it into the list */ InsertTailList(&HandleTableListHead, &HandleTable->HandleTableList); /* Release the lock */ ExReleasePushLockExclusive(&HandleTableListLock); KeLeaveCriticalRegion(); /* Return the handle table */ return HandleTable; } HANDLE NTAPI ExCreateHandle(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { EXHANDLE Handle; PHANDLE_TABLE_ENTRY NewEntry; PAGED_CODE(); /* Start with a clean handle */ Handle.GenericHandleOverlay = NULL; /* Allocate a new entry */ NewEntry = ExpAllocateHandleTableEntry(HandleTable, &Handle); if (NewEntry) { /* Enter a critical region */ KeEnterCriticalRegion(); /* Write the entry */ *NewEntry = *HandleTableEntry; /* Unlock it and leave the critical region */ ExUnlockHandleTableEntry(HandleTable, NewEntry); KeLeaveCriticalRegion(); } /* Return the handle value */ return Handle.GenericHandleOverlay; } VOID NTAPI ExpBlockOnLockedHandleEntry(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { LONG_PTR OldValue; EX_PUSH_LOCK_WAIT_BLOCK WaitBlock; /* Block on the pushlock */ ExBlockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock); /* Get the current value and check if it's been unlocked */ OldValue = HandleTableEntry->Value; if (!(OldValue) || (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT)) { /* Unblock the pushlock and return */ ExfUnblockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock); } else { /* Wait for it to be unblocked */ ExWaitForUnblockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock); } } BOOLEAN NTAPI ExpLockHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { LONG_PTR NewValue, OldValue; /* Sanity check */ ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL)); /* Start lock loop */ for (;;) { /* Get the current value and check if it's locked */ OldValue = *(volatile LONG_PTR *)&HandleTableEntry->Object; if (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) { /* It's not locked, remove the lock bit to lock it */ NewValue = OldValue & ~EXHANDLE_TABLE_ENTRY_LOCK_BIT; if (InterlockedCompareExchangePointer(&HandleTableEntry->Object, (PVOID)NewValue, (PVOID)OldValue) == (PVOID)OldValue) { /* We locked it, get out */ return TRUE; } } else { /* We couldn't lock it, bail out if it's been freed */ if (!OldValue) return FALSE; } /* It's locked, wait for it to be unlocked */ ExpBlockOnLockedHandleEntry(HandleTable, HandleTableEntry); } } VOID NTAPI ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, IN PHANDLE_TABLE_ENTRY HandleTableEntry) { LONG_PTR OldValue; PAGED_CODE(); /* Sanity check */ ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL)); /* Set the lock bit and make sure it wasn't earlier */ OldValue = InterlockedOr((PLONG) &HandleTableEntry->Value, EXHANDLE_TABLE_ENTRY_LOCK_BIT); ASSERT((OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0); /* Unblock any waiters */ ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL); } VOID NTAPI ExRemoveHandleTable(IN PHANDLE_TABLE HandleTable) { PAGED_CODE(); /* Acquire the table lock */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&HandleTableListLock); /* Remove the table and reset the list */ RemoveEntryList(&HandleTable->HandleTableList); InitializeListHead(&HandleTable->HandleTableList); /* Release the lock */ ExReleasePushLockExclusive(&HandleTableListLock); KeLeaveCriticalRegion(); } VOID NTAPI ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable, IN PVOID DestroyHandleProcedure OPTIONAL) { PAGED_CODE(); /* Remove the handle from the list */ ExRemoveHandleTable(HandleTable); /* Check if we have a destroy callback */ if (DestroyHandleProcedure) { /* FIXME: */ ASSERT(FALSE); } /* Free the handle table */ ExpFreeHandleTable(HandleTable); } BOOLEAN NTAPI ExDestroyHandle(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL) { EXHANDLE ExHandle; PVOID Object; PAGED_CODE(); /* Setup the actual handle value */ ExHandle.GenericHandleOverlay = Handle; /* Enter a critical region and check if we have to lookup the handle */ KeEnterCriticalRegion(); if (!HandleTableEntry) { /* Lookup the entry */ HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle); /* Make sure that we found an entry, and that it's valid */ if (!(HandleTableEntry) || !(HandleTableEntry->Object) || (HandleTableEntry->NextFreeTableEntry == -2)) { /* Invalid handle, fail */ KeLeaveCriticalRegion(); return FALSE; } /* Lock the entry */ if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Couldn't lock, fail */ KeLeaveCriticalRegion(); return FALSE; } } else { /* Make sure the handle is locked */ ASSERT((HandleTableEntry->Value & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0); } /* Clear the handle */ Object = InterlockedExchangePointer((PVOID*)&HandleTableEntry->Object, NULL); /* Sanity checks */ ASSERT(Object != NULL); ASSERT((((ULONG_PTR)Object) & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0); /* Unblock the pushlock */ ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL); /* Free the actual entry */ ExpFreeHandleTableEntry(HandleTable, ExHandle, HandleTableEntry); /* If we got here, return success */ KeLeaveCriticalRegion(); return TRUE; } PHANDLE_TABLE_ENTRY NTAPI ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle) { EXHANDLE ExHandle; PHANDLE_TABLE_ENTRY HandleTableEntry; PAGED_CODE(); /* Set the handle value */ ExHandle.GenericHandleOverlay = Handle; /* Fail if we got an invalid index */ if (!(ExHandle.Index & (LOW_LEVEL_ENTRIES - 1))) return NULL; /* Do the lookup */ HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle); if (!HandleTableEntry) return NULL; /* Lock it */ if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) return NULL; /* Return the entry */ return HandleTableEntry; } PHANDLE_TABLE NTAPI ExDupHandleTable(IN PEPROCESS Process, IN PHANDLE_TABLE HandleTable, IN PEX_DUPLICATE_HANDLE_CALLBACK DupHandleProcedure, IN ULONG_PTR Mask) { PHANDLE_TABLE NewTable; EXHANDLE Handle; PHANDLE_TABLE_ENTRY HandleTableEntry, NewEntry; BOOLEAN Failed = FALSE; PAGED_CODE(); /* Allocate the duplicated copy */ NewTable = ExpAllocateHandleTable(Process, FALSE); if (!NewTable) return NULL; /* Loop each entry */ while (NewTable->NextHandleNeedingPool < HandleTable->NextHandleNeedingPool) { /* Insert it into the duplicated copy */ if (!ExpAllocateHandleTableEntrySlow(NewTable, FALSE)) { /* Insert failed, free the new copy and return */ ExpFreeHandleTable(NewTable); return NULL; } } /* Setup the initial handle table data */ NewTable->HandleCount = 0; NewTable->ExtraInfoPages = 0; NewTable->FirstFree = 0; /* Setup the first handle value */ Handle.Value = INDEX_TO_HANDLE_VALUE(1); /* Enter a critical region and lookup the new entry */ KeEnterCriticalRegion(); while ((NewEntry = ExpLookupHandleTableEntry(NewTable, Handle))) { /* Lookup the old entry */ HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle); /* Loop each entry */ do { /* Check if it doesn't match the audit mask */ if (!(HandleTableEntry->Value & Mask)) { /* Free it since we won't use it */ Failed = TRUE; } else { /* Lock the entry */ if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Free it since we can't lock it, so we won't use it */ Failed = TRUE; } else { /* Copy the handle value */ *NewEntry = *HandleTableEntry; /* Call the duplicate callback */ if (DupHandleProcedure(Process, HandleTable, HandleTableEntry, NewEntry)) { /* Clear failure flag */ Failed = FALSE; /* Lock the entry, increase the handle count */ NewEntry->Value |= EXHANDLE_TABLE_ENTRY_LOCK_BIT; NewTable->HandleCount++; } else { /* Duplication callback refused, fail */ Failed = TRUE; } } } /* Check if we failed earlier and need to free */ if (Failed) { /* Free this entry */ NewEntry->Object = NULL; NewEntry->NextFreeTableEntry = NewTable->FirstFree; NewTable->FirstFree = (ULONG)Handle.Value; } /* Increase the handle value and move to the next entry */ Handle.Value += INDEX_TO_HANDLE_VALUE(1); NewEntry++; HandleTableEntry++; } while (Handle.Value % INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES)); /* We're done, skip the last entry */ Handle.Value += INDEX_TO_HANDLE_VALUE(1); } /* Acquire the table lock and insert this new table into the list */ ExAcquirePushLockExclusive(&HandleTableListLock); InsertTailList(&HandleTableListHead, &NewTable->HandleTableList); ExReleasePushLockExclusive(&HandleTableListLock); /* Leave the critical region we entered previously and return the table */ KeLeaveCriticalRegion(); return NewTable; } BOOLEAN NTAPI ExChangeHandle(IN PHANDLE_TABLE HandleTable, IN HANDLE Handle, IN PEX_CHANGE_HANDLE_CALLBACK ChangeRoutine, IN ULONG_PTR Context) { EXHANDLE ExHandle; PHANDLE_TABLE_ENTRY HandleTableEntry; BOOLEAN Result = FALSE; PAGED_CODE(); /* Set the handle value */ ExHandle.GenericHandleOverlay = Handle; /* Find the entry for this handle */ HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle); /* Make sure that we found an entry, and that it's valid */ if (!(HandleTableEntry) || !(HandleTableEntry->Object) || (HandleTableEntry->NextFreeTableEntry == -2)) { /* It isn't, fail */ return FALSE; } /* Enter a critical region */ KeEnterCriticalRegion(); /* Try locking the handle entry */ if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Call the change routine and unlock the entry */ Result = ChangeRoutine(HandleTableEntry, Context); ExUnlockHandleTableEntry(HandleTable, HandleTableEntry); } /* Leave the critical region and return the callback result */ KeLeaveCriticalRegion(); return Result; } VOID NTAPI ExSweepHandleTable(IN PHANDLE_TABLE HandleTable, IN PEX_SWEEP_HANDLE_CALLBACK EnumHandleProcedure, IN PVOID Context) { EXHANDLE Handle; PHANDLE_TABLE_ENTRY HandleTableEntry; PAGED_CODE(); /* Set the initial value and loop the entries */ Handle.Value = INDEX_TO_HANDLE_VALUE(1); while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle))) { /* Loop each handle */ do { /* Lock the entry */ if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Notify the callback routine */ EnumHandleProcedure(HandleTableEntry, Handle.GenericHandleOverlay, Context); } /* Go to the next handle and entry */ Handle.Value += INDEX_TO_HANDLE_VALUE(1); HandleTableEntry++; } while (Handle.Value % INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES)); /* Skip past the last entry */ Handle.Value += INDEX_TO_HANDLE_VALUE(1); } } /* * @implemented */ BOOLEAN NTAPI ExEnumHandleTable(IN PHANDLE_TABLE HandleTable, IN PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure, IN OUT PVOID Context, OUT PHANDLE EnumHandle OPTIONAL) { EXHANDLE Handle; PHANDLE_TABLE_ENTRY HandleTableEntry; BOOLEAN Result = FALSE; PAGED_CODE(); /* Enter a critical region */ KeEnterCriticalRegion(); /* Set the initial value and loop the entries */ Handle.Value = 0; while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle))) { /* Validate the entry */ if ((HandleTableEntry->Object) && (HandleTableEntry->NextFreeTableEntry != -2)) { /* Lock the entry */ if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) { /* Notify the callback routine */ Result = EnumHandleProcedure(HandleTableEntry, Handle.GenericHandleOverlay, Context); /* Unlock it */ ExUnlockHandleTableEntry(HandleTable, HandleTableEntry); /* Was this the one looked for? */ if (Result) { /* If so, return it if requested */ if (EnumHandle) *EnumHandle = Handle.GenericHandleOverlay; break; } } } /* Go to the next entry */ Handle.Value += INDEX_TO_HANDLE_VALUE(1); } /* Leave the critical region and return callback result */ KeLeaveCriticalRegion(); return Result; } #if DBG && defined(KDBG) #include BOOLEAN ExpKdbgExtHandle(ULONG Argc, PCHAR Argv[]) { USHORT i; char *endptr; HANDLE ProcessId; EXHANDLE ExHandle; PLIST_ENTRY Entry; PEPROCESS Process; WCHAR KeyPath[256]; PFILE_OBJECT FileObject; PHANDLE_TABLE HandleTable; POBJECT_HEADER ObjectHeader; PHANDLE_TABLE_ENTRY TableEntry; ULONG NeededLength = 0; ULONG NameLength; PCM_KEY_CONTROL_BLOCK Kcb, CurrentKcb; POBJECT_HEADER_NAME_INFO ObjectNameInfo; if (Argc > 1) { /* Get EPROCESS address or PID */ i = 0; while (Argv[1][i]) { if (!isdigit(Argv[1][i])) { i = 0; break; } ++i; } if (i == 0) { if (!KdbpGetHexNumber(Argv[1], (PVOID)&Process)) { KdbpPrint("Invalid parameter: %s\n", Argv[1]); return TRUE; } /* In the end, we always want a PID */ ProcessId = PsGetProcessId(Process); } else { ProcessId = (HANDLE)strtoulptr(Argv[1], &endptr, 10); if (*endptr != '\0') { KdbpPrint("Invalid parameter: %s\n", Argv[1]); return TRUE; } } } else { ProcessId = PsGetCurrentProcessId(); } for (Entry = HandleTableListHead.Flink; Entry != &HandleTableListHead; Entry = Entry->Flink) { /* Only return matching PID * 0 matches everything */ HandleTable = CONTAINING_RECORD(Entry, HANDLE_TABLE, HandleTableList); if (ProcessId != 0 && HandleTable->UniqueProcessId != ProcessId) { continue; } KdbpPrint("\n"); KdbpPrint("Handle table at %p with %d entries in use\n", HandleTable, HandleTable->HandleCount); ExHandle.Value = 0; while ((TableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle))) { if ((TableEntry->Object) && (TableEntry->NextFreeTableEntry != -2)) { ObjectHeader = ObpGetHandleObject(TableEntry); KdbpPrint("%p: Object: %p GrantedAccess: %x Entry: %p\n", ExHandle.Value, &ObjectHeader->Body, TableEntry->GrantedAccess, TableEntry); KdbpPrint("Object: %p Type: (%x) %wZ\n", &ObjectHeader->Body, ObjectHeader->Type, &ObjectHeader->Type->Name); KdbpPrint("\tObjectHeader: %p\n", ObjectHeader); KdbpPrint("\t\tHandleCount: %u PointerCount: %u\n", ObjectHeader->HandleCount, ObjectHeader->PointerCount); /* Specific objects debug prints */ /* For file, display path */ if (ObjectHeader->Type == IoFileObjectType) { FileObject = (PFILE_OBJECT)&ObjectHeader->Body; KdbpPrint("\t\t\tName: %wZ\n", &FileObject->FileName); } /* For directory, and win32k objects, display object name */ else if (ObjectHeader->Type == ObpDirectoryObjectType || ObjectHeader->Type == ExWindowStationObjectType || ObjectHeader->Type == ExDesktopObjectType || ObjectHeader->Type == MmSectionObjectType) { ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); if (ObjectNameInfo != NULL && ObjectNameInfo->Name.Buffer != NULL) { KdbpPrint("\t\t\tName: %wZ\n", &ObjectNameInfo->Name); } } /* For registry keys, display full path */ else if (ObjectHeader->Type == CmpKeyObjectType) { Kcb = ((PCM_KEY_BODY)&ObjectHeader->Body)->KeyControlBlock; if (!Kcb->Delete) { CurrentKcb = Kcb; /* See: CmpQueryNameInformation() */ while (CurrentKcb != NULL) { if (CurrentKcb->NameBlock->Compressed) NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength); else NeededLength += CurrentKcb->NameBlock->NameLength; NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR); CurrentKcb = CurrentKcb->ParentKcb; } if (NeededLength < sizeof(KeyPath)) { CurrentKcb = Kcb; while (CurrentKcb != NULL) { if (CurrentKcb->NameBlock->Compressed) { NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength); CmpCopyCompressedName(&KeyPath[(NeededLength - NameLength)/sizeof(WCHAR)], NameLength, CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength); } else { NameLength = CurrentKcb->NameBlock->NameLength; RtlCopyMemory(&KeyPath[(NeededLength - NameLength)/sizeof(WCHAR)], CurrentKcb->NameBlock->Name, NameLength); } NeededLength -= NameLength; NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR); KeyPath[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; CurrentKcb = CurrentKcb->ParentKcb; } } KdbpPrint("\t\t\tName: %S\n", KeyPath); } } } ExHandle.Value += INDEX_TO_HANDLE_VALUE(1); } } return TRUE; } #endif // DBG && defined(KDBG)