Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3489 lines
89 KiB

/*++
Copyright (c) 1989-1995 Microsoft Corporation
Module Name:
handle.c
Abstract:
This module implements a set of functions for supporting handles.
Author:
Steve Wood (stevewo) 25-Apr-1989
David N. Cutler (davec) 17-May-1995 (rewrite)
Gary Kimura (GaryKi) 9-Dec-1997 (rerewrite)
Adrian Marinescu (adrmarin) 24-May-2000
Support dynamic changes to the number of levels we use. The code
performs the best for typical handle table sizes and scales better.
Neill Clift (NeillC) 24-Jul-2000
Make the handle allocate, free and duplicate paths mostly lock free except
for the lock entry locks, table expansion and locks to solve the A-B-A problem.
Revision History:
--*/
#include "exp.h"
#pragma hdrstop
//
// Local constants and support routines
//
//
// Define global structures that link all handle tables together except the
// ones where the user has called RemoveHandleTable
//
ERESOURCE HandleTableListLock;
ULONG TotalTraceBuffers = 0;
#if !DBG // Make this a const varible so its optimized away on free
const
#endif
BOOLEAN ExTraceAllTables = FALSE;
#ifdef ALLOC_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
LIST_ENTRY HandleTableListHead;
#ifdef ALLOC_PRAGMA
#pragma data_seg()
#endif
//
// This is the sign low bit used to lock handle table entries
//
#define EXHANDLE_TABLE_ENTRY_LOCK_BIT 1
#define EX_ADDITIONAL_INFO_SIGNATURE (-2)
#define ExpIsValidObjectEntry(Entry) \
( (Entry != NULL) && (Entry->Object != NULL) && (Entry->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE) )
#define TABLE_PAGE_SIZE PAGE_SIZE
#define LOWLEVEL_COUNT (TABLE_PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
#define MIDLEVEL_COUNT (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))
#define HIGHLEVEL_COUNT 32
#define LOWLEVEL_THRESHOLD LOWLEVEL_COUNT
#define MIDLEVEL_THRESHOLD (MIDLEVEL_COUNT * LOWLEVEL_COUNT)
#define HIGHLEVEL_THRESHOLD (MIDLEVEL_COUNT * MIDLEVEL_COUNT * LOWLEVEL_COUNT)
#define HIGHLEVEL_SIZE (HIGHLEVEL_COUNT * sizeof (PHANDLE_TABLE_ENTRY))
#define LEVEL_CODE_MASK 3
//
// Local support routines
//
PHANDLE_TABLE
ExpAllocateHandleTable (
IN PEPROCESS Process OPTIONAL
);
VOID
ExpFreeHandleTable (
IN PHANDLE_TABLE HandleTable
);
BOOLEAN
ExpAllocateHandleTableEntrySlow (
IN PHANDLE_TABLE HandleTable
);
PHANDLE_TABLE_ENTRY
ExpAllocateHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
OUT PEXHANDLE Handle
);
VOID
ExpFreeHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE Handle,
IN PHANDLE_TABLE_ENTRY HandleTableEntry
);
PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE Handle
);
PHANDLE_TABLE_ENTRY *
ExpAllocateMidLevelTable (
IN PHANDLE_TABLE HandleTable,
OUT PHANDLE_TABLE_ENTRY *pNewLowLevel
);
PVOID
ExpAllocateTablePagedPool (
IN PEPROCESS QuotaProcess OPTIONAL,
IN SIZE_T NumberOfBytes
);
VOID
ExpFreeTablePagedPool (
IN PEPROCESS QuotaProcess OPTIONAL,
IN PVOID PoolMemory,
IN SIZE_T NumberOfBytes
);
PHANDLE_TABLE_ENTRY
ExpAllocateLowLevelTable (
IN PHANDLE_TABLE HandleTable
);
VOID
ExpFreeLowLevelTable (
IN PEPROCESS QuotaProcess,
IN PHANDLE_TABLE_ENTRY TableLevel1
);
VOID
ExpBlockOnLockedHandleEntry (
PHANDLE_TABLE HandleTable,
PHANDLE_TABLE_ENTRY HandleTableEntry
);
ULONG
ExpMoveFreeHandles (
IN PHANDLE_TABLE HandleTable
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExInitializeHandleTablePackage)
#pragma alloc_text(INIT, ExSetHandleTableStrictFIFO)
#pragma alloc_text(PAGE, ExUnlockHandleTableEntry)
#pragma alloc_text(PAGE, ExCreateHandleTable)
#pragma alloc_text(PAGE, ExRemoveHandleTable)
#pragma alloc_text(PAGE, ExDestroyHandleTable)
#pragma alloc_text(PAGE, ExEnumHandleTable)
#pragma alloc_text(PAGE, ExDupHandleTable)
#pragma alloc_text(PAGE, ExSnapShotHandleTables)
#pragma alloc_text(PAGE, ExCreateHandle)
#pragma alloc_text(PAGE, ExDestroyHandle)
#pragma alloc_text(PAGE, ExChangeHandle)
#pragma alloc_text(PAGE, ExMapHandleToPointer)
#pragma alloc_text(PAGE, ExMapHandleToPointerEx)
#pragma alloc_text(PAGE, ExpAllocateHandleTable)
#pragma alloc_text(PAGE, ExpFreeHandleTable)
#pragma alloc_text(PAGE, ExpAllocateHandleTableEntry)
#pragma alloc_text(PAGE, ExpAllocateHandleTableEntrySlow)
#pragma alloc_text(PAGE, ExpFreeHandleTableEntry)
#pragma alloc_text(PAGE, ExpLookupHandleTableEntry)
#pragma alloc_text(PAGE, ExSweepHandleTable)
#pragma alloc_text(PAGE, ExpAllocateMidLevelTable)
#pragma alloc_text(PAGE, ExpAllocateTablePagedPool)
#pragma alloc_text(PAGE, ExpFreeTablePagedPool)
#pragma alloc_text(PAGE, ExpAllocateLowLevelTable)
#pragma alloc_text(PAGE, ExSetHandleInfo)
#pragma alloc_text(PAGE, ExpGetHandleInfo)
#pragma alloc_text(PAGE, ExSnapShotHandleTablesEx)
#pragma alloc_text(PAGE, ExpFreeLowLevelTable)
#pragma alloc_text(PAGE, ExpBlockOnLockedHandleEntry)
#pragma alloc_text(PAGE, ExpMoveFreeHandles)
#pragma alloc_text(PAGE, ExEnableHandleTracing)
#endif
//
// Define macros to lock and unlock the handle table.
// We use this lock only for handle table expansion.
//
#define ExpLockHandleTableExclusive(xxHandleTable,xxCurrentThread) { \
KeEnterCriticalRegionThread (xxCurrentThread); \
ExAcquirePushLockExclusive (&HandleTable->HandleTableLock[0]); \
}
#define ExpUnlockHandleTableExclusive(xxHandleTable,xxCurrentThread) { \
ExReleasePushLockExclusive (&HandleTable->HandleTableLock[0]); \
KeLeaveCriticalRegionThread (xxCurrentThread); \
}
#define ExpLockHandleTableShared(xxHandleTable,xxCurrentThread,xxIdx) { \
KeEnterCriticalRegionThread (xxCurrentThread); \
ExAcquirePushLockShared (&HandleTable->HandleTableLock[Idx]); \
}
#define ExpUnlockHandleTableShared(xxHandleTable,xxCurrentThread,xxIdx) { \
ExReleasePushLockShared (&HandleTable->HandleTableLock[Idx]); \
KeLeaveCriticalRegionThread (xxCurrentThread); \
}
FORCEINLINE
ULONG
ExpInterlockedExchange (
IN OUT PULONG Index,
IN ULONG FirstIndex,
IN PHANDLE_TABLE_ENTRY Entry
)
{
ULONG OldIndex, NewIndex;
while (1) {
OldIndex = *Index;
//
// We use this routine for a list push.
//
NewIndex = FirstIndex;
Entry->NextFreeTableEntry = OldIndex;
if (OldIndex == (ULONG) InterlockedCompareExchange ((PLONG)Index,
NewIndex,
OldIndex)) {
return OldIndex;
}
}
}
ULONG
ExpMoveFreeHandles (
IN PHANDLE_TABLE HandleTable
)
{
ULONG OldValue, NewValue;
ULONG Index, OldIndex, NewIndex, FreeSize;
PHANDLE_TABLE_ENTRY Entry, FirstEntry;
EXHANDLE Handle;
ULONG Idx;
BOOLEAN StrictFIFO;
//
// First remove all the handles from the free list so we can add them to the
// list we use for allocates.
//
OldValue = InterlockedExchange ((PLONG)&HandleTable->LastFree,
0);
Index = OldValue;
if (Index == 0) {
return OldValue;
}
//
// We are pushing old entries onto the free list.
// We have the A-B-A problem here as these items may have been moved here because
// another thread was using them in the pop code.
//
for (Idx = 1; Idx < HANDLE_TABLE_LOCKS; Idx++) {
ExAcquireReleasePushLockExclusive (&HandleTable->HandleTableLock[Idx]);
}
StrictFIFO = HandleTable->StrictFIFO;
//
// If we are strict FIFO then reverse the list to make handle reuse rare.
//
if (!StrictFIFO) {
//
// We have a complete chain here. If there is no existing chain we
// can just push this one without any hassles. If we can't then
// we can just fall into the reversing code anyway as we need
// to find the end of the chain to continue it.
//
if (InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
OldValue,
0) == 0) {
return OldValue;
}
}
//
// Loop over all the entries and reverse the chain.
//
FreeSize = OldIndex = 0;
FirstEntry = NULL;
while (1) {
FreeSize++;
Handle.Value = Index;
Entry = ExpLookupHandleTableEntry (HandleTable, Handle);
NewIndex = Entry->NextFreeTableEntry;
Entry->NextFreeTableEntry = OldIndex;
if (OldIndex == 0) {
FirstEntry = Entry;
}
OldIndex = Index;
if (NewIndex == 0) {
break;
}
Index = NewIndex;
}
NewValue = ExpInterlockedExchange (&HandleTable->FirstFree,
OldIndex,
FirstEntry);
//
// If we haven't got a pool of a few handles then force
// table expansion to keep the free handle size high
//
if (FreeSize < 100 && StrictFIFO) {
OldValue = 0;
}
return OldValue;
}
PHANDLE_TABLE_ENTRY
ExpAllocateHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
OUT PEXHANDLE pHandle
)
/*++
Routine Description:
This routine does a fast allocate of a free handle. It's lock free if
possible.
Only the rare case of handle table expansion is covered by the handle
table lock.
Arguments:
HandleTable - Supplies the handle table being allocated from.
pHandle - Handle returned
Return Value:
PHANDLE_TABLE_ENTRY - The allocated handle table entry pointer or NULL
on failure.
--*/
{
PKTHREAD CurrentThread;
ULONG OldValue, NewValue;
PHANDLE_TABLE_ENTRY Entry;
EXHANDLE Handle;
BOOLEAN RetVal;
ULONG Idx;
CurrentThread = KeGetCurrentThread ();
while (1) {
OldValue = HandleTable->FirstFree;
while (OldValue == 0) {
//
// Lock the handle table for exclusive access as we will be
// allocating a new table level.
//
ExpLockHandleTableExclusive (HandleTable, CurrentThread);
//
// If we have multiple threads trying to expand the table at
// the same time then by just acquiring the table lock we
// force those threads to complete their allocations and
// populate the free list. We must check the free list here
// so we don't expand the list twice without needing to.
//
OldValue = HandleTable->FirstFree;
if (OldValue != 0) {
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
break;
}
//
// See if we have any handles on the alternate free list
// These handles need some locking to move them over.
//
OldValue = ExpMoveFreeHandles (HandleTable);
if (OldValue != 0) {
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
break;
}
//
// This must be the first thread attempting expansion or all the
// free handles allocated by another thread got used up in the gap.
//
RetVal = ExpAllocateHandleTableEntrySlow (HandleTable);
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
OldValue = HandleTable->FirstFree;
//
// If ExpAllocateHandleTableEntrySlow had a failed allocation
// then we want to fail the call. We check for free entries
// before we exit just in case they got allocated or freed by
// somebody else in the gap.
//
if (!RetVal) {
if (OldValue == 0) {
pHandle->GenericHandleOverlay = NULL;
return NULL;
}
}
}
Handle.Value = OldValue;
Entry = ExpLookupHandleTableEntry (HandleTable, Handle);
Idx = (OldValue>>2) % HANDLE_TABLE_LOCKS;
ExpLockHandleTableShared (HandleTable, CurrentThread, Idx);
if (OldValue != *(volatile ULONG *) &HandleTable->FirstFree) {
ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx);
continue;
}
KeMemoryBarrier ();
NewValue = *(volatile ULONG *) &Entry->NextFreeTableEntry;
NewValue = InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
NewValue,
OldValue);
ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx);
if (NewValue == OldValue) {
break;
}
}
InterlockedIncrement (&HandleTable->HandleCount);
*pHandle = Handle;
return Entry;
}
VOID
ExpBlockOnLockedHandleEntry (
PHANDLE_TABLE HandleTable,
PHANDLE_TABLE_ENTRY HandleTableEntry
)
{
EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
LONG_PTR CurrentValue;
//
// Queue our wait block to be signaled by a releasing thread.
//
ExBlockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
CurrentValue = HandleTableEntry->Value;
if (CurrentValue == 0 || (CurrentValue&EXHANDLE_TABLE_ENTRY_LOCK_BIT) != 0) {
ExUnblockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
} else {
ExWaitForUnblockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
}
}
BOOLEAN
FORCEINLINE
ExpLockHandleTableEntry (
PHANDLE_TABLE HandleTable,
PHANDLE_TABLE_ENTRY HandleTableEntry
)
/*++
Routine Description:
This routine locks the specified handle table entry. After the entry is
locked the sign bit will be set.
Arguments:
HandleTable - Supplies the handle table containing the entry being locked.
HandleTableEntry - Supplies the handle table entry being locked.
Return Value:
TRUE if the entry is valid and locked, and FALSE if the entry is
marked free.
--*/
{
LONG_PTR NewValue;
LONG_PTR CurrentValue;
//
// We are about to take a lock. Make sure we are protected.
//
ASSERT ((KeGetCurrentThread()->KernelApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL));
//
// We'll keep on looping reading in the value, making sure it is not null,
// and if it is not currently locked we'll try for the lock and return
// true if we get it. Otherwise we'll pause a bit and then try again.
//
while (TRUE) {
CurrentValue = *((volatile LONG_PTR *)&HandleTableEntry->Object);
//
// If the handle value is greater than zero then it is not currently
// locked and we should try for the lock, by setting the lock bit and
// doing an interlocked exchange.
//
if (CurrentValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) {
//
// Remove the
//
NewValue = CurrentValue - EXHANDLE_TABLE_ENTRY_LOCK_BIT;
if ((LONG_PTR)(InterlockedCompareExchangePointer (&HandleTableEntry->Object,
(PVOID)NewValue,
(PVOID)CurrentValue)) == CurrentValue) {
return TRUE;
}
} else {
//
// Make sure the handle table entry is not freed
//
if (CurrentValue == 0) {
return FALSE;
}
}
ExpBlockOnLockedHandleEntry (HandleTable, HandleTableEntry);
}
}
NTKERNELAPI
VOID
ExUnlockHandleTableEntry (
PHANDLE_TABLE HandleTable,
PHANDLE_TABLE_ENTRY HandleTableEntry
)
/*++
Routine Description:
This routine unlocks the specified handle table entry. After the entry is
unlocked the sign bit will be clear.
Arguments:
HandleTable - Supplies the handle table containing the entry being unlocked.
HandleTableEntry - Supplies the handle table entry being unlocked.
Return Value:
None.
--*/
{
LONG_PTR NewValue;
LONG_PTR CurrentValue;
PAGED_CODE();
//
// We are about to release a lock. Make sure we are protected from suspension.
//
ASSERT ((KeGetCurrentThread()->KernelApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL));
//
// This routine does not need to loop and attempt the unlock opeation more
// than once because by definition the caller has the entry already locked
// and no one can be changing the value without the lock.
//
// So we'll read in the current value, check that the entry is really
// locked, clear the lock bit and make sure the compare exchange worked.
//
CurrentValue = *((volatile LONG_PTR *)&HandleTableEntry->Object);
NewValue = CurrentValue | EXHANDLE_TABLE_ENTRY_LOCK_BIT;
if ( (CurrentValue == 0) ||
(CurrentValue == NewValue) ) {
KeBugCheckEx( BAD_EXHANDLE, __LINE__, (LONG_PTR)HandleTableEntry, NewValue, CurrentValue );
}
InterlockedExchangePointer (&HandleTableEntry->Object, (PVOID)NewValue);
//
// Unblock any waiters waiting for this table entry.
//
ExUnblockPushLock (&HandleTable->HandleContentionEvent, NULL);
return;
}
NTKERNELAPI
VOID
ExInitializeHandleTablePackage (
VOID
)
/*++
Routine Description:
This routine is called once at system initialization to setup the ex handle
table package
Arguments:
None.
Return Value:
None.
--*/
{
//
// Initialize the handle table synchronization resource and list head
//
InitializeListHead( &HandleTableListHead );
ExInitializeResourceLite( &HandleTableListLock );
return;
}
NTKERNELAPI
PHANDLE_TABLE
ExCreateHandleTable (
IN struct _EPROCESS *Process OPTIONAL
)
/*++
Routine Description:
This function allocate and initialize a new new handle table
Arguments:
Process - Supplies an optional pointer to the process against which quota
will be charged.
Return Value:
If a handle table is successfully created, then the address of the
handle table is returned as the function value. Otherwize, a value
NULL is returned.
--*/
{
PKTHREAD CurrentThread;
PHANDLE_TABLE HandleTable;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
//
// Allocate and initialize a handle table descriptor
//
HandleTable = ExpAllocateHandleTable( Process );
if (HandleTable == NULL) {
return NULL;
}
//
// Insert the handle table in the handle table list.
//
KeEnterCriticalRegionThread (CurrentThread);
ExAcquireResourceExclusiveLite( &HandleTableListLock, TRUE );
InsertTailList( &HandleTableListHead, &HandleTable->HandleTableList );
ExReleaseResourceLite( &HandleTableListLock );
KeLeaveCriticalRegionThread (CurrentThread);
//
// And return to our caller
//
return HandleTable;
}
NTKERNELAPI
VOID
ExRemoveHandleTable (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This function removes the specified exhandle table from the list of
exhandle tables. Used by PS and ATOM packages to make sure their handle
tables are not in the list enumerated by the ExSnapShotHandleTables
routine and the !handle debugger extension.
Arguments:
HandleTable - Supplies a pointer to a handle table
Return Value:
None.
--*/
{
PKTHREAD CurrentThread;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
//
// First, acquire the global handle table lock
//
KeEnterCriticalRegionThread (CurrentThread);
ExAcquireResourceExclusiveLite( &HandleTableListLock, TRUE );
//
// Remove the handle table from the handle table list. This routine is
// written so that multiple calls to remove a handle table will not
// corrupt the system.
//
RemoveEntryList( &HandleTable->HandleTableList );
InitializeListHead( &HandleTable->HandleTableList );
//
// Now release the global lock and return to our caller
//
ExReleaseResourceLite( &HandleTableListLock );
KeLeaveCriticalRegionThread (CurrentThread);
return;
}
NTKERNELAPI
VOID
ExDestroyHandleTable (
IN PHANDLE_TABLE HandleTable,
IN EX_DESTROY_HANDLE_ROUTINE DestroyHandleProcedure OPTIONAL
)
/*++
Routine Description:
This function destroys the specified handle table.
Arguments:
HandleTable - Supplies a pointer to a handle table
DestroyHandleProcedure - Supplies a pointer to a function to call for each
valid handle entry in the handle table.
Return Value:
None.
--*/
{
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
//
// Remove the handle table from the handle table list
//
ExRemoveHandleTable( HandleTable );
//
// Iterate through the handle table and for each handle that is allocated
// we'll invoke the call back. Note that this loop exits when we get a
// null handle table entry. We know there will be no more possible
// entries after the first null one is encountered because we allocate
// memory of the handles in a dense fashion. But first test that we have
// call back to use
//
if (ARGUMENT_PRESENT((ULONG_PTR)DestroyHandleProcedure)) {
for (Handle.Value = 0;
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
Handle.Value += HANDLE_VALUE_INC) {
//
// Only do the callback if the entry is not free
//
if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
(*DestroyHandleProcedure)( Handle.GenericHandleOverlay );
}
}
}
//
// Now free up the handle table memory and return to our caller
//
ExpFreeHandleTable( HandleTable );
return;
}
NTKERNELAPI
VOID
ExSweepHandleTable (
IN PHANDLE_TABLE HandleTable,
IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
IN PVOID EnumParameter
)
/*++
Routine Description:
This function sweeps a handle table in a unsynchronized manner.
Arguments:
HandleTable - Supplies a pointer to a handle table
EnumHandleProcedure - Supplies a pointer to a fucntion to call for
each valid handle in the enumerated handle table.
EnumParameter - Supplies an uninterpreted 32-bit value that is passed
to the EnumHandleProcedure each time it is called.
Return Value:
None.
--*/
{
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
ULONG i;
PAGED_CODE();
//
// Iterate through the handle table and for each handle that is allocated
// we'll invoke the call back. Note that this loop exits when we get a
// null handle table entry. We know there will be no more possible
// entries after the first null one is encountered because we allocate
// memory of the handles in a dense fashion.
//
Handle.Value = HANDLE_VALUE_INC;
while ((HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL) {
for (i = 1; i < LOWLEVEL_COUNT; i++) {
//
// Only do the callback if the entry is not free
//
if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
(*EnumHandleProcedure)( HandleTableEntry,
Handle.GenericHandleOverlay,
EnumParameter );
}
Handle.Value += HANDLE_VALUE_INC;
HandleTableEntry++;
}
// Skip past the first entry that's not a real entry
Handle.Value += HANDLE_VALUE_INC;
}
return;
}
NTKERNELAPI
BOOLEAN
ExEnumHandleTable (
IN PHANDLE_TABLE HandleTable,
IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
IN PVOID EnumParameter,
OUT PHANDLE Handle OPTIONAL
)
/*++
Routine Description:
This function enumerates all the valid handles in a handle table.
For each valid handle in the handle table, the specified eumeration
function is called. If the enumeration function returns TRUE, then
the enumeration is stopped, the current handle is returned to the
caller via the optional Handle parameter, and this function returns
TRUE to indicated that the enumeration stopped at a specific handle.
Arguments:
HandleTable - Supplies a pointer to a handle table.
EnumHandleProcedure - Supplies a pointer to a fucntion to call for
each valid handle in the enumerated handle table.
EnumParameter - Supplies an uninterpreted 32-bit value that is passed
to the EnumHandleProcedure each time it is called.
Handle - Supplies an optional pointer a variable that receives the
Handle value that the enumeration stopped at. Contents of the
variable only valid if this function returns TRUE.
Return Value:
If the enumeration stopped at a specific handle, then a value of TRUE
is returned. Otherwise, a value of FALSE is returned.
--*/
{
PKTHREAD CurrentThread;
BOOLEAN ResultValue;
EXHANDLE LocalHandle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
//
// Our initial return value is false until the enumeration callback
// function tells us otherwise
//
ResultValue = FALSE;
//
// Iterate through the handle table and for each handle that is
// allocated we'll invoke the call back. Note that this loop exits
// when we get a null handle table entry. We know there will be no
// more possible entries after the first null one is encountered
// because we allocate memory for the handles in a dense fashion
//
KeEnterCriticalRegionThread (CurrentThread);
for (LocalHandle.Value = 0; // does essentially the following "LocalHandle.Index = 0, LocalHandle.TagBits = 0;"
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, LocalHandle )) != NULL;
LocalHandle.Value += HANDLE_VALUE_INC) {
//
// Only do the callback if the entry is not free
//
if ( ExpIsValidObjectEntry( HandleTableEntry ) ) {
//
// Lock the handle table entry because we're about to give
// it to the callback function, then release the entry
// right after the call back.
//
if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
//
// Invoke the callback, and if it returns true then set
// the proper output values and break out of the loop.
//
ResultValue = (*EnumHandleProcedure)( HandleTableEntry,
LocalHandle.GenericHandleOverlay,
EnumParameter );
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
if (ResultValue) {
if (ARGUMENT_PRESENT( Handle )) {
*Handle = LocalHandle.GenericHandleOverlay;
}
break;
}
}
}
}
KeLeaveCriticalRegionThread (CurrentThread);
return ResultValue;
}
NTKERNELAPI
PHANDLE_TABLE
ExDupHandleTable (
IN struct _EPROCESS *Process OPTIONAL,
IN PHANDLE_TABLE OldHandleTable,
IN EX_DUPLICATE_HANDLE_ROUTINE DupHandleProcedure OPTIONAL
)
/*++
Routine Description:
This function creates a duplicate copy of the specified handle table.
Arguments:
Process - Supplies an optional to the process to charge quota to.
OldHandleTable - Supplies a pointer to a handle table.
DupHandleProcedure - Supplies an optional pointer to a function to call
for each valid handle in the duplicated handle table.
Return Value:
If the specified handle table is successfully duplicated, then the
address of the new handle table is returned as the function value.
Otherwize, a value NULL is returned.
--*/
{
PKTHREAD CurrentThread;
PHANDLE_TABLE NewHandleTable;
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY OldHandleTableEntry;
PHANDLE_TABLE_ENTRY NewHandleTableEntry;
BOOLEAN FreeEntry;
ULONG i;
NTSTATUS Status;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
//
// First allocate a new handle table. If this fails then
// return immediately to our caller
//
NewHandleTable = ExpAllocateHandleTable( Process );
if (NewHandleTable == NULL) {
return NULL;
}
//
// Now we'll build up the new handle table. We do this by calling
// allocating new handle table entries, and "fooling" the worker
// routine to allocate keep on allocating until the next free
// index needing pool are equal
//
while (NewHandleTable->NextHandleNeedingPool < OldHandleTable->NextHandleNeedingPool) {
//
// Call the worker routine to grow the new handle table. If
// not successful then free the new table as far as we got,
// set our output variable and exit out here
//
if (!ExpAllocateHandleTableEntrySlow (NewHandleTable)) {
ExpFreeHandleTable (NewHandleTable);
return NULL;
}
}
//
// Now modify the new handle table to think it has zero handles
// and set its free list to start on the same index as the old
// free list
//
NewHandleTable->HandleCount = 0;
NewHandleTable->ExtraInfoPages = 0;
NewHandleTable->FirstFree = 0;
//
// Now for every valid index value we'll copy over the old entry into
// the new entry
//
Handle.Value = HANDLE_VALUE_INC;
KeEnterCriticalRegionThread (CurrentThread);
while ((NewHandleTableEntry = ExpLookupHandleTableEntry( NewHandleTable, Handle )) != NULL) {
//
// Lookup the old entry. If it was being expanded then the old one
// might not be there but we expanded the new table this far.
//
OldHandleTableEntry = ExpLookupHandleTableEntry( OldHandleTable, Handle );
for (i = 1; i < LOWLEVEL_COUNT; i++) {
//
// If the old entry is free then simply copy over the entire
// old entry to the new entry. The lock command will tell us
// if the entry is free.
//
if (OldHandleTableEntry == NULL || OldHandleTableEntry->Object == NULL ||
!ExpLockHandleTableEntry( OldHandleTable, OldHandleTableEntry )) {
FreeEntry = TRUE;
} else {
PHANDLE_TABLE_ENTRY_INFO EntryInfo;
//
// Otherwise we have a non empty entry. So now copy it
// over, and unlock the old entry. In both cases we bump
// the handle count because either the entry is going into
// the new table or we're going to remove it with Exp Free
// Handle Table Entry which will decrement the handle count
//
*NewHandleTableEntry = *OldHandleTableEntry;
//
// Copy the entry info data, if any
//
Status = STATUS_SUCCESS;
EntryInfo = ExGetHandleInfo(OldHandleTable, Handle.GenericHandleOverlay, TRUE);
if (EntryInfo) {
Status = ExSetHandleInfo(NewHandleTable, Handle.GenericHandleOverlay, EntryInfo, TRUE);
}
//
// Invoke the callback and if it returns true then we
// unlock the new entry
//
if (NT_SUCCESS (Status)) {
if ((*DupHandleProcedure) (Process,
OldHandleTable,
OldHandleTableEntry,
NewHandleTableEntry)) {
ExUnlockHandleTableEntry (NewHandleTable, NewHandleTableEntry);
NewHandleTable->HandleCount += 1;
FreeEntry = FALSE;
} else {
if (EntryInfo) {
EntryInfo->AuditMask = 0;
}
FreeEntry = TRUE;
}
} else {
//
// Duplicate routine doesn't want this handle duplicated so free it
//
ExUnlockHandleTableEntry( OldHandleTable, OldHandleTableEntry );
FreeEntry = TRUE;
}
}
if (FreeEntry) {
NewHandleTableEntry->Object = NULL;
NewHandleTableEntry->NextFreeTableEntry =
NewHandleTable->FirstFree;
NewHandleTable->FirstFree = (ULONG) Handle.Value;
}
Handle.Index++;;
NewHandleTableEntry++;
if (OldHandleTableEntry != NULL) {
OldHandleTableEntry++;
}
}
Handle.Value += HANDLE_VALUE_INC; // Skip past the first entry thats not a real entry
}
//
// Insert the handle table in the handle table list.
//
ExAcquireResourceExclusiveLite( &HandleTableListLock, TRUE );
InsertTailList( &HandleTableListHead, &NewHandleTable->HandleTableList );
ExReleaseResourceLite( &HandleTableListLock );
KeLeaveCriticalRegionThread (CurrentThread);
//
// lastly return the new handle table to our caller
//
return NewHandleTable;
}
NTKERNELAPI
NTSTATUS
ExSnapShotHandleTables (
IN PEX_SNAPSHOT_HANDLE_ENTRY SnapShotHandleEntry,
IN OUT PSYSTEM_HANDLE_INFORMATION HandleInformation,
IN ULONG Length,
IN OUT PULONG RequiredLength
)
/*++
Routine Description:
This function visits and invokes the specified callback for every valid
handle that it can find off of the handle table.
Arguments:
SnapShotHandleEntry - Supplies a pointer to a function to call for
each valid handle we encounter.
HandleInformation - Supplies a handle information structure to
be filled in for each handle table we encounter. This routine
fills in the handle count, but relies on a callback to fill in
entry info fields.
Length - Supplies a parameter for the callback. In reality this is
the total size, in bytes, of the Handle Information buffer.
RequiredLength - Supplies a parameter for the callback. In reality
this is a final size in bytes used to store the requested
information.
Return Value:
The last return status of the callback
--*/
{
NTSTATUS Status;
PKTHREAD CurrentThread;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO HandleEntryInfo;
PLIST_ENTRY NextEntry;
PHANDLE_TABLE HandleTable;
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
Status = STATUS_SUCCESS;
//
// Setup the output buffer pointer that the callback will maintain
//
HandleEntryInfo = &HandleInformation->Handles[0];
//
// Zero out the handle count
//
HandleInformation->NumberOfHandles = 0;
//
// Lock the handle table list exclusive and traverse the list of handle
// tables.
//
KeEnterCriticalRegionThread (CurrentThread);
ExAcquireResourceExclusiveLite( &HandleTableListLock, TRUE );
//
// Iterate through all the handle tables in the system.
//
for (NextEntry = HandleTableListHead.Flink;
NextEntry != &HandleTableListHead;
NextEntry = NextEntry->Flink) {
//
// Get the address of the next handle table, lock the handle
// table exclusive, and scan the list of handle entries.
//
HandleTable = CONTAINING_RECORD( NextEntry,
HANDLE_TABLE,
HandleTableList );
// Iterate through the handle table and for each handle that
// is allocated we'll invoke the call back. Note that this
// loop exits when we get a null handle table entry. We know
// there will be no more possible entries after the first null
// one is encountered because we allocate memory of the
// handles in a dense fashion
//
for (Handle.Value = 0;
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
Handle.Value += HANDLE_VALUE_INC) {
//
// Only do the callback if the entry is not free
//
if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
//
// Increment the handle count information in the
// information buffer
//
HandleInformation->NumberOfHandles += 1;
//
// Lock the handle table entry because we're about to
// give it to the callback function, then release the
// entry right after the call back.
//
if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
Status = (*SnapShotHandleEntry)( &HandleEntryInfo,
HandleTable->UniqueProcessId,
HandleTableEntry,
Handle.GenericHandleOverlay,
Length,
RequiredLength );
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
}
}
}
}
ExReleaseResourceLite( &HandleTableListLock );
KeLeaveCriticalRegionThread (CurrentThread);
return Status;
}
NTKERNELAPI
NTSTATUS
ExSnapShotHandleTablesEx (
IN PEX_SNAPSHOT_HANDLE_ENTRY_EX SnapShotHandleEntry,
IN OUT PSYSTEM_HANDLE_INFORMATION_EX HandleInformation,
IN ULONG Length,
IN OUT PULONG RequiredLength
)
/*++
Routine Description:
This function visits and invokes the specified callback for every valid
handle that it can find off of the handle table.
Arguments:
SnapShotHandleEntry - Supplies a pointer to a function to call for
each valid handle we encounter.
HandleInformation - Supplies a handle information structure to
be filled in for each handle table we encounter. This routine
fills in the handle count, but relies on a callback to fill in
entry info fields.
Length - Supplies a parameter for the callback. In reality this is
the total size, in bytes, of the Handle Information buffer.
RequiredLength - Supplies a parameter for the callback. In reality
this is a final size in bytes used to store the requested
information.
Return Value:
The last return status of the callback
--*/
{
NTSTATUS Status;
PKTHREAD CurrentThread;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntryInfo;
PLIST_ENTRY NextEntry;
PHANDLE_TABLE HandleTable;
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
CurrentThread = KeGetCurrentThread ();
Status = STATUS_SUCCESS;
//
// Setup the output buffer pointer that the callback will maintain
//
HandleEntryInfo = &HandleInformation->Handles[0];
//
// Zero out the handle count
//
HandleInformation->NumberOfHandles = 0;
//
// Lock the handle table list exclusive and traverse the list of handle
// tables.
//
KeEnterCriticalRegionThread (CurrentThread);
ExAcquireResourceExclusiveLite( &HandleTableListLock, TRUE );
//
// Iterate through all the handle tables in the system.
//
for (NextEntry = HandleTableListHead.Flink;
NextEntry != &HandleTableListHead;
NextEntry = NextEntry->Flink) {
//
// Get the address of the next handle table, lock the handle
// table exclusive, and scan the list of handle entries.
//
HandleTable = CONTAINING_RECORD( NextEntry,
HANDLE_TABLE,
HandleTableList );
// Iterate through the handle table and for each handle that
// is allocated we'll invoke the call back. Note that this
// loop exits when we get a null handle table entry. We know
// there will be no more possible entries after the first null
// one is encountered because we allocate memory of the
// handles in a dense fashion
//
for (Handle.Value = 0;
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
Handle.Value += HANDLE_VALUE_INC) {
//
// Only do the callback if the entry is not free
//
if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
//
// Increment the handle count information in the
// information buffer
//
HandleInformation->NumberOfHandles += 1;
//
// Lock the handle table entry because we're about to
// give it to the callback function, then release the
// entry right after the call back.
//
if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
Status = (*SnapShotHandleEntry)( &HandleEntryInfo,
HandleTable->UniqueProcessId,
HandleTableEntry,
Handle.GenericHandleOverlay,
Length,
RequiredLength );
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
}
}
}
}
ExReleaseResourceLite( &HandleTableListLock );
KeLeaveCriticalRegionThread (CurrentThread);
return Status;
}
NTKERNELAPI
HANDLE
ExCreateHandle (
IN PHANDLE_TABLE HandleTable,
IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
/*++
Routine Description:
This function creates a handle entry in the specified handle table and
returns a handle for the entry.
Arguments:
HandleTable - Supplies a pointer to a handle table
HandleEntry - Supplies a poiner to the handle entry for which a
handle entry is created.
Return Value:
If the handle entry is successfully created, then value of the created
handle is returned as the function value. Otherwise, a value of zero is
returned.
--*/
{
EXHANDLE Handle;
PETHREAD CurrentThread;
PHANDLE_TABLE_ENTRY NewHandleTableEntry;
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
PHANDLE_TRACE_DB_ENTRY DebugEntry;
ULONG Index;
PAGED_CODE();
//
// Set out output variable to zero (i.e., null) before going on
//
//
// Clears Handle.Index and Handle.TagBits
//
Handle.GenericHandleOverlay = NULL;
//
// Allocate a new handle table entry, and get the handle value
//
NewHandleTableEntry = ExpAllocateHandleTableEntry( HandleTable,
&Handle );
//
// If we really got a handle then copy over the template and unlock
// the entry
//
if (NewHandleTableEntry != NULL) {
CurrentThread = PsGetCurrentThread ();
//
// We are about to create a locked entry so protect against suspension
//
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
*NewHandleTableEntry = *HandleTableEntry;
//
// If we are debugging handle operations then save away the details
//
DebugInfo = HandleTable->DebugInfo;
if (DebugInfo != NULL) {
Index = ((ULONG) InterlockedIncrement ((PLONG)&DebugInfo->CurrentStackIndex))
% HANDLE_TRACE_DB_MAX_STACKS;
DebugEntry = &DebugInfo->TraceDb[Index];
DebugEntry->ClientId = CurrentThread->Cid;
DebugEntry->Handle = Handle.GenericHandleOverlay;
DebugEntry->Type = HANDLE_TRACE_DB_OPEN;
Index = RtlWalkFrameChain (DebugEntry->StackTrace, HANDLE_TRACE_DB_STACK_SIZE, 0);
RtlWalkFrameChain (&DebugEntry->StackTrace[Index], HANDLE_TRACE_DB_STACK_SIZE - Index, 1);
}
ExUnlockHandleTableEntry( HandleTable, NewHandleTableEntry );
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
}
return Handle.GenericHandleOverlay;
}
NTKERNELAPI
BOOLEAN
ExDestroyHandle (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle,
IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL
)
/*++
Routine Description:
This function removes a handle from a handle table.
Arguments:
HandleTable - Supplies a pointer to a handle table
Handle - Supplies the handle value of the entry to remove.
HandleTableEntry - Optionally supplies a pointer to the handle
table entry being destroyed. If supplied the entry is
assume to be locked.
Return Value:
If the specified handle is successfully removed, then a value of
TRUE is returned. Otherwise, a value of FALSE is returned.
--*/
{
EXHANDLE LocalHandle;
PETHREAD CurrentThread;
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
PHANDLE_TRACE_DB_ENTRY DebugEntry;
ULONG Index;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
CurrentThread = PsGetCurrentThread ();
//
// If the caller did not supply the optional handle table entry then
// locate the entry via the supplied handle, make sure it is real, and
// then lock the entry.
//
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
if (HandleTableEntry == NULL) {
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if (!ExpIsValidObjectEntry(HandleTableEntry)) {
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
return FALSE;
}
if (!ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
return FALSE;
}
}
//
// If we are debugging handle operations then save away the details
//
DebugInfo = HandleTable->DebugInfo;
if (DebugInfo != NULL) {
Index = ((ULONG) InterlockedIncrement ((PLONG)&DebugInfo->CurrentStackIndex))
% HANDLE_TRACE_DB_MAX_STACKS;
DebugEntry = &DebugInfo->TraceDb[Index];
DebugEntry->ClientId = CurrentThread->Cid;
DebugEntry->Handle = Handle;
DebugEntry->Type = HANDLE_TRACE_DB_CLOSE;
Index = RtlWalkFrameChain (DebugEntry->StackTrace, HANDLE_TRACE_DB_STACK_SIZE, 0);
RtlWalkFrameChain (&DebugEntry->StackTrace[Index], HANDLE_TRACE_DB_STACK_SIZE - Index, 1);
}
//
// At this point we have a locked handle table entry. Now mark it free
// which does the implicit unlock. The system will not allocate it
// again until we add it to the free list which we will do right after
// we take out the lock
//
InterlockedExchangePointer (&HandleTableEntry->Object, NULL);
//
// Unblock any waiters waiting for this table entry.
//
ExUnblockPushLock (&HandleTable->HandleContentionEvent, NULL);
ExpFreeHandleTableEntry( HandleTable,
LocalHandle,
HandleTableEntry );
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
return TRUE;
}
NTKERNELAPI
BOOLEAN
ExChangeHandle (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle,
IN PEX_CHANGE_HANDLE_ROUTINE ChangeRoutine,
IN ULONG_PTR Parameter
)
/*++
Routine Description:
This function provides the capability to change the contents of the
handle entry corrsponding to the specified handle.
Arguments:
HandleTable - Supplies a pointer to a handle table.
Handle - Supplies the handle for the handle entry that is changed.
ChangeRoutine - Supplies a pointer to a function that is called to
perform the change.
Parameter - Supplies an uninterpreted parameter that is passed to
the change routine.
Return Value:
If the operation was successfully performed, then a value of TRUE
is returned. Otherwise, a value of FALSE is returned.
--*/
{
EXHANDLE LocalHandle;
PKTHREAD CurrentThread;
PHANDLE_TABLE_ENTRY HandleTableEntry;
BOOLEAN ReturnValue;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
CurrentThread = KeGetCurrentThread ();
//
// Translate the input handle to a handle table entry and make
// sure it is a valid handle.
//
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if ((HandleTableEntry == NULL) ||
!ExpIsValidObjectEntry(HandleTableEntry)) {
return FALSE;
}
//
// Try and lock the handle table entry, If this fails then that's
// because someone freed the handle
//
//
// Make sure we can't get suspended and then invoke the callback
//
KeEnterCriticalRegionThread (CurrentThread);
if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
ReturnValue = (*ChangeRoutine)( HandleTableEntry, Parameter );
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
} else {
ReturnValue = FALSE;
}
KeLeaveCriticalRegionThread (CurrentThread);
return ReturnValue;
}
NTKERNELAPI
PHANDLE_TABLE_ENTRY
ExMapHandleToPointer (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle
)
/*++
Routine Description:
This function maps a handle to a pointer to a handle table entry. If the
map operation is successful then the handle table entry is locked when
we return.
Arguments:
HandleTable - Supplies a pointer to a handle table.
Handle - Supplies the handle to be mapped to a handle entry.
Return Value:
If the handle was successfully mapped to a pointer to a handle entry,
then the address of the handle table entry is returned as the function
value with the entry locked. Otherwise, a value of NULL is returned.
--*/
{
EXHANDLE LocalHandle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
PHANDLE_TRACE_DB_ENTRY DebugEntry;
ULONG Index;
PETHREAD CurrentThread;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
if ((LocalHandle.Index & (LOWLEVEL_COUNT - 1)) == 0) {
return NULL;
}
//
// Translate the input handle to a handle table entry and make
// sure it is a valid handle.
//
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if ((HandleTableEntry == NULL) ||
!ExpLockHandleTableEntry( HandleTable, HandleTableEntry)) {
//
// If we are debugging handle operations then save away the details
//
DebugInfo = HandleTable->DebugInfo;
if (DebugInfo != NULL) {
Index = ((ULONG) InterlockedIncrement ((PLONG)&DebugInfo->CurrentStackIndex))
% HANDLE_TRACE_DB_MAX_STACKS;
DebugEntry = &DebugInfo->TraceDb[Index];
CurrentThread = PsGetCurrentThread ();
DebugEntry->ClientId = CurrentThread->Cid;
DebugEntry->Handle = Handle;
DebugEntry->Type = HANDLE_TRACE_DB_BADREF;
Index = RtlWalkFrameChain (DebugEntry->StackTrace, HANDLE_TRACE_DB_STACK_SIZE, 0);
RtlWalkFrameChain (&DebugEntry->StackTrace[Index], HANDLE_TRACE_DB_STACK_SIZE - Index, 1);
}
return NULL;
}
//
// Return the locked valid handle table entry
//
return HandleTableEntry;
}
NTKERNELAPI
PHANDLE_TABLE_ENTRY
ExMapHandleToPointerEx (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle,
IN KPROCESSOR_MODE PreviousMode
)
/*++
Routine Description:
This function maps a handle to a pointer to a handle table entry. If the
map operation is successful then the handle table entry is locked when
we return.
Arguments:
HandleTable - Supplies a pointer to a handle table.
Handle - Supplies the handle to be mapped to a handle entry.
PreviousMode - Previous mode of caller
Return Value:
If the handle was successfully mapped to a pointer to a handle entry,
then the address of the handle table entry is returned as the function
value with the entry locked. Otherwise, a value of NULL is returned.
--*/
{
EXHANDLE LocalHandle;
PHANDLE_TABLE_ENTRY HandleTableEntry = NULL;
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
PHANDLE_TRACE_DB_ENTRY DebugEntry;
ULONG Index;
PETHREAD CurrentThread;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
//
// Translate the input handle to a handle table entry and make
// sure it is a valid handle.
//
if (((LocalHandle.Index & (LOWLEVEL_COUNT - 1)) == 0) ||
((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, LocalHandle)) == NULL) ||
!ExpLockHandleTableEntry( HandleTable, HandleTableEntry)) {
//
// If we are debugging handle operations then save away the details
//
DebugInfo = HandleTable->DebugInfo;
if (DebugInfo != NULL) {
Index = ((ULONG) InterlockedIncrement ((PLONG)&DebugInfo->CurrentStackIndex))
% HANDLE_TRACE_DB_MAX_STACKS;
CurrentThread = PsGetCurrentThread ();
DebugEntry = &DebugInfo->TraceDb[Index];
DebugEntry->ClientId = CurrentThread->Cid;
DebugEntry->Handle = Handle;
DebugEntry->Type = HANDLE_TRACE_DB_BADREF;
Index = RtlWalkFrameChain (DebugEntry->StackTrace, HANDLE_TRACE_DB_STACK_SIZE, 0);
RtlWalkFrameChain (&DebugEntry->StackTrace[Index], HANDLE_TRACE_DB_STACK_SIZE - Index, 1);
//
// Since we have a non-null DebugInfo for the handle table of this
// process it means application verifier was enabled for this process.
//
if (PreviousMode == UserMode) {
if (!KeIsAttachedProcess() && !PsIsThreadTerminating (CurrentThread) &&
!CurrentThread->Tcb.ApcState.KernelApcInProgress) {
//
// If the current process is marked for verification
// then we will raise an exception in user mode. In case
// application verifier is enabled system wide we will
// break first.
//
if ((NtGlobalFlag & FLG_APPLICATION_VERIFIER)) {
DbgPrint ("AVRF: Invalid handle %p in process %p \n",
Handle,
PsGetCurrentProcess());
// DbgBreakPoint ();
}
KeRaiseUserException (STATUS_INVALID_HANDLE);
}
} else {
//
// We bugcheck for kernel handles only if we have the handle
// exceptions flag set system-wide. This way a user enabling
// application verifier for a process will not get bugchecks
// only user mode errors.
//
if ((NtGlobalFlag & FLG_ENABLE_HANDLE_EXCEPTIONS)) {
KeBugCheckEx(INVALID_KERNEL_HANDLE,
(ULONG_PTR)Handle,
(ULONG_PTR)HandleTable,
(ULONG_PTR)HandleTableEntry,
0x1);
}
}
}
return NULL;
}
//
// Return the locked valid handle table entry
//
return HandleTableEntry;
}
//
// Local Support Routine
//
PVOID
ExpAllocateTablePagedPool (
IN PEPROCESS QuotaProcess OPTIONAL,
IN SIZE_T NumberOfBytes
)
{
PVOID PoolMemory;
PoolMemory = ExAllocatePoolWithTag( PagedPool,
NumberOfBytes,
'btbO' );
if (PoolMemory != NULL) {
RtlZeroMemory( PoolMemory,
NumberOfBytes );
if (ARGUMENT_PRESENT(QuotaProcess)) {
if (!NT_SUCCESS (PsChargeProcessPagedPoolQuota ( QuotaProcess,
NumberOfBytes ))) {
ExFreePool( PoolMemory );
PoolMemory = NULL;
}
}
}
return PoolMemory;
}
//
// Local Support Routine
//
VOID
ExpFreeTablePagedPool (
IN PEPROCESS QuotaProcess OPTIONAL,
IN PVOID PoolMemory,
IN SIZE_T NumberOfBytes
)
{
ExFreePool( PoolMemory );
if ( QuotaProcess ) {
PsReturnProcessPagedPoolQuota( QuotaProcess,
NumberOfBytes
);
}
}
NTKERNELAPI
NTSTATUS
ExEnableHandleTracing (
IN PHANDLE_TABLE HandleTable
)
{
PHANDLE_TRACE_DEBUG_INFO DebugInfo;
PEPROCESS Process;
NTSTATUS Status;
SIZE_T TotalNow;
extern SIZE_T MmMaximumNonPagedPoolInBytes;
TotalNow = InterlockedIncrement ((PLONG) &TotalTraceBuffers);
//
// See if we used more than 30% of nonpaged pool.
//
if (TotalNow * sizeof (HANDLE_TRACE_DEBUG_INFO) > (MmMaximumNonPagedPoolInBytes * 30 / 100)) {
InterlockedDecrement ((PLONG) &TotalTraceBuffers);
return STATUS_INSUFFICIENT_RESOURCES;
}
Process = HandleTable->QuotaProcess;
if (Process) {
Status = PsChargeProcessNonPagedPoolQuota (Process,
sizeof (HANDLE_TRACE_DEBUG_INFO));
if (!NT_SUCCESS (Status)) {
InterlockedDecrement ((PLONG) &TotalTraceBuffers);
return Status;
}
}
//
// Allocate the handle debug database
//
DebugInfo = ExAllocatePoolWithTag (NonPagedPool,
sizeof (HANDLE_TRACE_DEBUG_INFO),
'dtbO');
if (DebugInfo == NULL) {
if (Process) {
PsReturnProcessNonPagedPoolQuota (Process,
sizeof (HANDLE_TRACE_DEBUG_INFO));
}
InterlockedDecrement ((PLONG) &TotalTraceBuffers);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory (DebugInfo, sizeof (HANDLE_TRACE_DEBUG_INFO));
//
// Since we are tracing then we should enforce strict FIFO
// Only do this for tables with processes so we leave atom tables alone.
//
if (Process != NULL) {
HandleTable->StrictFIFO = TRUE;
}
//
// Try and install the trace buffer. If there is one already there
// then free the new one. We return success anyway as tracing is enabled.
//
if (InterlockedCompareExchangePointer (&HandleTable->DebugInfo,
DebugInfo,
NULL) != NULL) {
ExFreePool (DebugInfo);
if (Process) {
PsReturnProcessNonPagedPoolQuota (Process,
sizeof (HANDLE_TRACE_DEBUG_INFO));
}
InterlockedDecrement ((PLONG) &TotalTraceBuffers);
}
return STATUS_SUCCESS;
}
//
// Local Support Routine
//
PHANDLE_TABLE
ExpAllocateHandleTable (
IN PEPROCESS Process OPTIONAL
)
/*++
Routine Description:
This worker routine will allocate and initialize a new handle table
structure. The new structure consists of the basic handle table
struct plus the first allocation needed to store handles. This is
really one page divided up into the top level node, the first mid
level node, and one bottom level node.
Arguments:
Process - Optionally supplies the process to charge quota for the
handle table
Return Value:
A pointer to the new handle table or NULL if unsuccessful at getting
pool.
--*/
{
PHANDLE_TABLE HandleTable;
PHANDLE_TABLE_ENTRY HandleTableTable;
ULONG i, Idx;
PAGED_CODE();
//
// If any alloation or quota failures happen we will catch it in the
// following try-except clause and cleanup after outselves before
// we return null
//
//
// First allocate the handle table, make sure we got one, charge quota
// for it and then zero it out
//
HandleTable = (PHANDLE_TABLE)ExAllocatePoolWithTag (PagedPool,
sizeof(HANDLE_TABLE),
'btbO');
if (HandleTable == NULL) {
return NULL;
}
if (ARGUMENT_PRESENT(Process)) {
if (!NT_SUCCESS (PsChargeProcessPagedPoolQuota( Process,
sizeof(HANDLE_TABLE)))) {
ExFreePool( HandleTable );
return NULL;
}
}
RtlZeroMemory( HandleTable, sizeof(HANDLE_TABLE) );
//
// Now allocate space of the top level, one mid level and one bottom
// level table structure. This will all fit on a page, maybe two.
//
HandleTableTable = ExpAllocateTablePagedPool( Process,
TABLE_PAGE_SIZE
);
if ( HandleTableTable == NULL ) {
ExFreePool( HandleTable );
if (ARGUMENT_PRESENT(Process)) {
PsReturnProcessPagedPoolQuota (Process,
sizeof(HANDLE_TABLE));
}
return NULL;
}
HandleTable->TableCode = (ULONG_PTR)HandleTableTable;
//
// Now setup the free list. We do this by chaining together the free
// entries such that each free entry give the next free index (i.e.,
// like a fat chain). The chain is terminated with a 0. Note that
// we'll skip handle zero because our callers will get that value
// confused with null.
//
for (i = 0; i < LOWLEVEL_COUNT; i += 1) {
HandleTableTable[i].NextFreeTableEntry = (i+1)<<2;
}
//
// We stamp with EX_ADDITIONAL_INFO_SIGNATURE to recognize in the future this
// is a special information entry
//
HandleTableTable[0].NextFreeTableEntry = EX_ADDITIONAL_INFO_SIGNATURE;
HandleTableTable[LOWLEVEL_COUNT - 1].NextFreeTableEntry = 0;
HandleTable->FirstFree = HANDLE_VALUE_INC;
HandleTable->NextHandleNeedingPool = LOWLEVEL_COUNT * HANDLE_VALUE_INC;
//
// Setup the necessary process information
//
HandleTable->QuotaProcess = Process;
HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId;
HandleTable->Flags = 0;
#if DBG
if (Process != NULL) {
HandleTable->StrictFIFO = TRUE;
}
#endif
//
// Initialize the handle table lock. This is only used by table expansion.
//
for (Idx = 0; Idx < HANDLE_TABLE_LOCKS; Idx++) {
ExInitializePushLock (&HandleTable->HandleTableLock[Idx]);
}
//
// Initialize the blocker for handle entry lock contention.
//
ExInitializePushLock (&HandleTable->HandleContentionEvent);
if (ExTraceAllTables) {
ExEnableHandleTracing (HandleTable);
}
//
// And return to our caller
//
return HandleTable;
}
//
// Local Support Routine
//
VOID
ExpFreeLowLevelTable (
IN PEPROCESS QuotaProcess,
IN PHANDLE_TABLE_ENTRY TableLevel1
)
/*++
Routine Description:
This worker routine frees a low-level handle table
and the additional info memory, if any.
Arguments:
HandleTable - Supplies the handle table being freed
Return Value:
None.
--*/
{
//
// Check whether we have a pool allocated for the additional info
//
if (TableLevel1[0].Object) {
ExpFreeTablePagedPool( QuotaProcess,
TableLevel1[0].Object,
LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO)
);
}
//
// Now free the low level table and return the quota for the process
//
ExpFreeTablePagedPool( QuotaProcess,
TableLevel1,
TABLE_PAGE_SIZE
);
//
// And return to our caller
//
return;
}
//
// Local Support Routine
//
VOID
ExpFreeHandleTable (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This worker routine tears down and frees the specified handle table.
Arguments:
HandleTable - Supplies the handle table being freed
Return Value:
None.
--*/
{
PEPROCESS Process;
ULONG i,j;
ULONG_PTR CapturedTable = HandleTable->TableCode;
ULONG TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
PAGED_CODE();
//
// Unmask the level bits
//
CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;
Process = HandleTable->QuotaProcess;
//
// We need to free all pages. We have 3 cases, depending on the number
// of levels
//
if (TableLevel == 0) {
//
// There is a single level handle table. We'll simply free the buffer
//
PHANDLE_TABLE_ENTRY TableLevel1 = (PHANDLE_TABLE_ENTRY)CapturedTable;
ExpFreeLowLevelTable( Process, TableLevel1 );
} else if (TableLevel == 1) {
//
// We have 2 levels in the handle table
//
PHANDLE_TABLE_ENTRY *TableLevel2 = (PHANDLE_TABLE_ENTRY *)CapturedTable;
for (i = 0; i < MIDLEVEL_COUNT; i++) {
//
// loop through the pointers to the low-level tables, and free each one
//
if (TableLevel2[i] == NULL) {
break;
}
ExpFreeLowLevelTable( Process, TableLevel2[i] );
}
//
// Free the top level table
//
ExpFreeTablePagedPool( Process,
TableLevel2,
PAGE_SIZE
);
} else {
//
// Here we handle the case where we have a 3 level handle table
//
PHANDLE_TABLE_ENTRY **TableLevel3 = (PHANDLE_TABLE_ENTRY **)CapturedTable;
//
// Iterates through the high-level pointers to mid-table
//
for (i = 0; i < HIGHLEVEL_COUNT; i++) {
if (TableLevel3[i] == NULL) {
break;
}
//
// Iterate through the mid-level table
// and free every low-level page
//
for (j = 0; j < MIDLEVEL_COUNT; j++) {
if (TableLevel3[i][j] == NULL) {
break;
}
ExpFreeLowLevelTable( Process, TableLevel3[i][j] );
}
ExpFreeTablePagedPool( Process,
TableLevel3[i],
PAGE_SIZE
);
}
//
// Free the top-level array
//
ExpFreeTablePagedPool( Process,
TableLevel3,
HIGHLEVEL_SIZE
);
}
//
// Free any debug info if we have any.
//
if (HandleTable->DebugInfo != NULL) {
ExFreePool (HandleTable->DebugInfo);
if (Process != NULL) {
PsReturnProcessNonPagedPoolQuota (Process,
sizeof (HANDLE_TRACE_DEBUG_INFO));
}
InterlockedDecrement ((PLONG) &TotalTraceBuffers);
}
//
// Finally deallocate the handle table itself
//
ExFreePool( HandleTable );
if (Process != NULL) {
PsReturnProcessPagedPoolQuota (Process,
sizeof(HANDLE_TABLE));
}
//
// And return to our caller
//
return;
}
//
// Local Support Routine
//
PHANDLE_TABLE_ENTRY
ExpAllocateLowLevelTable (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This worker routine allocates a new low level table
Note: The caller must have already locked the handle table
Arguments:
HandleTable - Supplies the handle table being used
Return Value:
Returns - a pointer to a low-level table if allocation is
successful otherwise the return value is null.
--*/
{
ULONG k;
PHANDLE_TABLE_ENTRY NewLowLevel = NULL;
ULONG BaseHandle;
//
// Allocate the pool for lower level
//
NewLowLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
TABLE_PAGE_SIZE
);
if (NewLowLevel == NULL) {
return NULL;
}
//
// We stamp with EX_ADDITIONAL_INFO_SIGNATURE to recognize in the future this
// is a special information entry
//
NewLowLevel[0].NextFreeTableEntry = EX_ADDITIONAL_INFO_SIGNATURE;
//
// Now add the new entries to the free list. To do this we
// chain the new free entries together. We are guaranteed to
// have at least one new buffer. The second buffer we need
// to check for.
//
// We reserve the first entry in the table to the structure with
// additional info
//
//
// Do the guaranteed first buffer
//
BaseHandle = HandleTable->NextHandleNeedingPool + 2 * HANDLE_VALUE_INC;
for (k = 1; k < LOWLEVEL_COUNT - 1; k++) {
NewLowLevel[k].NextFreeTableEntry = BaseHandle;
BaseHandle += HANDLE_VALUE_INC;
}
return NewLowLevel;
}
PHANDLE_TABLE_ENTRY *
ExpAllocateMidLevelTable (
IN PHANDLE_TABLE HandleTable,
OUT PHANDLE_TABLE_ENTRY *pNewLowLevel
)
/*++
Routine Description:
This worker routine allocates a mid-level table. This is an array with
pointers to low-level tables.
It will allocate also a low-level table and will save it in the first index
Note: The caller must have already locked the handle table
Arguments:
HandleTable - Supplies the handle table being used
pNewLowLevel - Returns the new low level taible for later free list chaining
Return Value:
Returns a pointer to the new mid-level table allocated
--*/
{
PHANDLE_TABLE_ENTRY *NewMidLevel;
PHANDLE_TABLE_ENTRY NewLowLevel;
NewMidLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
PAGE_SIZE
);
if (NewMidLevel == NULL) {
return NULL;
}
//
// If we need a new mid-level, we'll need a low-level too.
// We'll create one and if success we'll save it at the first position
//
NewLowLevel = ExpAllocateLowLevelTable( HandleTable );
if (NewLowLevel == NULL) {
ExpFreeTablePagedPool( HandleTable->QuotaProcess,
NewMidLevel,
PAGE_SIZE
);
return NULL;
}
//
// Set the low-level table at the first index
//
NewMidLevel[0] = NewLowLevel;
*pNewLowLevel = NewLowLevel;
return NewMidLevel;
}
BOOLEAN
ExpAllocateHandleTableEntrySlow (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This worker routine allocates a new handle table entry for the specified
handle table.
Note: The caller must have already locked the handle table
Arguments:
HandleTable - Supplies the handle table being used
Handle - Returns the handle of the new entry if the allocation is
successful otherwise the value is null
Return Value:
BOOLEAN - TRUE, Retry the fast allocation path, FALSE, We failed to allocate memory
--*/
{
ULONG i,j;
PHANDLE_TABLE_ENTRY NewLowLevel;
PHANDLE_TABLE_ENTRY *NewMidLevel;
PHANDLE_TABLE_ENTRY **NewHighLevel;
ULONG NewFree, OldFree;
ULONG OldIndex;
ULONG_PTR CapturedTable = HandleTable->TableCode;
ULONG TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
PAGED_CODE();
//
// Initializing NewLowLevel is not needed for
// correctness but without it the compiler cannot compile this code
// W4 to check for use of uninitialized variables.
//
NewLowLevel = NULL;
CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;
if ( TableLevel == 0 ) {
//
// We have a single level. We need to ad a mid-layer
// to the process handle table
//
NewMidLevel = ExpAllocateMidLevelTable( HandleTable, &NewLowLevel );
if (NewMidLevel == NULL) {
return FALSE;
}
//
// Since ExpAllocateMidLevelTable initialize the
// first position with a new table, we need to move it in
// the second position, and store in the first position the current one
//
NewMidLevel[1] = NewMidLevel[0];
NewMidLevel[0] = (PHANDLE_TABLE_ENTRY)CapturedTable;
//
// Encode the current level and set it to the handle table process
//
CapturedTable = ((ULONG_PTR)NewMidLevel) | 1;
InterlockedExchangePointer( (PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable );
} else if (TableLevel == 1) {
//
// We have a 2 levels handle table
//
PHANDLE_TABLE_ENTRY *TableLevel2 = (PHANDLE_TABLE_ENTRY *)CapturedTable;
//
// Test whether the index we need to create is still in the
// range for a 2 layers table
//
i = HandleTable->NextHandleNeedingPool / (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
if (i < MIDLEVEL_COUNT) {
//
// We just need to allocate a new low-level
// table
//
NewLowLevel = ExpAllocateLowLevelTable( HandleTable );
if (NewLowLevel == NULL) {
return FALSE;
}
//
// Set the new one to the table, at appropriate position
//
InterlockedExchangePointer( (PVOID *) (&TableLevel2[i]), NewLowLevel );
} else {
//
// We exhausted the 2 level domain. We need to insert a new one
//
NewHighLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
HIGHLEVEL_SIZE
);
if (NewHighLevel == NULL) {
return FALSE;
}
NewMidLevel = ExpAllocateMidLevelTable( HandleTable, &NewLowLevel );
if (NewMidLevel == NULL) {
ExpFreeTablePagedPool( HandleTable->QuotaProcess,
NewHighLevel,
HIGHLEVEL_SIZE
);
return FALSE;
}
//
// Initialize the first index with the previous mid-level layer
//
NewHighLevel[0] = (PHANDLE_TABLE_ENTRY*)CapturedTable;
NewHighLevel[1] = NewMidLevel;
//
// Encode the level into the table pointer
//
CapturedTable = ((ULONG_PTR)NewHighLevel) | 2;
//
// Change the handle table pointer with this one
//
InterlockedExchangePointer( (PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable );
}
} else if (TableLevel == 2) {
//
// we have already a table with 3 levels
//
ULONG RemainingIndex;
PHANDLE_TABLE_ENTRY **TableLevel3 = (PHANDLE_TABLE_ENTRY **)CapturedTable;
i = HandleTable->NextHandleNeedingPool / (MIDLEVEL_THRESHOLD * HANDLE_VALUE_INC);
//
// Check whether we exhausted all possible indexes.
//
if (i >= HIGHLEVEL_COUNT) {
return FALSE;
}
if (TableLevel3[i] == NULL) {
//
// The new available handle points to a free mid-level entry
// We need then to allocate a new one and save it in that position
//
NewMidLevel = ExpAllocateMidLevelTable( HandleTable, &NewLowLevel );
if (NewMidLevel == NULL) {
return FALSE;
}
InterlockedExchangePointer( (PVOID *) &(TableLevel3[i]), NewMidLevel );
} else {
//
// We have already a mid-level table. We just need to add a new low-level one
// at the end
//
RemainingIndex = (HandleTable->NextHandleNeedingPool / HANDLE_VALUE_INC) -
i * MIDLEVEL_THRESHOLD;
j = RemainingIndex / LOWLEVEL_COUNT;
NewLowLevel = ExpAllocateLowLevelTable( HandleTable );
if (NewLowLevel == NULL) {
return FALSE;
}
InterlockedExchangePointer( (PVOID *)(&TableLevel3[i][j]) , NewLowLevel );
}
}
//
// This must be done after the table pointers so that new created handles
// are valid before being freed.
//
OldIndex = InterlockedExchangeAdd ((PLONG) &HandleTable->NextHandleNeedingPool,
LOWLEVEL_COUNT * HANDLE_VALUE_INC);
//
// Now free the handles. These are all ready to be accepted by the lookup logic now.
//
NewFree = OldIndex + HANDLE_VALUE_INC;
while (1) {
OldFree = HandleTable->FirstFree;
NewLowLevel[LOWLEVEL_COUNT - 1].NextFreeTableEntry = OldFree;
//
// These are new entries that have never existed before. We can't have an A-B-A problem
// with these so we don't need to take any locks
//
NewFree = InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
NewFree,
OldFree);
if (NewFree == OldFree) {
break;
}
}
return TRUE;
}
VOID
ExSetHandleTableStrictFIFO (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This routine marks a handle table so that handle allocation is done in
a strict FIFO order.
Arguments:
HandleTable - Supplies the handle table being changed to FIFO
Return Value:
None.
--*/
{
HandleTable->StrictFIFO = TRUE;
}
//
// Local Support Routine
//
//
// The following is a global variable only present in the checked builds
// to help catch apps that reuse handle values after they're closed.
//
#if DBG
BOOLEAN ExReuseHandles = 1;
#endif //DBG
VOID
ExpFreeHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE Handle,
IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
/*++
Routine Description:
This worker routine returns the specified handle table entry to the free
list for the handle table.
Note: The caller must have already locked the handle table
Arguments:
HandleTable - Supplies the parent handle table being modified
Handle - Supplies the handle of the entry being freed
HandleTableEntry - Supplies the table entry being freed
Return Value:
None.
--*/
{
PHANDLE_TABLE_ENTRY_INFO EntryInfo;
ULONG OldFree, NewFree, *Free;
PKTHREAD CurrentThread;
ULONG Idx;
PAGED_CODE();
//
// Clear the AuditMask flags if these are present into the table
//
EntryInfo = ExGetHandleInfo(HandleTable, Handle.GenericHandleOverlay, TRUE);
if (EntryInfo) {
EntryInfo->AuditMask = 0;
}
//
// A free is simply a push onto the free table entry stack, or in the
// debug case we'll sometimes just float the entry to catch apps who
// reuse a recycled handle value.
//
InterlockedDecrement (&HandleTable->HandleCount);
CurrentThread = KeGetCurrentThread ();
#if DBG
if (ExReuseHandles) {
#endif //DBG
NewFree = (ULONG) Handle.Value & ~(HANDLE_VALUE_INC - 1);
if (!HandleTable->StrictFIFO) {
//
// We are pushing potentialy old entries onto the free list.
// Prevent the A-B-A problem by shifting to an alternate list
// read this element has the list head out of the loop.
//
Idx = (NewFree>>2) % HANDLE_TABLE_LOCKS;
if (ExTryAcquireReleasePushLockExclusive (&HandleTable->HandleTableLock[Idx])) {
Free = &HandleTable->FirstFree;
} else {
Free = &HandleTable->LastFree;
}
} else {
Free = &HandleTable->LastFree;
}
while (1) {
OldFree = *Free;
HandleTableEntry->NextFreeTableEntry = OldFree;
if ((ULONG)InterlockedCompareExchange ((PLONG)Free,
NewFree,
OldFree) == OldFree) {
break;
}
}
#if DBG
} else {
HandleTableEntry->NextFreeTableEntry = 0;
}
#endif //DBG
return;
}
//
// Local Support Routine
//
PHANDLE_TABLE_ENTRY
ExpLookupHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE Handle
)
/*++
Routine Description:
This routine looks up and returns the table entry for the
specified handle value.
Arguments:
HandleTable - Supplies the handle table being queried
Handle - Supplies the handle value being queried
Return Value:
Returns a pointer to the corresponding table entry for the input
handle. Or NULL if the handle value is invalid (i.e., too large
for the tables current allocation.
--*/
{
ULONG_PTR i,j,k;
ULONG_PTR CapturedTable;
ULONG TableLevel;
PHANDLE_TABLE_ENTRY Entry;
typedef HANDLE_TABLE_ENTRY *L1P;
typedef volatile L1P *L2P;
typedef volatile L2P *L3P;
L1P TableLevel1;
L2P TableLevel2;
L3P TableLevel3;
ULONG_PTR RemainingIndex;
ULONG_PTR MaxHandle;
ULONG_PTR Index;
PAGED_CODE();
//
// Extract the handle index
//
Handle.TagBits = 0;
Index = Handle.Index;
MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;
//
// See if this can be a valid handle given the table levels.
//
if (Handle.Value >= MaxHandle) {
return NULL;
}
//
// Now fetch the table address and level bits. We must preserve the
// ordering here.
//
CapturedTable = *(volatile ULONG_PTR *) &HandleTable->TableCode;
//
// we need to capture the current table. This routine is lock free
// so another thread may change the table at HandleTable->TableCode
//
TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;
//
// The lookup code depends on number of levels we have
//
switch (TableLevel) {
case 0:
//
// We have a simple index into the array, for a single level
// handle table
//
TableLevel1 = (L1P) CapturedTable;
Entry = &(TableLevel1[Index]);
break;
case 1:
//
// we have a 2 level handle table. We need to get the upper index
// and lower index into the array
//
TableLevel2 = (L2P) CapturedTable;
i = Index / LOWLEVEL_COUNT;
j = Index % LOWLEVEL_COUNT;
Entry = &(TableLevel2[i][j]);
break;
case 2:
//
// We have here a three level handle table.
//
TableLevel3 = (L3P) CapturedTable;
//
// Calculate the 3 indexes we need
//
i = Index / (MIDLEVEL_THRESHOLD);
RemainingIndex = Index - i * MIDLEVEL_THRESHOLD;
j = RemainingIndex / LOWLEVEL_COUNT;
k = RemainingIndex % LOWLEVEL_COUNT;
Entry = &(TableLevel3[i][j][k]);
break;
default :
_assume (0);
}
return Entry;
}
NTKERNELAPI
NTSTATUS
ExSetHandleInfo (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle,
IN PHANDLE_TABLE_ENTRY_INFO EntryInfo,
IN BOOLEAN EntryLocked
)
/*++
Routine Description:
The routine set the entry info for the specified handle table
Note: the handle entry must be locked when this function is called
Arguments:
HandleTable - Supplies the handle table being queried
Handle - Supplies the handle value being queried
Return Value:
--*/
{
PKTHREAD CurrentThread;
PHANDLE_TABLE_ENTRY InfoStructure;
EXHANDLE ExHandle;
NTSTATUS Status;
PHANDLE_TABLE_ENTRY TableEntry;
PHANDLE_TABLE_ENTRY_INFO InfoTable;
Status = STATUS_UNSUCCESSFUL;
TableEntry = NULL;
CurrentThread = NULL;
ExHandle.GenericHandleOverlay = Handle;
ExHandle.Index &= ~(LOWLEVEL_COUNT - 1);
if (!EntryLocked) {
CurrentThread = KeGetCurrentThread ();
KeEnterCriticalRegionThread (CurrentThread);
TableEntry = ExMapHandleToPointer(HandleTable, Handle);
if (TableEntry == NULL) {
KeLeaveCriticalRegionThread (CurrentThread);
return STATUS_UNSUCCESSFUL;
}
}
//
// The info structure is at the first position in each low-level table
//
InfoStructure = ExpLookupHandleTableEntry( HandleTable,
ExHandle
);
if (InfoStructure == NULL || InfoStructure->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE) {
if ( TableEntry ) {
ExUnlockHandleTableEntry( HandleTable, TableEntry );
KeLeaveCriticalRegionThread (CurrentThread);
}
return STATUS_UNSUCCESSFUL;
}
//
// Check whether we need to allocate a new table
//
InfoTable = InfoStructure->InfoTable;
if (InfoTable == NULL) {
//
// Nobody allocated the Infotable so far.
// We'll do it right now
//
InfoTable = ExpAllocateTablePagedPool (HandleTable->QuotaProcess,
LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO));
if (InfoTable) {
//
// Update the number of pages for extra info. If somebody beat us to it then free the
// new table
//
if (InterlockedCompareExchangePointer (&InfoStructure->InfoTable,
InfoTable,
NULL) == NULL) {
InterlockedIncrement(&HandleTable->ExtraInfoPages);
} else {
ExpFreeTablePagedPool (HandleTable->QuotaProcess,
InfoTable,
LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO));
InfoTable = InfoStructure->InfoTable;
}
}
}
if (InfoTable != NULL) {
//
// Calculate the index and copy the structure
//
ExHandle.GenericHandleOverlay = Handle;
InfoTable[ExHandle.Index % LOWLEVEL_COUNT] = *EntryInfo;
Status = STATUS_SUCCESS;
}
if ( TableEntry ) {
ExUnlockHandleTableEntry( HandleTable, TableEntry );
KeLeaveCriticalRegionThread (CurrentThread);
}
return Status;
}
NTKERNELAPI
PHANDLE_TABLE_ENTRY_INFO
ExpGetHandleInfo (
IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle,
IN BOOLEAN EntryLocked
)
/*++
Routine Description:
The routine reads the entry info for the specified handle table
Note: the handle entry must be locked when this function is called
Arguments:
HandleTable - Supplies the handle table being queried
Handle - Supplies the handle value being queried
Return Value:
--*/
{
PHANDLE_TABLE_ENTRY InfoStructure;
EXHANDLE ExHandle;
PHANDLE_TABLE_ENTRY TableEntry = NULL;
ExHandle.GenericHandleOverlay = Handle;
ExHandle.Index &= ~(LOWLEVEL_COUNT - 1);
if (!EntryLocked) {
TableEntry = ExMapHandleToPointer(HandleTable, Handle);
if (TableEntry == NULL) {
return NULL;
}
}
//
// The info structure is at the first position in each low-level table
//
InfoStructure = ExpLookupHandleTableEntry( HandleTable,
ExHandle
);
if (InfoStructure == NULL || InfoStructure->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE ||
InfoStructure->InfoTable == NULL) {
if ( TableEntry ) {
ExUnlockHandleTableEntry( HandleTable, TableEntry );
}
return NULL;
}
//
// Return a pointer to the info structure
//
ExHandle.GenericHandleOverlay = Handle;
return &(InfoStructure->InfoTable[ExHandle.Index % LOWLEVEL_COUNT]);
}