Windows2003-3790/inetcore/wininet/common/handle.cxx
2020-09-30 16:53:55 +02:00

1029 lines
28 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
handle.cxx
Abstract:
Contains functions to allocate and deallocate handle values for various
Windows Internet Extensions DLL 'objects'
Functions in this module generate pseudo-handle values and free them when
no longer required. Each handle value is generated from its position within
a (2K) bitmap (== 16K handles max.). We also maintain an array that is used
to map the generated handle to the address of the handle object that it
represents
Contents:
HandleInitialize
HandleTerminate
AllocateHandle
FreeHandle
MapHandleToAddress
DereferenceObject
(BitToIndex)
Author:
Richard L Firth (rfirth) 31-Oct-1994
Revision History:
11-Jan-1996 rfirth
Use fixed memory instead of moveable (Win95 has a bug w/ LocalUnlock)
31-Oct-1994 rfirth
Created
--*/
#include <wininetp.h>
//
// manifests
//
#define BASE_HANDLE_VALUE 0x00cc0000
#define HANDLE_INCREMENT 4
#define BITS_PER_BYTE 8
#define BITS_IN_DWORD (sizeof(DWORD) * BITS_PER_BYTE)
#define FULL_DWORD ((DWORD)-1)
#define MAXIMUM_HANDLE_NUMBER (64 K)
#define MAXIMUM_HANDLE_COUNT ((MAXIMUM_HANDLE_NUMBER / HANDLE_INCREMENT) - 1)
#define MAXIMUM_DWORD_INDEX ((MAXIMUM_HANDLE_COUNT + BITS_IN_DWORD - 1) / BITS_IN_DWORD)
#define MINIMUM_HANDLE_VALUE (BASE_HANDLE_VALUE + HANDLE_INCREMENT)
#define MAXIMUM_HANDLE_VALUE (BASE_HANDLE_VALUE + MAXIMUM_HANDLE_NUMBER - HANDLE_INCREMENT)
#define INITIAL_MAP_LENGTH 16 // 512 handles == 2048 bytes
#define HANDLE_MAP_INCREMENT 16
// Warning: In order for 64-bit compatibility, the range of handle
// values must be restricted to quantities representable by 32-bits.
// If maximum handle value >= 4GB the implementation has to change
// use 64-bit integral types internally.
//
// macros
//
#define NEXT_HANDLE_VALUE(d, i) (BASE_HANDLE_VALUE + ((d) * BITS_IN_DWORD + ((i) + 1)) * HANDLE_INCREMENT)
//
// private prototypes
//
PRIVATE
inline
DWORD
BitToIndex(
IN DWORD Bit
);
//
// private data
//
PRIVATE CRITICAL_SECTION HandleMapCritSec; // protects access to following variables
PRIVATE LPDWORD HandleMap = NULL; // bitmap of allocated handles
PRIVATE DWORD HandleMapLength; // number of DWORDs in HandleMap
PRIVATE DWORD NextHandleMapDword = 0; // first bitmap DWORD to check
PRIVATE DWORD NextHandleMapBit = 1; // map of first bit to check
PRIVATE DWORD NextHandleMapBitIndex = 0; // position of first bit in first DWORD
PRIVATE DWORD NextHandleValue = MINIMUM_HANDLE_VALUE;
PRIVATE DWORD NextHandleIndex = 0;
PRIVATE BOOL Initialized = FALSE;
PRIVATE LPVOID * MapArray = NULL;
PRIVATE DWORD MapArrayLength = 0;
//
// functions
//
DWORD
HandleInitialize(
VOID
)
/*++
Routine Description:
Performs initialization required by functions in this module
Arguments:
None.
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - return code from LocalAlloc
--*/
{
DEBUG_ENTER((DBG_HANDLE,
Dword,
"HandleInitialize",
NULL
));
InitializeCriticalSection(&HandleMapCritSec);
HandleMapLength = INITIAL_MAP_LENGTH;
DWORD error;
//
// ResizeBuffer() doesn't use LMEM_ZEROINIT
//
HandleMap = (LPDWORD)ALLOCATE_MEMORY(LMEM_ZEROINIT,
HandleMapLength * sizeof(DWORD)
);
if (HandleMap != NULL) {
MapArrayLength = INITIAL_MAP_LENGTH * BITS_IN_DWORD;
MapArray = (LPVOID *)ALLOCATE_MEMORY(LMEM_ZEROINIT,
MapArrayLength * sizeof(LPVOID)
);
if (MapArray != NULL) {
Initialized = TRUE;
error = ERROR_SUCCESS;
}
}
if (!HandleMap || !MapArray) {
error = GetLastError();
HandleTerminate();
}
DEBUG_LEAVE(error);
return error;
}
VOID
HandleTerminate(
VOID
)
/*++
Routine Description:
Obverse of HandleInitialize - frees any system resources allocated by
HandleInitialize
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_HANDLE,
None,
"HandleTerminate",
NULL
));
if (Initialized) {
//
// there shouldn't be any other threads active when this function is
// called but we'll grab the critical section anyway, just to make sure
//
EnterCriticalSection(&HandleMapCritSec);
//
// free up the memory occupied by the handle bitmap and map array
//
if (HandleMap != NULL) {
HandleMap = (LPDWORD)FREE_MEMORY((HLOCAL)HandleMap);
}
INET_ASSERT(HandleMap == NULL);
if (MapArray != NULL) {
MapArray = (LPVOID *)FREE_MEMORY((HLOCAL)MapArray);
}
INET_ASSERT(MapArray == NULL);
//
// no longer initialized
//
Initialized = FALSE;
//
// and reset the variables
//
HandleMapLength = 0;
NextHandleMapDword = 0;
NextHandleMapBit = 1;
NextHandleMapBitIndex = 0;
NextHandleValue = MINIMUM_HANDLE_VALUE;
NextHandleIndex = 0;
MapArrayLength = 0;
LeaveCriticalSection(&HandleMapCritSec);
//
// delete the critical section
//
DeleteCriticalSection(&HandleMapCritSec);
}
DEBUG_LEAVE(0);
}
DWORD
AllocateHandle(
IN LPVOID Address,
OUT LPHINTERNET lpHandle
)
/*++
Routine Description:
Generic handle allocator function which generates a unique handle value for
any object. The handle value is simply a number, slightly massaged to give a
value that can be easily differentiated from other Win32 handle ranges (for
debugging purposes mainly, and also to protect against illegal use of
handles (such as treating as a pointer and dereferencing it)).
The range of handles are kept in a bitmap, composed of DWORDs. The next
allocated handle is simply the index of the next free bit in the map. The
map is extended if we run out of handles (up to 16K-1 handles (== 2K bytes)).
If the map needs to be extended we don't go to the trouble to reduce it
again. For the sake of 2K bytes max (currently), its not worth the effort.
This function does not rely on knowing the type of object for which the
handle is being generated, and only requires serialization via a critical
section.
This function can increase the values of these variables:
NextHandleMapDword
NextHandleMapBit
NextHandleMapBitIndex
NextHandleValue
NextHandleIndex
Arguments:
Address - the (object address) value which will be associated with the
returned handle
lpHandle - place to return the allocated handle
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INTERNET_OUT_OF_HANDLES
16K-1 (currently) handles are outstanding!
ERROR_NOT_ENOUGH_MEMORY etc.
problems with Win32 memory/heap management?
--*/
{
DEBUG_ENTER((DBG_HANDLE,
Dword,
"AllocateHandle",
"%#x, %#x",
Address,
lpHandle
));
DWORD error;
//
// can't associate a NULL address with the generated handle
//
INET_ASSERT(Address != NULL);
//
// default returned handle
//
*lpHandle = NULL;
//
// in case this function is being called before we have initialized this
// module, or after we have terminated it, return an error
//
if (!Initialized) {
error = ERROR_INTERNET_SHUTDOWN;
goto quit;
}
error = ERROR_INTERNET_OUT_OF_HANDLES;
EnterCriticalSection(&HandleMapCritSec);
if (HandleMap == NULL) {
//
// don't ever expect this?
//
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto unlock_exit;
}
while (NextHandleMapDword != MAXIMUM_DWORD_INDEX) {
//
// if we reached the end of the map the last time, we must reallocate
//
if (NextHandleMapDword == HandleMapLength) {
HLOCAL newHandleMap;
newHandleMap = REALLOCATE_MEMORY((HLOCAL)HandleMap,
(HandleMapLength + HANDLE_MAP_INCREMENT)
* sizeof(DWORD),
LMEM_MOVEABLE | LMEM_ZEROINIT
);
HLOCAL newMapArray;
newMapArray = REALLOCATE_MEMORY((HLOCAL)MapArray,
(HandleMapLength + HANDLE_MAP_INCREMENT)
* BITS_IN_DWORD
* sizeof(LPVOID),
LMEM_MOVEABLE | LMEM_ZEROINIT
);
if ((newHandleMap != NULL) && (newMapArray != NULL)) {
HandleMapLength += HANDLE_MAP_INCREMENT;
HandleMap = (LPDWORD)newHandleMap;
MapArrayLength += HANDLE_MAP_INCREMENT * BITS_IN_DWORD;
MapArray = (LPVOID *)newMapArray;
DEBUG_PRINT(HANDLE,
INFO,
("re-allocated %d DWORDs: HandleMap = %#x MapArray = %#x\n",
HandleMapLength,
HandleMap,
MapArray
));
} else {
error = GetLastError();
DEBUG_PRINT(HANDLE,
ERROR,
("REALLOCATE_MEMORY() returns %d\n",
error
));
break;
}
} else if (NextHandleValue <= MAXIMUM_HANDLE_VALUE) {
HandleMap[NextHandleMapDword] |= NextHandleMapBit;
//
// first handle value returned is 0x00cc0004
//
DEBUG_PRINT(HANDLE,
INFO,
("handle = %#x, index = %d\n",
NextHandleValue,
NextHandleIndex
));
*lpHandle = (HINTERNET)(DWORD_PTR)NextHandleValue;
//
// store it in the map array at the specified index
//
INET_ASSERT(MapArray[NextHandleIndex] == NULL);
MapArray[NextHandleIndex] = Address;
error = ERROR_SUCCESS;
//
// find the next available bit for the next caller. Search up
// to the end of the currently allocated map. If we don't find
// it, the next caller will attempt to allocate a new DWORD
// (unless a lower handle gets freed meantime)
//
while (NextHandleMapDword < HandleMapLength) {
if (HandleMap[NextHandleMapDword] != FULL_DWORD) {
while (HandleMap[NextHandleMapDword] & NextHandleMapBit) {
NextHandleMapBit <<= 1;
++NextHandleMapBitIndex;
NextHandleValue += HANDLE_INCREMENT;
++NextHandleIndex;
}
} else {
NextHandleMapBit = 0;
}
if (NextHandleMapBit != 0) {
break;
} else {
//
// reached the end of a DWORD. Start the next
//
++NextHandleMapDword;
NextHandleMapBit = 1;
NextHandleMapBitIndex = 0;
//
// recalculate the next handle value
//
NextHandleValue = NEXT_HANDLE_VALUE(NextHandleMapDword,
NextHandleMapBitIndex
);
//
// and map array index
//
NextHandleIndex = NextHandleMapDword * BITS_IN_DWORD;
}
}
break;
} else {
//
// reached maximum handle value - return error
//
break;
}
}
unlock_exit:
LeaveCriticalSection(&HandleMapCritSec);
quit:
DEBUG_LEAVE(error);
return error;
}
DWORD
FreeHandle(
IN HINTERNET Handle
)
/*++
Routine Description:
The obverse function to AllocateHandle. Frees up a previously allocated
handle value. If this handle has a lower index than the currently selected
next index (Dword and Bit) then the next index is modified
This function can reduce the values of these variables:
NextHandleMapDword
NextHandleMapBit
NextHandleMapBitIndex
NextHandleValue
NextHandleIndex
Arguments:
Handle - handle value previously allocated via AllocateHandle
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_HANDLE
We don't think this handle was generated by AllocateHandle
or the corresponding bit in the map is already 0
--*/
{
DEBUG_ENTER((DBG_HANDLE,
Dword,
"FreeHandle",
"%#x",
Handle
));
DWORD error;
//
// ensure that we are in the correct state
//
if (!Initialized) {
error = ERROR_INTERNET_SHUTDOWN;
goto quit;
}
error = ERROR_INVALID_HANDLE;
//
// error if 0x00cc0000 > Handle > 0x00ccfffc
//
if ((PtrToUlong(Handle) < MINIMUM_HANDLE_VALUE)
|| (PtrToUlong(Handle) > MAXIMUM_HANDLE_VALUE)) {
goto quit;
}
//
// get the map DWORD index and bit mask from the handle
//
DWORD index;
index = (PtrToUlong(Handle) - MINIMUM_HANDLE_VALUE) / (BITS_IN_DWORD * HANDLE_INCREMENT);
DWORD bit;
bit = 1 << (((PtrToUlong(Handle) - MINIMUM_HANDLE_VALUE) / HANDLE_INCREMENT) & (BITS_IN_DWORD - 1));
DWORD mapIndex;
mapIndex = (PtrToUlong(Handle) - MINIMUM_HANDLE_VALUE) / HANDLE_INCREMENT;
EnterCriticalSection(&HandleMapCritSec);
//
// the index may be in range, but greater than the currently allocated
// map length, in which case its an error
//
if (index < HandleMapLength) {
if (HandleMap != NULL) {
if (HandleMap[index] & bit) {
BOOL recalc = FALSE;
DEBUG_PRINT(HANDLE,
INFO,
("handle = %#x, index = %d, address = %#x\n",
Handle,
mapIndex,
MapArray[mapIndex]
));
HandleMap[index] &= ~bit;
MapArray[mapIndex] = NULL;
error = ERROR_SUCCESS;
//
// if we have cleared a bit lower in the bitmap than the current
// index and bit indicators, then reset the indicators to the
// new position
//
if (index < NextHandleMapDword) {
//
// new DWORD is lower than current: change all variables
// to those for this handle
//
NextHandleMapDword = index;
recalc = TRUE;
} else if ((index == NextHandleMapDword) && (bit < NextHandleMapBit)) {
//
// same DWORD index, lower bit position
//
recalc = TRUE;
}
if (recalc) {
NextHandleMapBit = bit;
NextHandleMapBitIndex = BitToIndex(bit);
//
// recalculate the next handle value
//
NextHandleValue = NEXT_HANDLE_VALUE(NextHandleMapDword,
NextHandleMapBitIndex
);
//
// and map array index
//
NextHandleIndex = (NextHandleMapDword * BITS_IN_DWORD)
+ NextHandleMapBitIndex;
}
} else {
DEBUG_PRINT(HANDLE,
ERROR,
("Handle = %#x. HandleMap[%d].%#x not set\n",
Handle,
index,
bit
));
}
} else {
//
// don't ever expect this to happen
//
error = ERROR_INTERNET_INTERNAL_ERROR;
INET_ASSERT(FALSE);
}
} else {
DEBUG_PRINT(HANDLE,
ERROR,
("Handle = %#x, index = %d, HandleMapLength = %d\n",
Handle,
index,
HandleMapLength
));
}
LeaveCriticalSection(&HandleMapCritSec);
quit:
DEBUG_LEAVE(error);
return error;
}
DWORD
MapHandleToAddress(
IN HINTERNET Handle,
OUT LPVOID * lpAddress,
IN BOOL Invalidate
)
/*++
Routine Description:
Given a handle, retrieve its associated address from the map array. The
handle object represented by Handle is referenced
Assumes: 1. only HINTERNETs visible at the API are presented to this
function. Even though we AllocateHandle() for arbitrary
objects (e.g. gopher views) we never map their addresses
Arguments:
Handle - handle value generated by AllocateHandle()
lpAddress - place to store mapped address. If the handle has been closed
and unmapped, NULL is returned. If the handle is still
mapped, even though it has been invalidated, its address will
be returned, and its reference count incremented
Invalidate - TRUE if we are invalidating this handle
Return Value:
LPVOID
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_HANDLE
if *lpAddress == NULL then the handle has been closed and
unmapped, else it is still mapped, but invalidated. In
this case, we incremented the reference count
--*/
{
DEBUG_ENTER((DBG_HANDLE,
Dword,
"MapHandleToAddress",
"%#x, %#x, %B",
Handle,
lpAddress,
Invalidate
));
LPVOID address = NULL;
DWORD error = ERROR_INVALID_HANDLE;
//
// error if 0x00cc0000 > Handle > 0x00ccfffc
//
if ((PtrToUlong(Handle) >= MINIMUM_HANDLE_VALUE)
&& (PtrToUlong(Handle) <= MAXIMUM_HANDLE_VALUE)
&& !InDllCleanup) {
DWORD index = (PtrToUlong(Handle) - MINIMUM_HANDLE_VALUE) / HANDLE_INCREMENT;
//
// the caller could have supplied a value which has the correct range
// but may not yet have been generated, causing us to index past the
// end of the array
//
if (index < MapArrayLength) {
//
// we have to acquire the critical section in case another thread
// is reallocating the array
//
EnterCriticalSection(&HandleMapCritSec);
address = MapArray[index];
DEBUG_PRINT(HANDLE,
INFO,
("Handle %#x mapped to address %#x\n",
Handle,
address
));
if (address != NULL) {
//
// although we store addresses of arbitrary structures (e.g.
// FTP_SESSION_INFO), we are only calling this function to map
// pseudo-handles to object addresses at the API. Therefore it
// should be safe to assume that the pointer references a handle
// object
// However, there's nothing to stop an app passing in a random
// handle value that just happens to map to an FTP or gopher
// session or gopher view, and since we don't want to treat that
// as a handle object, we must make this test full-time
//
if (((HANDLE_OBJECT *)address)->IsValid(TypeWildHandle) == ERROR_SUCCESS) {
//
// this is also a very good time to increment the reference
// count. We are using the fact that we are serialized on the
// handle map critical section here. If Reference() returns
// ERROR_INVALID_HANDLE then the handle object has been
// invalidated, but its reference count will have been
// incremented. The caller should perform as little work as
// necessary and get out.
//
// If Refrerence() returns ERROR_ACCESS_DENIED, then the object
// is being destroyed (refcount already went to zero).
//
// If the reference count is incremented to 1 then there is
// another thread waiting to finish deleting this handle. It
// is virtually deleted, and if we return its address, the
// caller will have a deleted object
//
if (((HANDLE_OBJECT *)address)->ReferenceCount() == 0) {
DEBUG_PRINT(HANDLE,
ERROR,
("handle %#x [%#x] about to be deleted\n",
Handle,
address
));
address = NULL;
} else {
error = ((HANDLE_OBJECT *)address)->Reference();
if (error == ERROR_SUCCESS) {
if (Invalidate) {
//
// we were called from a handle close API.
// Subsequent API calls will discover that the
// handle is already invalidated and will quit
//
((HANDLE_OBJECT *)address)->Invalidate();
}
} else if (error == ERROR_ACCESS_DENIED) {
//
// if we get ERROR_ACCESS_DENIED, this means that the object is
// being destructed, so we *have* to return NULL.
//
DEBUG_PRINT(HANDLE,
ERROR,
("Reference() failed - handle %#x [%#x] about to be deleted\n",
Handle,
address
));
address = NULL;
} else {
DEBUG_PRINT(HANDLE,
ERROR,
("Reference() returns %d\n",
error
));
//
// if invalid and reference count already zero, we
// didn't increment ref count: handle already being
// deleted
//
if (((HANDLE_OBJECT *)address)->ReferenceCount() == 0) {
address = NULL;
}
}
}
} else {
//
// we still want to know about it in debug version
//
DEBUG_PRINT(HANDLE,
ERROR,
("invalid handle object: %#x [%#x]\n",
Handle,
address
));
IF_DEBUG(INVALID_HANDLES) {
//INET_ASSERT(FALSE);
}
}
} else {
//
// lets also catch this one (NULL address)
//
DEBUG_PRINT(HANDLE,
ERROR,
("NULL handle: %#x\n",
Handle
));
IF_DEBUG(INVALID_HANDLES) {
//INET_ASSERT(FALSE);
}
}
LeaveCriticalSection(&HandleMapCritSec);
}
} else if (InDllCleanup) {
error = ERROR_INTERNET_SHUTDOWN;
} else {
DEBUG_PRINT(HANDLE,
ERROR,
("bad handle value: %#x\n",
Handle
));
IF_DEBUG(INVALID_HANDLES) {
//INET_ASSERT(FALSE);
}
}
*lpAddress = address;
DEBUG_LEAVE(error);
return error;
}
DWORD
DereferenceObject(
IN LPVOID lpObject
)
/*++
Routine Description:
Undoes the reference added to the handle object by MapHandleToAddress(). May
result in the handle object being deleted
Arguments:
lpObject - address of object to dereference. This MUST be the mapped
object address as returned by MapHandleToAddress()
Return Value:
DWORD
Success - ERROR_SUCCESS
The handle object was destroyed
Failure - ERROR_INVALID_HANDLE
The object was not a valid handle
ERROR_INTERNET_HANDLE_EXISTS
The handle is still alive
--*/
{
DEBUG_ENTER((DBG_HANDLE,
Dword,
"DereferenceObject",
"%#x",
lpObject
));
INET_ASSERT(lpObject != NULL);
HANDLE_OBJECT * object = (HANDLE_OBJECT *)lpObject;
DWORD error = object->IsValid(TypeWildHandle);
if (error == ERROR_SUCCESS) {
if (!object->Dereference()) {
error = ERROR_INTERNET_HANDLE_EXISTS;
}
} else {
//
// IsValid() should never return an error if the reference counts
// are correct
//
INET_ASSERT(FALSE);
}
DEBUG_LEAVE(error);
return error;
}
PRIVATE
inline
DWORD
BitToIndex(
IN DWORD Bit
)
/*++
Routine Description:
Returns the index of the first bit set in a DWORD
Arguments:
Bit - bitmap
Return Value:
DWORD - 0..(BITS_IN_DWORD - 1) if bit found, else BITS_IN_DWORD
--*/
{
if (Bit == 0) {
return BITS_IN_DWORD;
} else {
DWORD index;
DWORD testBit;
for (index = 0, testBit = 1; !(Bit & testBit); ++index, testBit <<= 1) {
;
}
return index;
}
}