Windows2000/private/ntos/ex/handle.c
2020-09-30 17:12:32 +02:00

2174 lines
54 KiB
C

/*++
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)
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;
LIST_ENTRY HandleTableListHead;
// This is the sign bit used to lock handle table entries
#define EXHANDLE_TABLE_ENTRY_LOCK_BIT ((ULONG_PTR)1 << ((sizeof(ULONG_PTR) * 8) - 1))
// This is the delay we willing to do to try and get a handle table
// entry lock
LARGE_INTEGER Ex10Milliseconds = {(ULONG)(-10 * 1000 * 10), -1};
// Local support routines
PHANDLE_TABLE
ExpAllocateHandleTable (
IN PEPROCESS Process OPTIONAL
);
VOID
ExpFreeHandleTable (
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
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExInitializeHandleTablePackage)
#pragma alloc_text(PAGE, ExLockHandleTableShared)
#pragma alloc_text(PAGE, ExLockHandleTableExclusive)
#pragma alloc_text(PAGE, ExUnlockHandleTableShared)
#pragma alloc_text(PAGE, ExUnlockHandleTableExclusive)
#pragma alloc_text(PAGE, ExLockHandleTableEntry)
#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, ExpAllocateHandleTable)
#pragma alloc_text(PAGE, ExpFreeHandleTable)
#pragma alloc_text(PAGE, ExpAllocateHandleTableEntry)
#pragma alloc_text(PAGE, ExpFreeHandleTableEntry)
#pragma alloc_text(PAGE, ExpLookupHandleTableEntry)
#endif
NTKERNELAPI
VOID
ExLockHandleTableShared (
PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This routine locks the specified handle table for shared access. Once
locked new entries cannot be added to deleted from the handle table.
Arguments:
HandleTable - Supplies the handle table being locked.
Return Value:
None.
--*/
{
PAGED_CODE();
(VOID)ExAcquireResourceShared( &HandleTable->HandleTableLock, TRUE );
return;
}
NTKERNELAPI
VOID
ExLockHandleTableExclusive (
PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This routine locks the specified handle table for exclusive access. Once
locked the onwer can add or delete entries from the handle table.
Arguments:
HandleTable - Supplies the handle table being locked.
Return Value:
None.
--*/
{
PAGED_CODE();
(VOID)ExAcquireResourceExclusive( &HandleTable->HandleTableLock, TRUE );
return;
}
NTKERNELAPI
VOID
ExUnlockHandleTableShared (
PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This routine unlocks the specified handle table from shared access.
Arguments:
HandleTable - Supplies the handle table being unlocked.
Return Value:
None.
--*/
{
PAGED_CODE();
(VOID)ExReleaseResource( &HandleTable->HandleTableLock );
return;
}
NTKERNELAPI
VOID
ExUnlockHandleTableExclusive (
PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This routine unlocks the specified handle table from exclusive access.
Arguments:
HandleTable - Supplies the handle table being unlocked.
Return Value:
None.
--*/
{
PAGED_CODE();
(VOID)ExReleaseResource( &HandleTable->HandleTableLock );
return;
}
NTKERNELAPI
BOOLEAN
ExLockHandleTableEntry (
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;
ULONG LoopCount = 0;
PAGED_CODE();
// 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) {
NewValue =
CurrentValue = *((volatile LONG_PTR *)&HandleTableEntry->Object);
// Make sure the handle table entry is not freed
if (CurrentValue == 0) {
return FALSE;
}
// If the handle value is greater than zero then it is not currently
// locked and we should try for the lock, but setting the lock bit and
// doing an interlocked exchange.
if (CurrentValue > 0) {
NewValue |= EXHANDLE_TABLE_ENTRY_LOCK_BIT;
if ((LONG_PTR)(InterlockedCompareExchangePointer( &HandleTableEntry->Object,
(PVOID)NewValue,
(PVOID)CurrentValue )) == CurrentValue) {
return TRUE;
}
}
// Either someone already has the entry locked or the value changed
// somehow, either way we'll wait a bit and try again. The first
// time we'll wait by spinning for 10us and if that doesn't work
// then we'll wait for the handle contention event to go off or
// for 10ms, whichever comes first.
if (LoopCount++ < 1) {
KeStallExecutionProcessor( 10 );
} else {
KeWaitForSingleObject( &HandleTable->HandleContentionEvent,
Executive,
KernelMode,
FALSE,
&Ex10Milliseconds );
}
}
}
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();
// This routine does not need to loop and attempt the unlock opeation more
// than once because by defintion 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.
NewValue = CurrentValue = *((volatile LONG_PTR *)&HandleTableEntry->Object);
if (CurrentValue >= 0) {
KeBugCheckEx( BAD_EXHANDLE, __LINE__, (LONG_PTR)HandleTableEntry, NewValue, CurrentValue );
}
NewValue &= ~EXHANDLE_TABLE_ENTRY_LOCK_BIT;
if ((LONG_PTR)(InterlockedCompareExchangePointer( &HandleTableEntry->Object,
(PVOID)NewValue,
(PVOID)CurrentValue )) != CurrentValue) {
KeBugCheckEx( BAD_EXHANDLE, __LINE__, (LONG_PTR)HandleTableEntry, NewValue, CurrentValue );
}
// Now that we've unlocked the event we'll see if there are any waiters
// for handle table entries in this table. If there are any waiters
// we'll wake them up and let them try for their lock again.
// Note that this will wake up all the waiters for all locks in a given
// handle table. Each waiter also has a short time out so the worse
// that can happen is that we might wake someone too early. It can also
// be that no one is waiting for this exact lock, or the one waiting for
// it has already gone off, but these are all benign.
// **** Note that we're testing for a non empty event header wait list
// head. This is sort of behind the back and a better design
// would get a macro from ke.h to do the test, but this will
// suffice for now. This is actually an unsafe test in that it
// might return the wrong answer, but even then we are willing to
// live with it because any waiters will wake up in 10ms anyway and
// any extra call to pulse the event without waiters is benign.
if (!IsListEmpty( &HandleTable->HandleContentionEvent.Header.WaitListHead )) {
KePulseEvent( &HandleTable->HandleContentionEvent, EVENT_INCREMENT, FALSE );
}
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 );
ExInitializeResource( &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.
--*/
{
PHANDLE_TABLE HandleTable;
PAGED_CODE();
// Allocate and initialize a handle table descriptor
HandleTable = ExpAllocateHandleTable( Process );
// 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.
--*/
{
PAGED_CODE();
// First, acquire the global handle table lock
KeEnterCriticalRegion();
ExAcquireResourceExclusive( &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.
if (!IsListEmpty( &HandleTable->HandleTableList )) {
RemoveEntryList( &HandleTable->HandleTableList );
InitializeListHead( &HandleTable->HandleTableList );
}
// Now release the global lock and return to our caller
ExReleaseResource( &HandleTableListLock );
KeLeaveCriticalRegion();
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(DestroyHandleProcedure)) {
for (Handle.GenericHandleOverlay = NULL; // does essentially the following "Handle.Index = 0, Handle.TagBits = 0;"
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
Handle.Index += 1) {
// Only do the callback if the entry is not free
if (HandleTableEntry->Object != NULL) {
(*DestroyHandleProcedure)( Handle.GenericHandleOverlay );
}
}
}
// Now free up the handle table memory and return to our caller
ExpFreeHandleTable( HandleTable );
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.
--*/
{
BOOLEAN ResultValue;
EXHANDLE LocalHandle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
// First lock the handle table shared to stop anyone from creating or
// destroying new handles. We need to do this because out iteration
// function doesn't want the handles to be closed while we're doing
// our work
KeEnterCriticalRegion();
ExLockHandleTableShared( HandleTable );
try {
// 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 of the handles in a dense fashion
for (LocalHandle.GenericHandleOverlay = NULL; // does essentially the following "LocalHandle.Index = 0, LocalHandle.TagBits = 0;"
(HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, LocalHandle )) != NULL;
LocalHandle.Index += 1) {
// Only do the callback if the entry is not free
if (HandleTableEntry->Object != NULL) {
// 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 (ExLockHandleTableEntry( HandleTable, HandleTableEntry )) {
try {
// Invoke the callback, and if it returns true then set
// the proper output values and break out of the loop.
if ((*EnumHandleProcedure)( HandleTableEntry,
LocalHandle.GenericHandleOverlay,
EnumParameter )) {
if (ARGUMENT_PRESENT( Handle )) {
*Handle = LocalHandle.GenericHandleOverlay;
}
ResultValue = TRUE;
break;
}
} finally {
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
}
}
}
}
} finally {
ExUnlockHandleTableShared( HandleTable );
KeLeaveCriticalRegion();
}
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.
--*/
{
PHANDLE_TABLE NewHandleTable;
PHANDLE_TABLE_ENTRY AdditionalFreeEntries;
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY OldHandleTableEntry;
PHANDLE_TABLE_ENTRY NewHandleTableEntry;
PAGED_CODE();
// First allocate a new handle table. If this fails then
// return immediately to our caller
NewHandleTable = ExpAllocateHandleTable( Process );
if (NewHandleTable == NULL) {
return NULL;
}
// Now lock down the old handle table. We will release it
// right after enumerating through the table
KeEnterCriticalRegion();
ExLockHandleTableShared( OldHandleTable );
AdditionalFreeEntries = NULL;
try {
// 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->NextIndexNeedingPool < OldHandleTable->NextIndexNeedingPool) {
// If we set the first free table entry to -1 then the worker
// routine will allocate another buffer
NewHandleTable->FirstFreeTableEntry = -1;
// Call the worker routine to grow the new handle table. If
// not successful then free the new table as far as we got,
// set out output variable and exit out here
if (ExpAllocateHandleTableEntry( NewHandleTable, &Handle ) == NULL) {
ExpFreeHandleTable( NewHandleTable );
NewHandleTable = NULL;
leave;
}
}
// 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->FirstFreeTableEntry = OldHandleTable->FirstFreeTableEntry;
// Now for every valid index value we'll copy over the old entry into
// the new entry
for (Handle.GenericHandleOverlay = NULL; // does essentially the following "Handle.Index = 0, Handle.TagBits = 0;"
(OldHandleTableEntry = ExpLookupHandleTableEntry( OldHandleTable, Handle )) != NULL;
Handle.Index += 1) {
// The conditinal in the loop gives up the old handle table
// entry, by definition there must exist a corresponding new
// handle table entry, so now look it up
NewHandleTableEntry = ExpLookupHandleTableEntry( NewHandleTable,
Handle );
// If the old entry is free then simply copy over the entire
// old entry to the new entry. The lock command will tell us
// it entry is free.
if (!ExLockHandleTableEntry( OldHandleTable, OldHandleTableEntry )) {
*NewHandleTableEntry = *OldHandleTableEntry;
} else {
// 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;
NewHandleTable->HandleCount += 1;
ExUnlockHandleTableEntry( OldHandleTable, OldHandleTableEntry );
// Invoke the callback and if it returns true then we
// unlock the new entry
if ((*DupHandleProcedure)( Process, NewHandleTableEntry )) {
ExUnlockHandleTableEntry( NewHandleTable, NewHandleTableEntry );
} else {
// Otherwise this entry is going to be freed in the
// new handle table. So add it to the stack list of
// additional freed entries
NewHandleTableEntry->Object = AdditionalFreeEntries;
NewHandleTableEntry->NextFreeTableEntry = Handle.Index;
AdditionalFreeEntries = NewHandleTableEntry;
}
}
}
} finally {
ExUnlockHandleTableShared( OldHandleTable );
KeLeaveCriticalRegion();
}
// At this point we are through with the old handle table,
// and if present the new handle table is done with except
// for adding back the newly freed entrires. The only time
// the additionalFreeEntries variable will not be null is if
// we successfully built the new table and the dup routine
// came back false.
// While there are additional entries to add to the free list
// pop the entry off the stack and add it to the table
Handle.GenericHandleOverlay = NULL;
while (AdditionalFreeEntries != NULL) {
PVOID Next;
Next = AdditionalFreeEntries->Object;
Handle.Index = AdditionalFreeEntries->NextFreeTableEntry;
AdditionalFreeEntries->Object = NULL;
ExpFreeHandleTableEntry( NewHandleTable,
Handle,
AdditionalFreeEntries );
AdditionalFreeEntries = Next;
}
// 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;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO HandleEntryInfo;
PLIST_ENTRY NextEntry;
PHANDLE_TABLE HandleTable;
EXHANDLE Handle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
// Lock the handle table list exclusive and traverse the list of handle tables.
Status = STATUS_SUCCESS;
KeEnterCriticalRegion();
ExAcquireResourceExclusive( &HandleTableListLock, TRUE );
try {
HandleEntryInfo = &HandleInformation->Handles[0];// Setup the output buffer pointer that the callback will maintain
HandleInformation->NumberOfHandles = 0;// Zero out the handle count
// 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 );
ExLockHandleTableExclusive( HandleTable );
try {
// 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.Index = 0, Handle.TagBits = 0; (HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL; Handle.Index += 1) {
// Only do the callback if the entry is not free
if (HandleTableEntry->Object != NULL) {
// 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 (ExLockHandleTableEntry( HandleTable, HandleTableEntry )) {
try {
Status = (*SnapShotHandleEntry)( &HandleEntryInfo, HandleTable->UniqueProcessId, HandleTableEntry, Handle.GenericHandleOverlay, Length, RequiredLength );
} finally {
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
}
}
}
}
} finally {
ExUnlockHandleTableExclusive( HandleTable );
}
}
} finally {
ExReleaseResource( &HandleTableListLock );
KeLeaveCriticalRegion();
}
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;
PHANDLE_TABLE_ENTRY NewHandleTableEntry;
PAGED_CODE();
// Set out output variable to zero (i.e., null) before going on
// Clears Handle.Index and Handle.TagBits
Handle.GenericHandleOverlay = NULL;
// Lock the handle table for exclusive access
KeEnterCriticalRegion();
ExLockHandleTableExclusive( HandleTable );
try {
// 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) {
*NewHandleTableEntry = *HandleTableEntry;
ExUnlockHandleTableEntry( HandleTable, NewHandleTableEntry );
}
} finally {
ExUnlockHandleTableExclusive( HandleTable );
KeLeaveCriticalRegion();
}
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;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
// 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.
if (HandleTableEntry == NULL) {
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if (HandleTableEntry == NULL) {
return FALSE;
}
if (!ExLockHandleTableEntry( HandleTable, HandleTableEntry )) {
return FALSE;
}
}
// 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
HandleTableEntry->Object = NULL;
KeEnterCriticalRegion();
ExLockHandleTableExclusive( HandleTable );
try {
ExpFreeHandleTableEntry( HandleTable,
LocalHandle,
HandleTableEntry );
} finally {
ExUnlockHandleTableExclusive( HandleTable );
KeLeaveCriticalRegion();
}
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;
PHANDLE_TABLE_ENTRY HandleTableEntry;
BOOLEAN ReturnValue;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
// Translate the input handle to a handle table entry and make
// sure it is a valid handle.
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if (HandleTableEntry == NULL) {
return FALSE;
}
// Try and lock the handle table entry, If this fails then that's
// because someone freed the handle
if (!ExLockHandleTableEntry( HandleTable, HandleTableEntry )) {
return FALSE;
}
// Make sure we can't get suspended and then invoke the callback
KeEnterCriticalRegion();
try {
ReturnValue = (*ChangeRoutine)( HandleTableEntry, Parameter );
} finally {
ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
KeLeaveCriticalRegion();
}
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;
PAGED_CODE();
LocalHandle.GenericHandleOverlay = Handle;
// Translate the input handle to a handle table entry and make
// sure it is a valid handle.
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
LocalHandle );
if (HandleTableEntry == NULL) {
return NULL;
}
// Try and lock the handle table entry, If this fails then that's
// because someone freed the handle
if (!ExLockHandleTableEntry( HandleTable, HandleTableEntry )) {
return NULL;
}
// Return the locked valid handle table entry
return HandleTableEntry;
}
// 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;
BOOLEAN HandleTableQuotaCharged;
PVOID HandleTableTable;
BOOLEAN HandleTableTableQuotaCharged;
ULONG i;
PAGED_CODE();
HandleTable = NULL;
HandleTableQuotaCharged = FALSE;
HandleTableTable = NULL;
HandleTableTableQuotaCharged = FALSE;
// 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
try {
// First allocate the handle table, make sure we got one, charge quota
// for it and then zero it out
HandleTable = (PHANDLE_TABLE)ExAllocatePoolWithTag( NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
sizeof(HANDLE_TABLE),
'btbO' );
if (ARGUMENT_PRESENT(Process)) {
PsChargePoolQuota( Process,
NonPagedPool,
sizeof(HANDLE_TABLE));
HandleTableQuotaCharged = TRUE;
}
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 =
HandleTable->Table = ExAllocatePoolWithTag( PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
(2 * sizeof(ULONG_PTR) * 256) + (sizeof(HANDLE_TABLE_ENTRY) * 256),
'btbO' );
if (ARGUMENT_PRESENT(Process)) {
PsChargePoolQuota( Process,
PagedPool,
(2 * sizeof(ULONG_PTR) * 256) + (sizeof(HANDLE_TABLE_ENTRY) * 256) );
HandleTableTableQuotaCharged = TRUE;
}
RtlZeroMemory( HandleTable->Table,
(2 * sizeof(ULONG_PTR) * 256) + (sizeof(HANDLE_TABLE_ENTRY) * 256) );
} except (EXCEPTION_EXECUTE_HANDLER) {
if (HandleTable != NULL) {
ExFreePool( HandleTable );
if (HandleTableQuotaCharged) {
PsReturnPoolQuota( Process,
NonPagedPool,
sizeof(HANDLE_TABLE));
}
}
if (HandleTableTable != NULL) {
ExFreePool( HandleTableTable );
if (HandleTableTableQuotaCharged) {
PsReturnPoolQuota( Process,
PagedPool,
(2 * sizeof(ULONG_PTR) * 256) + (sizeof(HANDLE_TABLE_ENTRY) * 256) );
}
}
return NULL;
}
// Now setup the pointers for the initial handle table tree
HandleTable->Table[0] = (PVOID)(((PCHAR)(HandleTable->Table)) + 1 * (sizeof(ULONG_PTR) * 256));
HandleTable->Table[0][0] = (PVOID)(((PCHAR)(HandleTable->Table)) + 2 * (sizeof(ULONG_PTR) * 256));
// 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 -1. Note that
// we'll skip handle zero because our callers will get that value
// confused with null.
for (i = 0; i < 255; i += 1) {
(HandleTable->Table[0][0])[i].NextFreeTableEntry = i+1;
}
(HandleTable->Table[0][0])[255].NextFreeTableEntry = -1;
HandleTable->FirstFreeTableEntry = 1;
HandleTable->NextIndexNeedingPool = 256;
// Setup the necessary process information
HandleTable->QuotaProcess = Process;
HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId;
// Initialize the lock handle table resource
ExInitializeResource( &HandleTable->HandleTableLock );
// Initialize the notification event for handle table entry contention
KeInitializeEvent( &HandleTable->HandleContentionEvent, NotificationEvent, FALSE );
// Insert the handle table in the handle table list.
KeEnterCriticalRegion();
ExAcquireResourceExclusive( &HandleTableListLock, TRUE );
InsertTailList( &HandleTableListHead, &HandleTable->HandleTableList );
ExReleaseResource( &HandleTableListLock );
KeLeaveCriticalRegion();
// And return to our caller
return HandleTable;
}
// Local Support Routine
VOID
ExpFreeHandleTable (
IN PHANDLE_TABLE HandleTable
)
/*++
Routine Description:
This worker routine tearsdown and frees the specified handle table.
Arguments:
HandleTable - Supplies the handle table being freed
Return Value:
None.
--*/
{
PEPROCESS Process;
ULONG i,j;
PAGED_CODE();
Process = HandleTable->QuotaProcess;
// First free the lock for the local handle table
ExDeleteResource( &HandleTable->HandleTableLock );
// We first return all of the additional exhandle table entry buffers
// that we allocated. Note that each pool buffer we allocated is for
// two subindex buffers in the table, and note also that we have to
// start a little funny to compensate for the first that really contains
// the top level and first mid level, and first entry buffer
// Take care of the special case where the [0][0] index is really
// allocated up front, and we only need to examine [0][1] to see if
// anything special needs to be deallocated, followed by [0][3], [0][5],
// up to and including [0][255]
for (j = 1; j < 256; j += 2) {
if (HandleTable->Table[0][j] == NULL) {
break;
}
ExFreePool( HandleTable->Table[0][j] );
if (Process != NULL) {
PsReturnPoolQuota( Process,
PagedPool,
2 * sizeof(HANDLE_TABLE_ENTRY) * 256 );
}
}
// Now that we've handle the special case we can do the rest of the table
// starting with index [1][0], [1][2], ... [1][254], [2][0], [2][2] ...
// up to and including [255][254]
for (i = 1; i < 256; i += 1) {
if (HandleTable->Table[i] == NULL) {
break;
}
for (j = 0; j < 256; j += 2) {
if (HandleTable->Table[i][j] == NULL) {
break;
}
ExFreePool( HandleTable->Table[i][j] );
if (Process != NULL) {
PsReturnPoolQuota( Process,
PagedPool,
2 * sizeof(HANDLE_TABLE_ENTRY) * 256 );
}
}
}
// Now that we've deallocated the handle table entry buffer we can
// deallocate the additional mid level buffers. Note that that these
// start at index [0][1] and were allocated four a time from pool.
for (i = 1; i < 256; i += 4) {
if (HandleTable->Table[i] == NULL) {
break;
}
ExFreePool( HandleTable->Table[i] );
if (Process != NULL) {
PsReturnPoolQuota( Process,
PagedPool,
4 * sizeof(ULONG_PTR) * 256 );
}
}
// Now deallocate the original handle table buffer used to store
// the top level, first mid level, and first table entry buffer
ExFreePool( HandleTable->Table );
if (Process != NULL) {
PsReturnPoolQuota( Process,
PagedPool,
(2 * sizeof(ULONG_PTR) * 256) + (sizeof(HANDLE_TABLE_ENTRY) * 256) );
}
// Finally deallocate the handle table itself
ExFreePool( HandleTable );
if (Process != NULL) {
PsReturnPoolQuota( Process,
NonPagedPool,
sizeof(HANDLE_TABLE) );
}
// And return to our caller
return;
}
// Local Support Routine
PHANDLE_TABLE_ENTRY
ExpAllocateHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
OUT PEXHANDLE Handle
)
/*++
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:
Returns a pointer to the new handle table entry is the allocation is
successful otherwise the return value is null.
--*/
{
ULONG i,j,k;
PUCHAR NewMidLevel;
BOOLEAN MidTableQuotaCharged;
PUCHAR NewLowLevel;
BOOLEAN LowTableQuotaCharged;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
Handle->GenericHandleOverlay = NULL;
// First see if the free stack is emtpy and if so then we need
// to add some more entries to it
if (HandleTable->FirstFreeTableEntry == -1) {
// We need to allocate some more pool. We start by seeing
// if the next index is too large to allocate. We only have
// 24 bits of handle values to use
if (HandleTable->NextIndexNeedingPool >= (1 << 24)) {
return NULL;
}
// Now find the next index to allocate a buffer for
i = (HandleTable->NextIndexNeedingPool >> 16) & 255;
j = (HandleTable->NextIndexNeedingPool >> 8) & 255;
// Within the following try body we'll be allocating and initializing
// a low level buffer and possibly a new mid level buffer. The
// might seem a little odd but is necessary to handle the situation
// where our first buffer is allocated off a little bit (i.e., the
// next buffer index starts are [0][1]). By the time we reach
// [0][255] we add only one new buffer. Then start with even numbers
// at [1][0] and work our way to completion
NewMidLevel = NULL;
MidTableQuotaCharged = FALSE;
NewLowLevel = NULL;
LowTableQuotaCharged = FALSE;
try {
// Check if we need to allocate a new mid level buffer, We do
// these allocations four at a time
if (HandleTable->Table[i] == NULL) {
NewMidLevel = ExAllocatePoolWithTag( PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
4 * sizeof(ULONG_PTR) * 256,
'btbO' );
RtlZeroMemory( NewMidLevel, 4 * sizeof(ULONG_PTR) * 256 );
if (HandleTable->QuotaProcess != NULL) {
PsChargePoolQuota( HandleTable->QuotaProcess,
PagedPool,
(4 * sizeof(ULONG_PTR) * 256) );
MidTableQuotaCharged = TRUE;
}
}
// Now Allocate two new table entry buffers
NewLowLevel = ExAllocatePoolWithTag( PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
2 * sizeof(HANDLE_TABLE_ENTRY) * 256,
'btbO' );
RtlZeroMemory( NewLowLevel, 2 * sizeof(HANDLE_TABLE_ENTRY) * 256 );
if (HandleTable->QuotaProcess != NULL) {
PsChargePoolQuota( HandleTable->QuotaProcess,
PagedPool,
(2 * sizeof(HANDLE_TABLE_ENTRY) * 256) );
LowTableQuotaCharged = TRUE;
}
// Now to guard against reordering of code we need to do
// something to ensure that the last zero memory really has
// happened before setting up the rest of our work. An
// interlocked exchance will do the trick.
InterlockedExchangePointer( (PVOID *) NewLowLevel, NULL );
// Now if we've allocated a new mid level buffer we need to
// update the pointers from our handle table. The if's are
// needed when we get near the end of the table
if (NewMidLevel != NULL) {
if (i+0 < 256) { HandleTable->Table[i+0] = (PVOID)(NewMidLevel + 0 * sizeof(ULONG_PTR) * 256); }
if (i+1 < 256) { HandleTable->Table[i+1] = (PVOID)(NewMidLevel + 1 * sizeof(ULONG_PTR) * 256); }
if (i+2 < 256) { HandleTable->Table[i+2] = (PVOID)(NewMidLevel + 2 * sizeof(ULONG_PTR) * 256); }
if (i+3 < 256) { HandleTable->Table[i+3] = (PVOID)(NewMidLevel + 3 * sizeof(ULONG_PTR) * 256); }
}
// Now fix up the low level buffers. We are actually guaranteed
// that the first buffer will fit, because j starts out from a
// byte.
{ HandleTable->Table[i][j+0] = (PVOID)(NewLowLevel + 0 * sizeof(HANDLE_TABLE_ENTRY) * 256); }
if (j+1 < 256) { HandleTable->Table[i][j+1] = (PVOID)(NewLowLevel + 1 * sizeof(HANDLE_TABLE_ENTRY) * 256); }
// 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.
// Start by starting our stack top
HandleTable->FirstFreeTableEntry = HandleTable->NextIndexNeedingPool;
// Do the guaranteed first buffer
for (k = 0; k < 256; k += 1) {
HandleTable->Table[i][j+0][k].NextFreeTableEntry = HandleTable->NextIndexNeedingPool + k + 1;
}
// Check if there is a second buffer
if ((j+1 < 256) && (HandleTable->Table[i][j+1] != NULL)) {
// Do the second buffer
for (k = 0; k < 255; k += 1) {
HandleTable->Table[i][j+1][k].NextFreeTableEntry = HandleTable->NextIndexNeedingPool + k + 1 + 256;
}
// Fixup the last entry and update the next index needing
// pool.
HandleTable->Table[i][j+1][255].NextFreeTableEntry = -1;
HandleTable->NextIndexNeedingPool += 512;
} else {
// Fixup the last entry and update the next index needing
// pool.
HandleTable->Table[i][j+0][255].NextFreeTableEntry = -1;
HandleTable->NextIndexNeedingPool += 256;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
// We only execute this exception handler if the pool allocation
// raises or if the quota request fails. In these cases we need
// to cleanup after ourselves and return null to our caller
if (NewMidLevel != NULL) {
ExFreePool( NewMidLevel );
if (MidTableQuotaCharged) {
PsReturnPoolQuota( HandleTable->QuotaProcess,
PagedPool,
(4 * sizeof(ULONG_PTR) * 256) );
}
}
if (NewLowLevel != NULL) {
ExFreePool( NewLowLevel );
if (LowTableQuotaCharged) {
PsReturnPoolQuota( HandleTable->QuotaProcess,
PagedPool,
(2 * sizeof(HANDLE_TABLE_ENTRY) * 256) );
}
}
Handle->Index = 0;
return NULL;
}
}
// At this point the free stack has some elements in it. So we
// only need to pop off the first entry.
// Get the top of the stack, both the index and table entry
Handle->Index = HandleTable->FirstFreeTableEntry;
HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
*Handle );
// Do the pop
HandleTable->FirstFreeTableEntry = HandleTableEntry->NextFreeTableEntry;
// Zero out the new table entry
RtlZeroMemory( HandleTableEntry, sizeof(HANDLE_TABLE_ENTRY ));
// Update our bookkeeping counters and return the entry to our
// caller
HandleTable->HandleCount += 1;
return HandleTableEntry;
}
// Local Support Routine
// The following is a global variable only present in the checked builds
// to help catch apps that reuse handles 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
IN
)
/*++
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.
--*/
{
PAGED_CODE();
// 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.
#if DBG
if (ExReuseHandles) {
#endif //DBG
HandleTableEntry->NextFreeTableEntry = HandleTable->FirstFreeTableEntry;
HandleTable->FirstFreeTableEntry = Handle.Index;
#if DBG
} else {
HandleTableEntry->NextFreeTableEntry = 0;
}
#endif //DBG
// And then update our handle count before returning to our caller
HandleTable->HandleCount -= 1;
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 i,j,k,l;
PAGED_CODE();
// Decode the handle index into its separate table indicies
l = (Handle.Index >> 24) & 255;
i = (Handle.Index >> 16) & 255;
j = (Handle.Index >> 8) & 255;
k = (Handle.Index) & 255;
// The last bits should be 0 into a valid handle. If a function calls
// ExpLookupHandleTableEntry for a kernel handle, it should decode the handle
// before.
if ( l != 0 ) {
// Invalid handle. Return a NULL table entry.
return NULL;
}
// Check that the top level table is present
if (HandleTable->Table[i] == NULL) {
return NULL;
}
// Check that the mid level table is present
if (HandleTable->Table[i][j] == NULL) {
return NULL;
}
return &(HandleTable->Table[i][j][k]);// Return a pointer to the table entry
}