|
|
/******************************Module*Header*******************************\
* Module Name: hmgrapi.cxx * * Handle manager API entry points * * Created: 08-Dec-1989 23:03:03 * Author: Donald Sidoroff [donalds] * * Copyright (c) 1989-1999 Microsoft Corporation \**************************************************************************/
#include "precomp.hxx"
//
// Synchronization of the handle manager
//
HSEMAPHORE ghsemHmgr;
PDEVCAPS gpGdiDevCaps = NULL;
//
// Points to shared memory including handle table and cfont cache
//
PGDI_SHARED_MEMORY gpGdiSharedMemory;
//
// Points to handle array
//
ENTRY *gpentHmgr=NULL;
//
// Free List of handles
//
HOBJ ghFreeHmgr;
//
// Max handle alloced so far
//
ULONG gcMaxHmgr;
//
// Look aside list headers and maximum allowed entry-size array
PPAGED_LOOKASIDE_LIST pHmgLookAsideList[MAX_TYPE + 1]; ULONG laSize[MAX_TYPE + 1]; #define HMG_LAL_TAG '0alG'
//
//
//
extern "C" { HFASTMUTEX ghfmMemory; }
PVOID gpHmgrSharedHandleSection;
//
// 10 millisecond wait value
//
// We only have a pointer to it since it must be allocated out of Non-paged
// pool
//
PLARGE_INTEGER gpLockShortDelay;
//
// Hydra session flag
//
// Note that this only has bearing on whether process is a hydra session
// or not. It is possible to not be a hydra session, but still be a
// a hydra server (check gIsTerminalServer for that information).
//
// Value is TRUE if not a hydra session.
// Value is FALSE if a hydra session.
//
extern BOOL G_fConsole;
//
// Short term temporary buffer that can be used to put large
// objects for which we have no place in the stack.
//
// The value should only be used through the Alloc\FreeTmpBuffer functions
//
#define TMP_GLOBAL_BUFFER_SIZE 0x1000
PVOID *gpTmpGlobalFree; PVOID gpTmpGlobal = NULL;
// Number of DCATTR blocks in 1 page
#define ULNUMDC ((PAGE_SIZE)/sizeof(DC_ATTR))
// Number of OBJECTATTR blocks in 1 page
#define ULNUMBRUSH ((PAGE_SIZE)/sizeof(OBJECTATTR))
typedef struct _DCAFLISTENTRY { LIST_ENTRY listEntry; ULONG cFree; PDC_ATTR* a[ULNUMDC];
} DCAFLISTENTRY, *PDCAFLISTENTRY;
typedef struct _OBAFLISTENTRY { LIST_ENTRY listEntry; ULONG cFree; POBJECTATTR* a[ULNUMBRUSH];
} OBAFLISTENTRY, *POBAFLISTENTRY;
//
// Prototype for a handy debugging routine.
//
#if DBG
extern "C" VOID HmgPrintBadHandle( HOBJ hobj, OBJTYPE objt );
#else
#define HmgPrintBadHandle(hobj, objt)
#endif
//
// During initialization, some things which are really really bad
// (like not being able to allocate the handle manager table) and
// we RIP because we cannot proceed and its completely unexpected.
//
// However, on a Hydra system, some of these are things we need
// to expect. So what would be a RIP on a non-Hydra system can
// be treated as a WARNING on a Hydra system
//
#if DBG
#define NONHYDRA_RIP(a) \
{ \ if (G_fConsole) \ RIP(a); \ else \ WARNING(a); \ } #else
#define NONHYDRA_RIP(a)
#endif
/**************************************************************************\
* * Fast tempporary memory allocator * \**************************************************************************/
PVOID AllocFreeTmpBuffer( ULONG size) { PVOID tmpPtr = NULL;
if (size <= TMP_GLOBAL_BUFFER_SIZE) { tmpPtr = (PVOID) InterlockedExchangePointer((PVOID *)gpTmpGlobalFree, 0); }
if (!tmpPtr) { tmpPtr = AllocThreadBufferWithTag(size,'pmTG'); }
return tmpPtr; }
VOID FreeTmpBuffer( PVOID pv) { if (pv == gpTmpGlobal) { ASSERTGDI(*gpTmpGlobalFree == NULL, "GRE: gpTmpGlobalFree is inconsistent\n"); *gpTmpGlobalFree = pv; } else { FreeThreadBufferWithTag(pv); } }
/**************************************************************************\
* * Thread specific memory allocator. Memory is added to tracking list in * the thread and cleaned up if its still in the list when the thread dies. * \**************************************************************************/ PVOID AllocThreadBufferWithTag( ULONG size, ULONG tag) { PVOID tmpPtr = NULL;
if (size < MAXULONG - sizeof(LIST_ENTRY)) { tmpPtr = PALLOCNOZ(size + sizeof(LIST_ENTRY),tag);
if (tmpPtr) { PW32THREAD pThread = W32GetCurrentThread();
ASSERTGDI(pThread, "GRE: AllocThreadBuffer W32Thread is NULL\n");
if (pThread) { PLIST_ENTRY pEntry = (PLIST_ENTRY)tmpPtr;
InsertHeadList(&pThread->GdiTmpAllocList, pEntry);
tmpPtr = (PVOID)(pEntry + 1); } } }
return tmpPtr; }
VOID FreeThreadBufferWithTag( PVOID pv) { PW32THREAD pThread = W32GetCurrentThread();
ASSERTGDI(pThread, "GRE: FreeThreadBuffer W32Thread is NULL\n");
if (pThread) { PLIST_ENTRY pFree = (PLIST_ENTRY)pv;
RemoveEntryList(pFree-1);
VFREEMEM(pFree-1); } }
/**************************************************************************\
* * Performance, hit rate, memory size statistics * \**************************************************************************/
#if DBG
#define GDI_PERF 1
#endif
#if GDI_PERF
extern "C" { // these must be extern "C" so the debugger extensions can see them
ULONG HmgCurrentNumberOfObjects[MAX_TYPE + 1]; ULONG HmgMaximumNumberOfObjects[MAX_TYPE + 1]; ULONG HmgCurrentNumberOfLookAsideObjects[MAX_TYPE + 1]; ULONG HmgMaximumNumberOfLookAsideObjects[MAX_TYPE + 1]; ULONG HmgNumberOfObjectsAllocated[MAX_TYPE + 1]; ULONG HmgNumberOfLookAsideHits[MAX_TYPE + 1];
ULONG HmgCurrentNumberOfHandles[MAX_TYPE + 1]; ULONG HmgMaximumNumberOfHandles[MAX_TYPE + 1]; ULONG HmgNumberOfHandlesAllocated[MAX_TYPE + 1]; };
#endif
/*****************************Exported*Routine*****************************\
* HmgCreate() * * Initializes a new handle manager. * * History: * * Mon 14-Apr-1997 -by- Dan Almosnino [danalm] * Modified Lookaside initialization to use DaveC S-Lists * and set the allowable object size on the list based on memory model * * Wed 29-Apr-1992 -by- Patrick Haluptzok [patrickh] * Change to mutex for exclusion, init event here. * * Mon 21-Oct-1991 -by- Patrick Haluptzok [patrickh] * Reserve memory for the handle table so unlock doesn't need semaphore. * * Mon 08-Jul-1991 -by- Patrick Haluptzok [patrickh] * make 0 an invalid handle * * Sun 20-Jun-1991 -by- Patrick Haluptzok [patrickh] * The hheap has gone away, the correct error codes are logged. * * 30-Nov-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL HmgCreate() {
ULONG ulType; ULONG Size; BOOL LargeSystemSize = FALSE;
#if 0
KdPrint((" MASK CBITS SHIFT\n")); KdPrint(("INDEX = 0x%8.8lx, %3d %3d \n",INDEX_MASK ,INDEX_BITS ,INDEX_SHIFT)); KdPrint(("TYPE = 0x%8.8lx, %3d %3d \n",TYPE_MASK ,TYPE_BITS ,TYPE_SHIFT )); KdPrint(("ALTTYPE = 0x%8.8lx, %3d %3d \n",ALTTYPE_MASK ,ALTTYPE_BITS ,ALTTYPE_SHIFT )); KdPrint(("STOCK = 0x%8.8lx, %3d %3d \n",STOCK_MASK ,STOCK_BITS ,STOCK_SHIFT )); KdPrint(("UNIQUE = 0x%8.8lx, %3d %3d \n",UNIQUE_MASK ,UNIQUE_BITS ,UNIQUE_SHIFT )); KdPrint(("LOTYPE = 0x%8.8lx, %3d %3d \n",LOTYPE_MASK ,LOTYPE_BITS ,LOTYPE_SHIFT )); KdPrint(("FULLTYPE = 0x%8.8lx, %3d %3d \n",FULLTYPE_MASK ,FULLTYPE_BITS ,FULLTYPE_SHIFT )); KdPrint(("FULLUNIQUE = 0x%8.8lx, %3d %3d \n",FULLUNIQUE_MASK,FULLUNIQUE_BITS,FULLUNIQUE_SHIFT )); KdPrint(("\n"));
#endif
//
// Initialize exclusion stuff.
//
if ((ghsemHmgr = GreCreateSemaphore()) == NULL) return FALSE;
//
// Initialize the handle manager allocation database.
//
ghFreeHmgr = 0; // No free handles
gcMaxHmgr = HMGR_HANDLE_BASE; // Initialize with handle index base
//
// Initialize the allocation of lookaside lists for selected objects.
//
// Initialize the maximum allowed entry-size for each object to zero
if (MmMediumSystem <= MmQuerySystemSize()) LargeSystemSize = TRUE;
for (ulType = 0; ulType < MAX_TYPE + 1; ulType++) { laSize[ulType] = 0; }
// Now Initialize the Lookaside lists
Size = (LargeSystemSize == TRUE)? HMG_DC_MAX : HMG_DC_SIZE; if (HmgInitializeLookAsideList(DC_TYPE, Size, HMG_LAL_TAG, HMG_DC_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_RGN_MAX : HMG_RGN_SIZE); if (HmgInitializeLookAsideList(RGN_TYPE, Size, HMG_LAL_TAG, HMG_RGN_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_SURF_MAX : HMG_SURF_SIZE); if (HmgInitializeLookAsideList(SURF_TYPE, Size, HMG_LAL_TAG, HMG_SURF_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_PAL_MAX : HMG_PAL_SIZE); if (HmgInitializeLookAsideList(PAL_TYPE, Size, HMG_LAL_TAG, HMG_PAL_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_BRUSH_MAX : HMG_BRUSH_SIZE); if (HmgInitializeLookAsideList(BRUSH_TYPE, Size, HMG_LAL_TAG, HMG_BRUSH_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_LFONT_MAX : HMG_LFONT_SIZE); if (HmgInitializeLookAsideList(LFONT_TYPE, Size, HMG_LAL_TAG, HMG_LFONT_OBJECTS) == FALSE) return FALSE;
Size = (ULONG)((LargeSystemSize == TRUE)? HMG_RFONT_MAX : HMG_RFONT_SIZE); if (HmgInitializeLookAsideList(RFONT_TYPE, Size, HMG_LAL_TAG, HMG_RFONT_OBJECTS) == FALSE) return FALSE;
//
//
// Init mutex
//
// allocate and initialize memory fast mutex from non-paged pool
//
ghfmMemory = GreCreateFastMutex();
if (ghfmMemory == NULL) { NONHYDRA_RIP("HmgCreate failed to create ghfmMemory"); return(FALSE); }
//
// Create section for shared GDI handle table
//
#if defined(_GDIPLUS_)
{ //
// NOTE: _GDIPLUS_ is some work in progress code.
//
//
// This should probably be a reserve-and-commit
// We ceed to fix clean-up case, too
//
gpGdiSharedMemory = (GDI_SHARED_MEMORY*) PALLOCMEM(sizeof(GDI_SHARED_MEMORY), ' thG');
if (gpGdiSharedMemory == NULL) { RIP("Invalid handle table."); } } #else
{ NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeString; LARGE_INTEGER MaximumSize; HANDLE hHmgrSharedHandleTable;
ACCESS_MASK DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE;
ULONG SectionPageProtection = PAGE_READWRITE;
ULONG AllocationAttributes = SEC_COMMIT | SEC_NO_CHANGE;
MaximumSize.HighPart = 0; MaximumSize.LowPart = sizeof(GDI_SHARED_MEMORY);
ASSERTGDI((gpHmgrSharedHandleSection == NULL), "gpHmgrSharedHandleSection MUST be NULL");
Status = Win32CreateSection(&gpHmgrSharedHandleSection, DesiredAccess, NULL, &MaximumSize, SectionPageProtection, AllocationAttributes, NULL, NULL, TAG_SECTION_HMGR);
if (!NT_SUCCESS(Status)) { KdPrint(("Error in HmgCreate: Win32CreateSection returns %lx\n",Status)); NONHYDRA_RIP("Can't continue without shared handle section"); return(FALSE); } else { SIZE_T ViewSize = 0;
#ifdef _HYDRA_
//
// MmMapViewInSessionSpace is internally promoted to
// MmMapViewInSystemSpace on non-Hydra systems.
//
// Win32MapViewInSessionSpace is included for tracking
// Section objects. It's promoted to MmMapViewInSessionSpace
// after the tracking code.
//
Status = Win32MapViewInSessionSpace( gpHmgrSharedHandleSection, (PVOID*)&gpGdiSharedMemory, &ViewSize); #else
Status = MmMapViewInSystemSpace( gpHmgrSharedHandleSection, (PVOID*)&gpGdiSharedMemory, &ViewSize); #endif
if (!NT_SUCCESS(Status)) { KdPrint(("Error in HmgCreate: MmMapViewInSystemSpace returns %lx\n",Status)); NONHYDRA_RIP("Can't continue without shared handle section"); return(FALSE); } } } #endif
gpentHmgr = gpGdiSharedMemory->aentryHmgr; gpGdiDevCaps = &(gpGdiSharedMemory->DevCaps);
//
// Allocate the handle table and commit the initial allocation.
//
// N.B. Committed memory is demand zero.
//
//
if (gpentHmgr == NULL) { NONHYDRA_RIP("Hmgr-bInit failed to reserve the handle table memory\n"); return(FALSE); }
//
// allocate and initialize the timeout lock for the handle manager.
//
gpLockShortDelay = (PLARGE_INTEGER) GdiAllocPoolNonPaged(sizeof(LARGE_INTEGER), 'iniG');
if (gpLockShortDelay == NULL) { NONHYDRA_RIP("Hmgr-could not allocate memory for delay lock\n"); return(FALSE); }
gpLockShortDelay->LowPart = (ULONG) -100000; gpLockShortDelay->HighPart = -1;
//
// Create a short term temporary buffer that can be used to put large
// objects for which we have no place in the stack.
//
// We actually have to allocate the pointer to the buffer in non-paged
// pool so that we can do an Interlocked exchange safely on it
//
gpTmpGlobal = (PVOID)PALLOCNOZ(TMP_GLOBAL_BUFFER_SIZE, 'blgG'); gpTmpGlobalFree = (PVOID *)GdiAllocPoolNonPaged(sizeof(PVOID), 'iniG');
if ((gpTmpGlobal == NULL) || (gpTmpGlobalFree == NULL)) { NONHYDRA_RIP("Can't allocate process list entry"); return(FALSE); }
*gpTmpGlobalFree = gpTmpGlobal;
return(TRUE); }
/******************************Public*Routine******************************\
* InitializeLookAsideList * * Initializes a LookAside List for a selected object * Uses DaveC's S-Lists * * History: * 14-Apr-1997 -by- Dan Almosnino danalm * Wrote it \**************************************************************************/
PVOID StubGdiAlloc( POOL_TYPE PoolType, SIZE_T uBytes, ULONG iTag) { return GdiAllocPool((ULONG)uBytes, iTag);
UNREFERENCED_PARAMETER(PoolType); }
// Does anyone want to change this to #define ? if so please do so after win2000.
//
// #define StubGdiFree(p) GdiFreePool((p))
VOID StubGdiFree( PVOID p) { GdiFreePool(p); }
BOOL HmgInitializeLookAsideList( ULONG ulType, ULONG Size, ULONG dwTag, USHORT Number ) {
#if !defined(_GDIPLUS_)
ULONG Flags = 0;
// Modify tag using type.
dwTag += ulType << 24;
// Set the maximum allowed entry-size for this object
laSize[ulType] = Size;
// Allocate space for the List headers from Non-Paged pool (Otherwise bugcheck occurs
// if Kernel tries to access them for modification of list-depth if they are paged out)
pHmgLookAsideList[ulType] = (PPAGED_LOOKASIDE_LIST)GdiAllocPoolNonPagedNS( sizeof(PAGED_LOOKASIDE_LIST), dwTag);
if (pHmgLookAsideList[ulType] == (PPAGED_LOOKASIDE_LIST)NULL) { WARNING1("HmgCreate failed to allocate memory for pHmgLookAsideList\n"); return(FALSE); }
// Now Initialize the Lookaside list
#ifdef _HYDRA_
if (gIsTerminalServer) { Flags |= gSessionPoolMask; } #endif
ExInitializePagedLookasideList(pHmgLookAsideList[ulType], StubGdiAlloc, StubGdiFree, Flags, Size, dwTag, Number);
#endif
return (TRUE); }
/******************************Public*Routine******************************\
* IncProcessHandleCount - inc process handle count if under limit or * flag specifies no check. * * Arguments: * * Return Value: * * TRUE if count is incremented. * * History: * * 30-Apr-1996 -by- Mark Enstrom [marke] * \**************************************************************************/
BOOL HmgIncProcessHandleCount( W32PID w32pid, OBJTYPE objt ) { BOOL bRet = TRUE;
#if !defined(_GDIPLUS_)
//
// maintain handle count but don't limit quota
// for DCs
// --> changed it to limit quota for DCs. The fact that
// USER create DCs for menu's and borders on behalf of the
// app should not be that bad to prevent us from limiting DCs
// end user can always use 'close' or task manager to kill a bad app.
// If we don't limit DCs, the whole system will be affected.
// [lingyunw]
//
if ((w32pid != OBJECT_OWNER_PUBLIC) && (w32pid != OBJECT_OWNER_NONE)) { //
// is the PID the current PID
//
if (w32pid == W32GetCurrentPID()) { PW32PROCESS pw32Current = W32GetCurrentProcess();
//
// if the w32 process is not NULL then use
// the current value, otherwise the process
// has no W32PROCESS, so don't track handle quota
//
if (pw32Current) { //
// increment handle count unless call specifies check quota and
// process is already at or above limit.
//
if (pw32Current->GDIHandleCount >= gProcessHandleQuota) { WARNING1("GDI Handle Limit reached\n"); bRet = FALSE; } else { InterlockedIncrement(&pw32Current->GDIHandleCount); } } } else { PW32PROCESS pw32; PEPROCESS Process; NTSTATUS Status;
Status = PsLookupProcessByProcessId(LongToHandle(w32pid), &Process); if (NT_SUCCESS(Status)) { if (pw32 = (PW32PROCESS)(PsGetProcessWin32Process(Process))) { if (pw32->GDIHandleCount >= gProcessHandleQuota) { WARNING1("GDI Handle Limit reached\n"); bRet = FALSE; } else { InterlockedIncrement(&pw32->GDIHandleCount); } } ObDereferenceObject(Process); } else { WARNING1("HmgIncProcessGDIHandleCount: Couldn't find PID owner\n"); } } }
#endif
return(bRet); }
/******************************Public*Routine******************************\
* HmgDecProcessHandleCount - dec process handle count * * Arguments: * * none * * Return Value: * * nont * * History: * * 6-May-1996 -by- Mark Enstrom [marke] * \**************************************************************************/
VOID HmgDecProcessHandleCount( W32PID w32pid ) {
#if !defined(_GDIPLUS_)
if ((w32pid != OBJECT_OWNER_PUBLIC) && (w32pid != OBJECT_OWNER_NONE)) { if (w32pid == W32GetCurrentPID()) { PW32PROCESS pw32Current = W32GetCurrentProcess();
if (pw32Current) { //
// use current process
//
InterlockedDecrement(&pw32Current->GDIHandleCount);
if (pw32Current->GDIHandleCount < 0) { WARNING("GDI process handle count: decremented below zero"); } } } else { PW32PROCESS pw32; PEPROCESS Process; NTSTATUS Status;
Status = PsLookupProcessByProcessId(LongToHandle(w32pid), &Process); if (NT_SUCCESS(Status)) { if (pw32 = (PW32PROCESS)(PsGetProcessWin32Process(Process))) {
InterlockedDecrement(&pw32->GDIHandleCount); if (pw32->GDIHandleCount < 0) { WARNING("GDI process handle count: decremented below zero"); } } ObDereferenceObject(Process); } else { WARNING1("HmgDeccProcessGDIHandleCount: Couldn't find PID owner\n"); } } }
#endif
}
/******************************Public*Routine******************************\
* HmgValidHandle * * Returns TRUE if the handle is valid, FALSE if not. * * Note we don't need to lock the semaphore, we aren't changing anything, * we are just peeking in. * * History: * 08-Jul-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
BOOL HmgValidHandle( HOBJ hobj, OBJTYPE objt) { PENTRY pentTmp; UINT uiIndex = (UINT) (UINT) HmgIfromH(hobj);
if ((uiIndex < gcMaxHmgr) && ((pentTmp = &gpentHmgr[uiIndex])->Objt == objt) && (pentTmp->FullUnique == HmgUfromH(hobj))) { ASSERTGDI(pentTmp->einfo.pobj != (POBJ) NULL, "ERROR how can it be NULL"); return(TRUE); } else { return(FALSE); } }
/******************************Public*Routine******************************\
* HmgInsertObject * * This inserts an object into the handle table, returning the handle * associated with the pointer. * * History: * 13-Oct-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
HOBJ HmgInsertObject( PVOID pv, FLONG flags, // flags can be a combination of the following :
// HMGR_MAKE_PUBLIC - Allow object to be lockable by
// any process
// HMGR_ALLOC_LOCK - Do an HmgLock on the object and
// return a pointer instead of handle
// HMGR_ALLOC_ALT_LOCK - Do an HmgShareLock on the object
// and return a pointer instead of
// handle
OBJTYPE objt) { ASSERTGDI(pv != (PVOID) NULL, "Invalid address"); ASSERTGDI(objt != (OBJTYPE) DEF_TYPE, "objt is bad");
HOBJ h = 0;
BOOL bHandleQuota = TRUE; W32PID W32Pid = W32GetCurrentPID(); #if defined(_WIN64)
PW32THREAD pW32Thread = W32GetCurrentThread(); PRINTCLIENTID *pClientID = pW32Thread ? (PRINTCLIENTID*)pW32Thread->pClientID : 0; W32Pid = pClientID ? pClientID->clientPid : W32Pid; #endif
//
// need the HMGR lock held while getting the handle
//
{ MLOCKFAST mo;
if (!(flags & HMGR_MAKE_PUBLIC)) { //
// Inc handle count for non-public objects. DC objects
// can be allocated above process limit
//
bHandleQuota = HmgIncProcessHandleCount(W32Pid,objt); }
if (bHandleQuota) { h = hGetFreeHandle((OBJTYPE) objt);
if (h == 0) { WARNING1("HmgInsert failed hGetFreeHandle\n");
//
// decrement handle count in case of failure
//
if (!(flags & HMGR_MAKE_PUBLIC)) { HmgDecProcessHandleCount(W32Pid); } } else { // WINBUG #345863 3-17-2001 jasonha Hold ghsemHmgr thru out insert
((ENTRYOBJ *) &(gpentHmgr[HmgIfromH(h)]))->vSetup((POBJ) pv, objt, (FSHORT) flags); ((OBJECT *) pv)->hHmgr = (HANDLE) h; } } else { WARNING1("HmgInsertObject failed due to handle quota\n"); } }
return(h); }
/******************************Public*Routine******************************\
* HmgRemoveObject * * Removes an object from the handle table if certain conditions are met. * * History: * 13-Oct-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
PVOID HmgRemoveObject( HOBJ hobj, LONG cExclusiveLock, LONG cShareLock, BOOL bIgnoreUndeletable, OBJTYPE objt) { POBJ pobj; UINT uiIndex = (UINT) HmgIfromH(hobj);
if (uiIndex < gcMaxHmgr) { //
// Must acquire hmgr lock before handle lock
//
GreAcquireHmgrSemaphore();
//
// lock handle
//
PENTRY pentTmp = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentTmp,TRUE);
if (HandleLock.bValid()) { //
// verify objt and unique
//
if ((pentTmp->Objt == objt) && (pentTmp->FullUnique == HmgUfromH(hobj))) { pobj = pentTmp->einfo.pobj;
if ((pobj->cExclusiveLock == (USHORT)cExclusiveLock) && (pobj->ulShareCount == (ULONG)cShareLock)) { if (bIgnoreUndeletable || (!(pentTmp->Flags & HMGR_ENTRY_UNDELETABLE))) { //
// The undeletable flag is not set or else we are ignoring it.
//
#if GDI_PERF
HmgCurrentNumberOfHandles[objt]--; #endif
//
// set the handle in the object to NULL
// to prevent/catch accidental decrement of the
// shared reference count
//
pobj->hHmgr = NULL;
//
// free the handle
//
((ENTRYOBJ *) pentTmp)->vFree(uiIndex);
} else { WARNING1("HmgRemove failed object is undeletable\n"); pobj = NULL; } } else { //
// object is busy
//
// WARNING1("HmgRemove failed - object busy elsewhere\n");
pobj = NULL; } } else { WARNING1("HmgRemove: bad objt or unique\n"); pobj = NULL; }
HandleLock.vUnlock(); } else { WARNING1("HmgRemove: failed to lock handle\n"); pobj = NULL; }
//
// free hmgr lock
//
GreReleaseHmgrSemaphore(); } else { WARNING1("HmgRemove failed invalid index\n"); pobj = NULL; }
return((PVOID)pobj); }
/******************************Public*Routine******************************\
* HmgReplace * * Change the object pointer. Note this is callable only under very precise * circumstances: * * 1> When the object is exclusively locked. * * History: * Tue 14-Dec-1993 -by- Patrick Haluptzok [patrickh] * Seperate out lock counts from object structure. * * 18-Oct-1993 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
POBJ HmgReplace( HOBJ hobj, POBJ pobjNew, FLONG flags, LONG cLock, OBJTYPE objt) { //
// We assume everything is valid, that the handle being replaced is exclusively
// locked and valid.
//
// Note that currently "exclusively locked" can mean that we call this under
// OBJLOCK control which doesn't set the cExclusiveLock
//
POBJ pobj = NULL;
UINT uiIndex = (UINT) HmgIfromH(hobj);
ASSERTGDI(uiIndex != 0, "HmgReplace invalid handle 0"); ASSERTGDI(uiIndex < gcMaxHmgr, "HmgReplace invalid handle");
PENTRY pentTmp = &gpentHmgr[uiIndex];
ASSERTGDI(pentTmp->Objt == objt, "HmgReplace invalid object type"); ASSERTGDI(pentTmp->FullUnique == HmgUfromH(hobj), "HmgReplace invalid uniqueness"); ASSERTGDI((OBJECTOWNER_PID(pentTmp->ObjectOwner) == OBJECT_OWNER_PUBLIC) || (OBJECTOWNER_PID(pentTmp->ObjectOwner) == W32GetCurrentPID()), "HmgReplace invalid PID owner"); HANDLELOCK HandleLock(pentTmp,TRUE);
if (HandleLock.bValid()) { //
// Return the old value. We have to be under the mutex here since the
// counts live in the objects and if we do the switch while someone is
// looking at the old object and the old object gets deleted after this
// call before the original thread gets to run again he may fault or
// wrongly succeed to lock it down.
//
pobj = pentTmp->einfo.pobj; pentTmp->einfo.pobj = pobjNew;
HandleLock.vUnlock(); }
return((POBJ) pobj); }
/******************************Public*Routine******************************\
* AllocateObject * * Allocates an object through a look a side buffer if possible else just * allocates out of the heap. * * History: * 14-Apr-1997 -by- Dan Almosnino danalm * Modify to use DaveC's S-Lists mechanism for Lookaside Lists * 12-Oct-1993 -by- Patrick Haluptzok patrickh * Based on DaveC's HmgAlloc look-aside code \**************************************************************************/
//
// This struct and the following union can be thrown away
// when someone fixes the BASEOBJECT cExclusiveLock and BaseFlags sharing
// the same DWORD.
//
struct SplitLockAndFlags { USHORT c_cExclusiveLock; USHORT c_BaseFlags; };
union SplitOrCombinedLockAndFlags { SplitLockAndFlags S; ULONG W; };
PVOID AllocateObject( ULONG cBytes, ULONG ulType, BOOL bZero) {
PVOID pvReturn = NULL;
ASSERTGDI(ulType != DEF_TYPE, "AllocateObject ulType is bad"); ASSERTGDI(cBytes >= sizeof(BASEOBJECT), "AllocateObject cBytes is bad");
//
// Debug check to avoid assert in ExAllocatePool
//
#if DBG
if (cBytes >= (PAGE_SIZE * 10000)) { WARNING("AllocateObject: cBytes >= 10000 pages"); return(NULL); }
#endif
//
// If the object type has a lookaside list and the list contains a
// free entry and the requested size is less than or equal to the
// lookaside list size, then attempt to allocate memory from the
// lookaside list.
//
#if !defined(_GDIPLUS_)
if (laSize[ulType] >= (ULONG)cBytes) {
if((pvReturn = ExAllocateFromPagedLookasideList(pHmgLookAsideList[ulType])) != NULL) {
if (bZero) { RtlZeroMemory(pvReturn, (UINT) cBytes); } else { //
// At least the BASEOBJECT should be initialized.
//
RtlZeroMemory(pvReturn, (UINT) sizeof(BASEOBJECT)); }
//
// This code is really hideous.
// This is one of a very few places where it is necessary to know
// that the BaseFlags entry in BASEOBJECT is actually the upper
// 16 bits of the DWORD containing the cExclusiveLock.
// If the layout of BASEOBJECT ever changes separating the cExclusiveLock
// and the BaseFlags then this code _WILL_ break.
//
// The InterlockedExchange is required because it inserts appropriate
// memory barriers on Alpha preventing other processors from seeing
// the write delayed.
//
// Using the InterlockedExchange here is probably not necessary
// Before anyone else can access this data it has to be put in the
// Handle table and the code that does that should have the appropriate
// mb instruction. However, just to be safe...
//
SplitOrCombinedLockAndFlags M; M.S.c_cExclusiveLock = ((BASEOBJECT *)pvReturn)->cExclusiveLock; M.S.c_BaseFlags = ((BASEOBJECT *)pvReturn)->BaseFlags | HMGR_LOOKASIDE_ALLOC_FLAG; InterlockedExchange( (LONG *)& (((BASEOBJECT *)pvReturn)->cExclusiveLock), M.W );
//
// This is the code without the InterlockedExchange.
// If BaseFlags is removed from the DWORD containing the cExclusiveLock, the code
// can be restored to this (provided all code that puts the object
// in the handle table uses the HANDLELOCK):
//
//((BASEOBJECT *)pvReturn)->BaseFlags |= HMGR_LOOKASIDE_ALLOC_FLAG; //Set Base Object Lookaside Flag
//
#if GDI_PERF
HmgNumberOfLookAsideHits[ulType] += 1; HmgCurrentNumberOfLookAsideObjects[ulType] += 1;
if (HmgCurrentNumberOfLookAsideObjects[ulType] > HmgMaximumNumberOfLookAsideObjects[ulType]) { HmgMaximumNumberOfLookAsideObjects[ulType] = HmgCurrentNumberOfLookAsideObjects[ulType]; } #endif
}
}
#endif
if (pvReturn == NULL) { //
// The attempted allocation from Look-Aside failed
// (or the object is too big for the lookaside).
// Attempt to allocate the object from the heap.
//
ULONG ulTag = '00hG'; ulTag += ulType << 24;
// Inject the Session Id into the tag
// Sessions with Id's from 0 to 78 will use an ascii character
// from '0' to '~', respectively. Sessions with higher Id's will
// all have a ' '.
ULONG ulSessionId = PsGetCurrentProcessSessionId(); if (ulSessionId > '~' - '0') { ulSessionId = ' ' - '0'; } ulTag += ulSessionId << 16;
//
// Note: not necessary to clear the HMGR_LOOKASIDE_ALLOC_FLAG
// in BaseFlags since the BASEOBJECT is always zero-initialized.
//
if (bZero) { pvReturn = PALLOCMEM(cBytes, ulTag); } else { pvReturn = PALLOCNOZ(cBytes, ulTag);
//
// At least the BASEOBJECT should be initialized.
//
if (pvReturn) { RtlZeroMemory(pvReturn, (UINT) sizeof(BASEOBJECT)); } }
//
// If the allocation failed again, then set the extended
// error status and return an invalid handle.
//
if (!pvReturn) { KdPrint(("GDISRV:AllocateObject failed alloc of %lu bytes\n", (cBytes))); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); return NULL; }
}
#if GDI_PERF
//
// Increment the object performance counters.
//
HmgCurrentNumberOfObjects[ulType] += 1; HmgNumberOfObjectsAllocated[ulType] += 1;
if (HmgCurrentNumberOfObjects[ulType] > HmgMaximumNumberOfObjects[ulType]) { HmgMaximumNumberOfObjects[ulType] = HmgCurrentNumberOfObjects[ulType]; }
#endif
return(pvReturn); }
/******************************Public*Routine******************************\
* HmgAlloc * * Allocate an object from Handle Manager. * * WARNING: * -------- * * If the object is share-lockable via an API, you MUST use HmgInsertObject * instead. If the object is only exclusive-lockable via an API, you MUST * either use HmgInsertObject or specify HMGR_ALLOC_LOCK. * * (This is because if you use HmgAlloc, a malicious multi-threaded * application could guess the handle and cause it to be dereferenced * before you've finished initializing it, possibly causing an access * violation.) * * History: * 23-Sep-1991 -by- Patrick Haluptzok patrickh * Rewrite to be flat, no memory management under semaphore. * * 08-Dec-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
HOBJ HmgAlloc( ULONGSIZE_T cb, OBJTYPE objt, FSHORT fs) // fs can be a combination of the following:
// HMGR_NO_ZERO_INIT - Don't zero initialize
// HMGR_MAKE_PUBLIC - Allow object to be lockable by
// any process
// HMGR_ALLOC_LOCK - Do an HmgLock on the object and
// return a pointer instead of handle
// HMGR_ALLOC_ALT_LOCK - Do an HmgShareLock on the object
// and return a pointer instead of
// handle
{ HOBJ Handle; PVOID pv;
ASSERTGDI(objt != (OBJTYPE) DEF_TYPE, "HmgAlloc objt is bad"); ASSERTGDI(cb >= 8, "ERROR hmgr writes in first 8 bytes");
//
// Allocate a pointer.
//
pv = AllocateObject(cb, (ULONG) objt, ((fs & HMGR_NO_ZERO_INIT) == 0));
if (pv != (PVOID) NULL) { BOOL bHandleQuota = TRUE; W32PID W32Pid = W32GetCurrentPID(); #if defined(_WIN64)
PW32THREAD pW32Thread = W32GetCurrentThread(); PRINTCLIENTID *pClientID = pW32Thread ? (PRINTCLIENTID*)pW32Thread->pClientID : 0; W32Pid = pClientID ? pClientID->clientPid : W32Pid; #endif
//
// Allocate a handle. We need the mutex to access the free list
//
GreAcquireHmgrSemaphore();
if (!(fs & HMGR_MAKE_PUBLIC)) { //
// increment handle quota on non public objects. DC objects
// can be allocated over a process quota limit.
//
bHandleQuota = HmgIncProcessHandleCount(W32Pid, objt); }
if (bHandleQuota) { Handle = hGetFreeHandle(objt);
if (Handle != (HOBJ) 0) { //
// Store a pointer to the object in the entry corresponding to the
// allocated handle and initialize the handle data.
//
((ENTRYOBJ *) &(gpentHmgr[HmgIfromH(Handle)]))->vSetup((POBJ) pv, objt, fs);
//
// Store the object handle at the beginning of the object memory.
//
((OBJECT *)pv)->hHmgr = (HANDLE)Handle;
GreReleaseHmgrSemaphore();
return ((fs & (HMGR_ALLOC_ALT_LOCK | HMGR_ALLOC_LOCK)) ? (HOBJ)pv : Handle); } else { //
// decrement process handle count if not public
// (while lock is still held)
//
if (!(fs & HMGR_MAKE_PUBLIC)) { HmgDecProcessHandleCount(W32Pid); }
//
// We just failed a handle allocation. Release the memory.
//
GreReleaseHmgrSemaphore(); FreeObject(pv,(ULONG) objt);
} } else { WARNING("Failed HmgAlloc due to handle quota\n"); GreReleaseHmgrSemaphore(); FreeObject(pv,(ULONG) objt); } }
return((HOBJ) 0); }
/******************************Public*Routine******************************\
* FreeObject * * Frees the object from where it was allocated. * * History: * * 14-Apr-1997 -by- Dan Almosnino danalm * Modified to use DaveC's S-List mechanism for Lookaside lists * * 12-Oct-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
VOID FreeObject(PVOID pvFree, ULONG ulType) {
#if GDI_PERF
HmgCurrentNumberOfObjects[ulType] -= 1;
#endif
// Find if the object originates from a lookaside allocation,
// if so, return it to the free list.
if( ((BASEOBJECT *)pvFree)->BaseFlags & HMGR_LOOKASIDE_ALLOC_FLAG ) {
#if !defined(_GDIPLUS_)
ExFreeToPagedLookasideList(pHmgLookAsideList[ulType],pvFree);
#endif
#if GDI_PERF
HmgCurrentNumberOfLookAsideObjects[ulType] -= 1;
#endif
} else { // If the object memory was allocated from the general heap, then
// release the memory to the heap.
VFREEMEM(pvFree); }
}
/******************************Public*Routine******************************\
* HmgFree * * Free an object from the handle manager. * * History: * 23-Sep-1991 -by- Patrick Haluptzok patrickh * Rewrite to be flat, no memory management under semaphore. * * 08-Dec-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
VOID HmgFree(HOBJ hobj) { UINT uiIndex = (UINT) HmgIfromH(hobj); POBJ pobjTmp; OBJTYPE objtTmp;
ASSERTGDI(uiIndex != 0, "ERROR HmgFree invalid 0 handle");
if (uiIndex < gcMaxHmgr) { PENTRY pentTmp = &gpentHmgr[uiIndex];
//
// Acquire the handle manager lock and decrement the count of objects of
// the specfied type.
//
GreAcquireHmgrSemaphore();
//
// lock handle
//
HANDLELOCK HandleLock(pentTmp,FALSE);
if (HandleLock.bValid()) { #if GDI_PERF
HmgCurrentNumberOfHandles[pentTmp->Objt]--; #endif
pobjTmp = pentTmp->einfo.pobj; objtTmp = pentTmp->Objt;
//
// Free the object handle
//
((ENTRYOBJ *) pentTmp)->vFree(uiIndex);
HandleLock.vUnlock(); }
GreReleaseHmgrSemaphore();
if (pobjTmp) { FreeObject((PVOID)pobjTmp, (ULONG) objtTmp); } } else { WARNING1("HmgFree: bad handle index"); } }
/******************************Public*Routine******************************\
* HmgSetOwner - set new object owner * * Arguments: * hobj - handle of object * objt - type of object * usCurrent - OBJECT_OWNER flag for current owner * pw32Current - PW32PROCESS of current owner if not current w32process * usNew - OBJECT_OWNER flag for new owner * pw32New - PW32PROCESS of new owner if not current w32process * * Return Value: * * Status * * History: * * 15-May-1996 -by- Mark Enstrom [marke] * \**************************************************************************/
BOOL HmgSetOwner( HOBJ hobj, W32PID W32PidNew, OBJTYPE objt ) { PENTRY pentTmp; UINT uiIndex = (UINT) HmgIfromH(hobj); BOOL bStatus = FALSE; W32PID W32PidCurrent; #if defined(_WIN64)
KERNEL_PVOID pclientID; PW32THREAD pW32thread; BOOL bWOW64 = FALSE; #endif
if (W32PidNew == OBJECT_OWNER_CURRENT) { W32PidNew = W32GetCurrentPID(); }
if (uiIndex < gcMaxHmgr) { pentTmp = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentTmp,FALSE);
if (HandleLock.bValid()) { //
// verify objt and unique
//
if ((pentTmp->Objt == objt) && (pentTmp->FullUnique == HmgUfromH(hobj))) {
POBJ pobj = pentTmp->einfo.pobj; #if defined(_WIN64)
pW32thread = W32GetCurrentThread();
//
// Set the handle owner pid to the wow64 print client pid if
// the the PRINTCLIENTID pointer in W32TREAD is non zero.
// In this case, we are doing WOW64 printing via LPC.
//
pclientID = pW32thread ? pW32thread->pClientID : 0; if(pclientID) bWOW64 = TRUE; #endif
if ((pobj->cExclusiveLock == 0) || (pobj->Tid == (PW32THREAD)PsGetCurrentThread()) #if defined(_WIN64)
|| (bWOW64) #endif
) {
bStatus = TRUE;
//
// determine current W32PID
//
W32PidCurrent = HandleLock.Pid(); #if defined(_WIN64)
if (bWOW64) { W32PidNew = ((PRINTCLIENTID*)pclientID)->clientPid; } #endif
if (W32PidCurrent != W32PidNew) { bStatus = HmgIncProcessHandleCount(W32PidNew,objt);
if (bStatus) { HmgDecProcessHandleCount(W32PidCurrent);
//
// set new owner
//
HandleLock.Pid(W32PidNew); } else { WARNING1("HmgSetOwner: Failed to set owner due to handle quota\n"); } } } else { WARNING1("HmgSetOwner, Object is exclusively locked\n"); } } else { WARNING1("HmgSetOwner, object type or unique mismach\n"); }
HandleLock.vUnlock(); } } else { WARNING1("HmgSetOwner: bad index\n"); }
return(bStatus); }
/*****************************Exported*Routine*****************************\
* GreGetObjectOwner * * Get the owner of an object * * Arguments: * * hobj - handle to object * objt - handle type for verification * * History: * * 27-Apr-1994 -by- Johnc * Dupped from SetOwner. \**************************************************************************/
W32PID GreGetObjectOwner( HOBJ hobj, DWORD objt) { W32PID pid = OBJECT_OWNER_ERROR; UINT uiIndex = (UINT) HmgIfromH(hobj);
if (uiIndex < gcMaxHmgr) { PENTRY pentTmp = &gpentHmgr[uiIndex];
if ((pentTmp->Objt == objt) && (pentTmp->FullUnique == HmgUfromH(hobj))) { pid = OBJECTOWNER_PID(pentTmp->ObjectOwner); } else { WARNING1("GreGetObjectOwner failed - object type of unique mismatch\n"); }
} else { WARNING1("GreGetObjectOwner failed - invalid handle index\n"); }
return(pid); }
/******************************Public*Routine******************************\
* * HmgSwapHandleContents locks both handles and verifies lock counts. * The handle contents for each element are then swapped. * * Arguments: * * hobj1 - handle to object 1 * cShare1 - share count object 1 must have after handle is locked * hobj2 - handle to object 2 * cShare1 - share count object 2 must have after handle is locked * objt - type that both handles must be * * Return Value: * * BOOL status * * History: * * 24-Jul-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
BOOL HmgSwapHandleContents( HOBJ hobj1, ULONG cShare1, HOBJ hobj2, ULONG cShare2, OBJTYPE objt) { //
// acquire hmgr resource for this operation to
// make sure we don't deadlock with two threads
// trying to swap the same handles in reverse
// order
//
MLOCKFAST mo;
return( HmgSwapLockedHandleContents( hobj1, cShare1, hobj2, cShare2, objt) );
}
/******************************Public*Routine******************************\
* * HmgSwapLockedHandleContents locks both handles and verifies lock counts. * The handle contents for each element are then swapped. HmgrResource must * be owned to prevent deadlock. * * Arguments: * * hobj1 - handle to object 1 * cShare1 - share count object 1 must have after handle is locked * hobj2 - handle to object 2 * cShare1 - share count object 2 must have after handle is locked * objt - type that both handles must be * * Return Value: * * BOOL status * * History: * * 24-Jul-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
BOOL HmgSwapLockedHandleContents( HOBJ hobj1, ULONG cShare1, HOBJ hobj2, ULONG cShare2, OBJTYPE objt) {
UINT uiIndex1 = (UINT) HmgIfromH(hobj1); UINT uiIndex2 = (UINT) HmgIfromH(hobj2); BOOL bResult = FALSE;
ASSERTGDI(objt != (OBJTYPE) DEF_TYPE, "Bad type");
//
// Acquire handle lock on both objects
//
PENTRY pentry1 = &gpentHmgr[uiIndex1];
PENTRY pentry2 = &gpentHmgr[uiIndex2];
HANDLELOCK HandleLock1(pentry1,FALSE);
if (HandleLock1.bValid()) { HANDLELOCK HandleLock2(pentry2,FALSE); if (HandleLock2.bValid()) { //
// Verify share lock counts and object types
//
if ((HandleLock1.ShareCount() == (USHORT)cShare1) && (pentry1->Objt == objt)) { if ((HandleLock2.ShareCount() == (USHORT)cShare2) && (pentry2->Objt == objt)) { POBJ pobjTemp; PVOID pvTmp;
//
// swap pobj in handle table
//
pobjTemp = pentry1->einfo.pobj; pentry1->einfo.pobj = pentry2->einfo.pobj; pentry2->einfo.pobj = pobjTemp;
//
// swap puser in handle table
//
pvTmp = pentry1->pUser; pentry1->pUser = pentry2->pUser; pentry2->pUser = pvTmp;
//
// swap BASEOBJECTS
//
BASEOBJECT obj;
obj = *pentry1->einfo.pobj; *pentry1->einfo.pobj = *pentry2->einfo.pobj; *pentry2->einfo.pobj = obj;
//
// unswap the BASEOBJECT.BaseFlags
//
// The BaseFlags field contains information about how
// the object was allocated (and therefore, how it must
// be deallocated). Thus, it represents state associated
// with the actual memory, not the object, and should not
// be swapped.
//
// See comments regarding BASEOBJECT in inc\hmgshare.h
// for more details. Also, there is swapping code in
// RGNOBJ::bSwap (rgnobj.cxx).
//
USHORT usTemp;
usTemp = pentry1->einfo.pobj->BaseFlags; pentry1->einfo.pobj->BaseFlags = pentry2->einfo.pobj->BaseFlags; pentry2->einfo.pobj->BaseFlags = usTemp;
bResult = TRUE; } else { WARNING1("HmgSwapHandleContents: wrong share count or objt for hobj2"); } } else { WARNING1("HmgSwapHandleContents: wrong share count or objt for hobj1"); }
HandleLock2.vUnlock(); }
HandleLock1.vUnlock(); }
return(bResult); }
/*****************************Exported*Routine*****************************\
* HOBJ HmgNextOwned(hmgr, hobj, pid) * * Report the next object owned by specified process * * History: * 08-Dec-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
HOBJ FASTCALL HmgNextOwned( HOBJ hobj, W32PID pid) { MLOCKFAST mo;
return(HmgSafeNextOwned(hobj, pid)); }
/*****************************Exported*Routine*****************************\
* HOBJ HmgSafeNextOwned * * Report the next object owned by specified process * * History: * Sat 11-Dec-1993 -by- Patrick Haluptzok [patrickh] * Remove function wrapper. * * 08-Dec-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
HOBJ FASTCALL HmgSafeNextOwned( HOBJ hobj, W32PID pid) {
PENTRYOBJ pentTmp; UINT uiIndex = (UINT) HmgIfromH(hobj);
//
// If we are passed 0 we inc to 1 because 0 can never be valid.
// If we are passed != 0 we inc 1 to find the next one valid.
//
uiIndex++;
while (uiIndex < gcMaxHmgr) { pentTmp = (PENTRYOBJ) &gpentHmgr[uiIndex];
if (pentTmp->bOwnedBy(pid)) { return((HOBJ) MAKE_HMGR_HANDLE(uiIndex, pentTmp->FullUnique)); }
//
// Advance to next object
//
uiIndex++;
}
//
// No objects found
//
return((HOBJ) 0); }
/*****************************Exported*Routine*****************************\
* HOBJ HmgSafeNextObjt * * Report the next object of a certain type. * * History: * Tue 19-Apr-1994 -by- Patrick Haluptzok [patrickh] * Wrote it. \**************************************************************************/
POBJ FASTCALL HmgSafeNextObjt( HOBJ hobj, OBJTYPE objt) {
PENTRYOBJ pentTmp; UINT uiIndex = (UINT) HmgIfromH(hobj);
//
// If we are passed 0 we inc to 1 because 0 can never be valid.
// If we are passed != 0 we inc 1 to find the next one valid.
//
uiIndex++;
while (uiIndex < gcMaxHmgr) { pentTmp = (PENTRYOBJ) &gpentHmgr[uiIndex];
if (pentTmp->Objt == objt) { return(pentTmp->einfo.pobj); }
//
// Advance to next object
//
uiIndex++; }
//
// no objects found
//
return((POBJ) 0); }
/*******************************Routine************************************\
* HmgLockAllOwners * * Description: * * Acquire an exclusive lock on an object. Note that this routine is * different than HmgLock in that the PID owner doesn't need to match the * current PID. This is useful in GreRestoreDC where we need to get a lock * on the saved DC in order to to copy its information to the primary DC, but * don't want to fail if the process has already reached its handle quota * because this saved DC is about to be deleted anyway. See bug #223129 * for details. * * WARNING: Do not call this routine outside of the above scenario unless * there's a very good reason! * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgLockAllOwners( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT)HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex];
//
// Passing FALSE as the second argument to the HANDLELOCK constructor
// allows us to obtain a lock regardless of the PID owner of the object.
//
HANDLELOCK HandleLock(pentry,FALSE);
if (HandleLock.bValid()) { if ((pentry->Objt != objt) || (pentry->FullUnique != HmgUfromH(hobj))) { HmgPrintBadHandle(hobj, objt); } else { pobj = pentry->einfo.pobj;
if ((pobj->cExclusiveLock == 0) || (pobj->Tid == (PW32THREAD)PsGetCurrentThread())) { pobj->cExclusiveLock++; pobj->Tid = (PW32THREAD)PsGetCurrentThread(); } else { WARNING1("HmgLock: object already locked by another thread"); pobj = (POBJ)NULL; } } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
#if !defined(_X86_) || defined(_GDIPLUS_)
/*******************************Routine************************************\
* HmgLock * * Description: * * Acquire an exclusive lock on an object, PID owner must match current PID * or be a public. * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgLock( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT)HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,TRUE);
if (HandleLock.bValid()) { if ((pentry->Objt != objt) || (pentry->FullUnique != HmgUfromH(hobj))) { HmgPrintBadHandle(hobj, objt); } else { pobj = pentry->einfo.pobj;
if ((pobj->cExclusiveLock == 0) || (pobj->Tid == (PW32THREAD)PsGetCurrentThread())) { pobj->cExclusiveLock++; pobj->Tid = (PW32THREAD)PsGetCurrentThread(); } else { WARNING1("HmgLock: object already locked by another thread"); pobj = (POBJ)NULL; } } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
/*******************************Routine************************************\
* HmgShareCheckLock * * Description: * * Acquire a share lock on an object, PID owner must match current PID * or be a public. * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgShareCheckLock( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT) HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex]; HANDLELOCK HandleLock(pentry,TRUE);
if (HandleLock.bValid()) { if ((pentry->Objt == objt) && (pentry->FullUnique == HmgUfromH(hobj))) { pobj = pentry->einfo.pobj; pobj->ulShareCount++; } else { HmgPrintBadHandle(hobj, objt); } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
/*******************************Routine************************************\
* HmgShareLock * * Description: * * Acquire a share lock on an object, don't check PID owner * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgShareLock( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT) HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,FALSE); if (HandleLock.bValid()) { if ((pentry->Objt == objt) && (pentry->FullUnique == HmgUfromH(hobj))) { pobj = pentry->einfo.pobj; pobj->ulShareCount++; } else { HmgPrintBadHandle(hobj, objt); } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
#endif
/*******************************Routine************************************\
* HmgShareCheckLockIgnoreStockBit * * Description: * * Acquire a share lock on an object, PID owner must match current PID * or be a public. We ignore the stock bit. * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgShareCheckLockIgnoreStockBit( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT) HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex]; HANDLELOCK HandleLock(pentry,TRUE);
if (HandleLock.bValid()) { if ((pentry->Objt == objt) && ((pentry->FullUnique & ~FULLUNIQUE_STOCK_MASK) == (HmgUfromH(hobj) & ~FULLUNIQUE_STOCK_MASK))) { pobj = pentry->einfo.pobj; pobj->ulShareCount++; } else { HmgPrintBadHandle(hobj, objt); } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
/*******************************Routine************************************\
* HmgShareLockIgnoreStockBit * * Description: * * Acquire a share lock on an object, don't check PID owner. Ignore stock * bit. * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgShareLockIgnoreStockBit( HOBJ hobj, OBJTYPE objt ) { POBJ pobj = (POBJ)NULL; UINT uiIndex = (UINT) HmgIfromH(hobj); if (uiIndex < gcMaxHmgr) { PENTRY pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,FALSE); if (HandleLock.bValid()) { if ((pentry->Objt == objt) && ((pentry->FullUnique & ~FULLUNIQUE_STOCK_MASK) == (HmgUfromH(hobj) & ~FULLUNIQUE_STOCK_MASK))) { pobj = pentry->einfo.pobj; pobj->ulShareCount++; } else { HmgPrintBadHandle(hobj, objt); } HandleLock.vUnlock(); } else { HmgPrintBadHandle(hobj, objt); } } else { HmgPrintBadHandle(hobj, objt); } return(pobj); }
/******************************Public*Routine******************************\
* HmgShareUnlock * * Make this a macro once it is debugged * * Arguments: * * pobj - pointer to share-locked object * * Return Value: * * None * \**************************************************************************/
VOID HmgShareUnlock( POBJ pobj ) { PENTRY pentry;
//
// decrement shared reference count to object, Handle checks
// are not done.
//
UINT uiIndex = (UINT) HmgIfromH(pobj->hHmgr);
pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,FALSE);
if (HandleLock.bValid()) { //
// decrement use count
//
pobj->ulShareCount--;
HandleLock.vUnlock(); } }
/*******************************Routine************************************\
* HmgReferenceCheckLock * * Description: * * The routine validates the hanlde passed in and returns a pointer to * the object. This routine must only be called when the HmgrReource is * held and the pointer to the object is only valid while the Resource * is held. No unlock is neccessary since the object is not reference- * counted. * * Arguments: * * hobj - Handle to lock * objt - Check to make sure handle is of expected type * * Return Value: * * Pointer to object or NULL * \**************************************************************************/
POBJ FASTCALL HmgReferenceCheckLock( HOBJ hobj, OBJTYPE objt, BOOL bDebugPrint ) { PENTRY pentry; POBJ pobj = (POBJ)NULL;
UINT uiIndex = (UINT) HmgIfromH(hobj);
if (uiIndex < gcMaxHmgr) { pentry = &gpentHmgr[uiIndex];
if ((pentry->Objt == objt) && (pentry->FullUnique == HmgUfromH(hobj))) { pobj = pentry->einfo.pobj; } else { if (bDebugPrint) { HmgPrintBadHandle(hobj, objt); } } } else { if (bDebugPrint) { HmgPrintBadHandle(hobj, objt); } } return(pobj); }
#if !defined(_X86_) || defined(_GDIPLUS_)
/******************************Public*Routine******************************\
* HmgIncrementShareReferenceCount * * interlocked increment shared reference count * * Arguments: * * pObj - pointer to valid object * * Return Value: * * none * * History: * * 6-Jun-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
VOID FASTCALL HmgIncrementShareReferenceCount( POBJ pObj ) { HANDLELOCK HandleLock(PENTRY_FROM_POBJ(pObj), FALSE);
if (HandleLock.bValid()) { pObj->ulShareCount++; HandleLock.vUnlock(); } else { HmgPrintBadHandle((HOBJ)pObj->hHmgr, PENTRY_FROM_POBJ(pObj)->Objt); } }
/******************************Public*Routine******************************\
* HmgDecrementShareReferenceCount * * interlocked increment shared reference count * * Arguments: * * pObj - pointer to valid object * * Return Value: * * previous lock count * * History: * * 6-Jun-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
ULONG FASTCALL HmgDecrementShareReferenceCount( POBJ pObj ) { ULONG ulOrigShareCount=0; HANDLELOCK HandleLock(PENTRY_FROM_POBJ(pObj), FALSE);
if (HandleLock.bValid()) { ulOrigShareCount = pObj->ulShareCount; pObj->ulShareCount--; HandleLock.vUnlock(); } else { HmgPrintBadHandle((HOBJ)pObj->hHmgr, PENTRY_FROM_POBJ(pObj)->Objt); }
return ulOrigShareCount; }
#endif
/******************************Public*Routine******************************\
* HmgMarkDeletable * * Mark an object as deletable. * * History: * 11-Jun-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
BOOL FASTCALL HmgMarkDeletable( HOBJ hobj, OBJTYPE objt) { PENTRY pentry; UINT uiIndex = (UINT) HmgIfromH(hobj); BOOL bStatus = FALSE;
if (uiIndex < gcMaxHmgr) { pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,FALSE); if (HandleLock.bValid()) {
if ((pentry->Objt == objt) && (pentry->FullUnique == HmgUfromH(hobj))) { pentry->Flags &= ~HMGR_ENTRY_UNDELETABLE; bStatus = TRUE; } else { WARNING1("HmgMarkDeletable: bad objt or unique"); } HandleLock.vUnlock(); } else { WARNING1("HmgMarkDeletable failed: not object owner\n"); } } else { WARNING1("HmgMarkDeletable failed: index out of range\n"); }
return(bStatus); }
/******************************Public*Routine******************************\
* HmgMarkUndeletable * * Mark an object as undeletable. * * History: * 11-Jun-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
BOOL FASTCALL HmgMarkUndeletable(HOBJ hobj, OBJTYPE objt) { PENTRY pentry; UINT uiIndex = (UINT) HmgIfromH(hobj); BOOL bStatus = FALSE;
if (uiIndex < gcMaxHmgr) { pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,TRUE); if (HandleLock.bValid()) { if ((pentry->Objt == objt) && (pentry->FullUnique == HmgUfromH(hobj))) { pentry->Flags |= HMGR_ENTRY_UNDELETABLE; bStatus = TRUE; } else { WARNING1("HmgMarkUndeletable: bad objt or unique"); } HandleLock.vUnlock(); } else { WARNING1("HmgMarkUndeletable failed: not object owner\n"); } } else { WARNING1("HmgMarkUndeletable failed: index out of range\n"); }
return(bStatus); }
/******************************Public*Routine******************************\
* BOOL HmgSetLock * * Set the lock count of the object. * This is currently used by process cleanup code to reset object lock count. * * History: * Wed May 25 15:24:33 1994 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/
BOOL FASTCALL HmgSetLock(HOBJ hobj, ULONG cLock) { PENTRY pentTmp; UINT uiIndex = (UINT) HmgIfromH(hobj);
if (uiIndex < gcMaxHmgr) { pentTmp = &gpentHmgr[uiIndex]; if (pentTmp->FullUnique == HmgUfromH(hobj)) { POBJ pobj = pentTmp->einfo.pobj;
//
// This code is really hideous.
// This is one of a very few places where it is necessary to know
// that the BaseFlags entry in BASEOBJECT is actually the upper
// 16 bits of the DWORD containing the cExclusiveLock.
// If the layout of BASEOBJECT ever changes separating the cExclusiveLock
// and the BaseFlags then this code _WILL_ break.
//
// Also this code only works because nobody actually sets BaseFlags after
// object allocation. If this fact ever changes then this code needs to
// be changed to an InterlockedCompareExchange loop and should probably be
// protected by the HandleLock. Also the BaseFlags modification code
// would then also require an InterlockedCompareExchange too.
//
// The only reason we actually entertain this mess is because this code
// is only ever used in cleanup. It's bad enough that the code stomps the
// lock without checking it anyway.
//
// The InterlockedExchange is required because it inserts appropriate
// memory barriers on Alpha preventing other processors from seeing
// the write delayed.
//
SplitOrCombinedLockAndFlags M;
//
// get a pointer to the cExclusiveLock, make it a ULONG * and then
// dereference it getting 32bits from the beginning of the 16bit
// cExclusiveLock. This will include the BaseFlags which we know won't
// change (see above and BASEOBJECT).
//
M.W = *(ULONG *)&(pobj->cExclusiveLock); M.S.c_cExclusiveLock = (USHORT)cLock; InterlockedExchange((LONG *)&(pobj->cExclusiveLock), M.W);
} else { pentTmp = (PENTRY) NULL; WARNING1("HmgSetLock failed - Uniqueness does not match\n"); } } else { pentTmp = (PENTRY) NULL; WARNING1("HmgSetLock failed - invalid handle index or object type\n"); }
return((BOOL) (ULONG_PTR)pentTmp); }
/******************************Public*Routine******************************\
* HmgQueryLock * * This returns the number of times an object has been Locked. * * Expects: A valid handle. The handle should be validated and locked * before calling this. * * Returns: The number of times the object has been locked. * * History: * 28-Jun-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
ULONG FASTCALL HmgQueryLock(HOBJ hobj) { UINT uiIndex = (UINT) HmgIfromH(hobj);
// Note we don't need to grab the semaphore because this call assumes the
// handle has already been locked down and we are just reading memory.
ASSERTGDI(uiIndex < gcMaxHmgr, "HmgQueryLock invalid handle");
return(gpentHmgr[uiIndex].einfo.pobj->cExclusiveLock); }
/******************************Public*Routine******************************\
* HmgQueryAltLock * * This returns the number of times an object has been Alt-Locked. * * Expects: A valid handle. The handle should be validated and locked * before calling this. * * Returns: The number of times the object has been locked. * * History: * 28-Jun-1991 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
ULONG FASTCALL HmgQueryAltLock(HOBJ hobj) { UINT uiIndex = (UINT) HmgIfromH(hobj);
// Note we don't need to grab the semaphore because this call assumes the
// handle has already been locked down and we are just reading memory.
ASSERTGDI(uiIndex < gcMaxHmgr, "HmgQueryAltLock invalid handle");
return((ULONG) gpentHmgr[uiIndex].einfo.pobj->ulShareCount); }
/******************************Public*Routine******************************\
* HOBJ hGetFreeHandle() * * Get the next available handle. * * History: * Mon 21-Oct-1991 -by- Patrick Haluptzok [patrickh] * Make pent commit memory as needed, add logging of error codes. * * Sun 20-Oct-1991 -by- Patrick Haluptzok [patrickh] * add uniqueness to the handle when getting it. * * 12-Dec-1989 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
HOBJ hGetFreeHandle( OBJTYPE objt ) {
#if GDI_PERF
HmgCurrentNumberOfHandles[objt]++; HmgNumberOfHandlesAllocated[objt]++;
if (HmgCurrentNumberOfHandles[objt] > HmgMaximumNumberOfHandles[objt]) HmgMaximumNumberOfHandles[objt]++; #endif
LONG_PTR uiIndex;
if (ghFreeHmgr != (HOBJ) 0) { PENTRYOBJ pentTmp;
uiIndex = HmgIfromH(ghFreeHmgr); pentTmp = (PENTRYOBJ) &gpentHmgr[uiIndex]; ghFreeHmgr = pentTmp->einfo.hFree;
pentTmp->FullUnique = USUNIQUE(pentTmp->FullUnique,objt);
uiIndex = (LONG_PTR)MAKE_HMGR_HANDLE(uiIndex,pentTmp->FullUnique); return((HOBJ) uiIndex); }
if (gcMaxHmgr >= MAX_HANDLE_COUNT) { WARNING("Hmgr hGetFreeHandle failed to grow\n"); return ((HOBJ) 0); }
//
// Allocate a new handle table entry and set the uniqueness value.
//
// N.B. All newly committed memory is zeroed.
//
uiIndex = USUNIQUE(UNIQUE_INCREMENT,objt); gpentHmgr[gcMaxHmgr].FullUnique = (USHORT) uiIndex; uiIndex = (LONG_PTR)MAKE_HMGR_HANDLE(gcMaxHmgr,uiIndex); gcMaxHmgr++; return((HOBJ) uiIndex); }
/******************************Public*Routine******************************\
* HmgModifyHandleUniqueness() * * The handle passed in has already been updated to the new handle. * * This routine adds bits to the uniquess, updating both the handle entry * and and the handle in the object it self. This routine assumes that it is * safe to be playing with the handle. This means that the handle is locked * or it is during initialization and setting the stock bit. * * * History: * 18-Nov-1994 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
HOBJ HmgModifyHandleType( HOBJ h ) { PENTRY pent = &gpentHmgr[HmgIfromH(h)]; USHORT usUnique = HmgUfromH(h);
ASSERTGDI((((ULONG_PTR)pent->einfo.pobj->hHmgr ^ (ULONG_PTR)h) & (UNIQUE_MASK | TYPE_MASK)) == 0, "HmgModifyHandleType bad handle\n");
ASSERTGDI((pent->einfo.pobj->cExclusiveLock > 0) || (pent->einfo.pobj->ulShareCount > 0) || HmgStockObj(h), "HmgModifyHandleType not safe\n");
pent->FullUnique = usUnique; pent->einfo.pobj->hHmgr = h; return(h); }
/******************************Public*Routine******************************\
* HmgLockAndModifyHandleType() * * The handle passed in has already been updated to the new handle. * * The Handle is locked assuming owned by OBJECT_OWNER_CURRENT * or ObJECT_OWNER_PUBLIC. * * Then this routine adds bits to the uniquess, updating both the handle entry * and and the handle in the object it self. * * History: * 31-Oct-2000 -by- Pravin Santiago [pravins] * Wrote it. \**************************************************************************/
BOOL HmgLockAndModifyHandleType( HOBJ h ) { PENTRY pent = &gpentHmgr[HmgIfromH(h)];
HANDLELOCK HandleLock(pent,TRUE);
if (HandleLock.bValid()) { HmgModifyHandleType(h);
HandleLock.vUnlock();
return(TRUE); } return(FALSE); } /******************************Public*Routine******************************\
* HmgIncUniqueness() * * Modify the handle uniqueness * * This is implemented for the brush/pen caching and lazy deletion to make sure: * * a)a handle will not be cached twice (changing the handle uniqueness gurantees this) * * b)a handle will not be cached if it is a lazy deletion (cShareLock == 0) * * History: * 8-May-1995 -by- Lingyun Wang [lingyunw] * \**************************************************************************/
HOBJ HmgIncUniqueness( HOBJ hobj, OBJTYPE objt ) { PENTRY pentry; USHORT usUnique = HmgUfromH(hobj); POBJ pobj; UINT uiIndex= (UINT) HmgIfromH(hobj); HOBJ hNew = NULL;
if (uiIndex < gcMaxHmgr) { pentry = &gpentHmgr[uiIndex];
HANDLELOCK HandleLock(pentry,FALSE);
if (HandleLock.bValid()) { //
// validate objt and uniqueness, increment unique if correct
//
if ( (pentry->FullUnique == usUnique) && (OBJECTOWNER_PID(pentry->ObjectOwner) == W32GetCurrentPID()) && ((pentry = &gpentHmgr[uiIndex])->Objt == objt) ) { pentry->FullUnique += UNIQUE_INCREMENT;
hNew = (HOBJ)MAKE_HMGR_HANDLE(uiIndex, pentry->FullUnique);
pentry->einfo.pobj->hHmgr = hNew; } HandleLock.vUnlock(); } }
return(hNew); }
/******************************Public*Routine******************************\
* int NtGdiGetStats(HANDLE,int,int,PVOID,UINT) * * This function returns information from the handle manager about * the gdi objects that are existing. * * Parameters: * hProcess - handle to the process to query for information about * iIndex - type of information to return * iPidType - whether to query for just the process pointed * to by hProcess, or ALL gdi objects or PUBLIC gdi objects * pResults - Pointer to the buffer to fill the data into * cjResultSize - Size of the buffer to fill * * The user buffer is expected to be zero'd. Memory trashing * may occur if the size parameter is incorrect. * * returns: Success state * * History: * Wed 7-Jun-1995 -by- Andrew Skowronski [t-andsko] * Wrote it. \**************************************************************************/
#define OBJECT_OWNER_IGNORE (0x0001)
NTSTATUS APIENTRY NtGdiGetStats( HANDLE hProcess, int iIndex, int iPidType, PVOID pResults, UINT cjResultSize ) { DWORD * pdwRes = (DWORD *) pResults; //Pointer to the result buffer
W32PID pid; PENTRY pHmgEntry; //Pointer to current entry in the handle manager table
ULONG ulLoop; NTSTATUS iRet = STATUS_SUCCESS;
//
// Check permissions flag
//
#if !defined(_GDIPLUS_)
if (!( RtlGetNtGlobalFlags() & FLG_POOL_ENABLE_TAGGING)) { iRet = STATUS_ACCESS_DENIED; }
#endif
//
// Validate buffer size
//
switch(iIndex) { case (GS_NUM_OBJS_ALL) : if (cjResultSize < ((MAX_TYPE+1)*sizeof(ULONG))) iRet = STATUS_BUFFER_TOO_SMALL; break;
#if DBG
case (GS_HANDOBJ_CURRENT) : case (GS_HANDOBJ_MAX) : case (GS_HANDOBJ_ALLOC) : case (GS_LOOKASIDE_INFO) : if (cjResultSize < ((MAX_TYPE+1)*sizeof(ULONG)*2)) iRet = STATUS_BUFFER_TOO_SMALL; break; #else
// This info is not available in non-checked builds
case (GS_HANDOBJ_CURRENT) : case (GS_HANDOBJ_MAX) : case (GS_HANDOBJ_ALLOC) : case (GS_LOOKASIDE_INFO) : break; #endif
default : iRet = STATUS_NOT_IMPLEMENTED; break; }
if (NT_SUCCESS(iRet)) { __try { ProbeForWrite(pResults, cjResultSize, sizeof(UCHAR));
switch(iIndex) { case (GS_NUM_OBJS_ALL) :
//
// Determine the search key.
//
if (iPidType == (int) OBJECT_OWNER_CURRENT) { pid = (W32PID) ((ULONG) ((ULONG_PTR)hProcess & PID_BITS)); } else { //
// This takes care of OBJECT_OWNER_PUBLIC
// and OBJECT_OWNER_IGNORE
//
pid = (W32PID) iPidType; }
//
// Scan through the handle manager table and
// pick out the relevant objects
//
for (ulLoop = 0; ulLoop < gcMaxHmgr; ulLoop++) { pHmgEntry = &(gpentHmgr[ulLoop]);
if ((pid == OBJECT_OWNER_IGNORE) || (pid == OBJECTOWNER_PID(pHmgEntry->ObjectOwner))) { //
// Now, depending on the iIndex value we
// analyse the results
//
switch (iIndex) { case (GS_NUM_OBJS_ALL) : pdwRes[pHmgEntry->Objt]++; break; default : iRet = STATUS_NOT_IMPLEMENTED; break; } } } break;
#if DBG
case (GS_HANDOBJ_CURRENT) : RtlCopyMemory(pdwRes, HmgCurrentNumberOfHandles, (MAX_TYPE+1)*sizeof(ULONG));
RtlCopyMemory(&(pdwRes[MAX_TYPE + 1]), HmgCurrentNumberOfObjects, (MAX_TYPE+1)*sizeof(ULONG)); break;
case (GS_HANDOBJ_MAX) : RtlCopyMemory(pdwRes, HmgMaximumNumberOfHandles, (MAX_TYPE+1)*sizeof(ULONG));
RtlCopyMemory(&(pdwRes[MAX_TYPE + 1]), HmgMaximumNumberOfObjects, (MAX_TYPE+1)*sizeof(ULONG)); break;
case (GS_HANDOBJ_ALLOC) : RtlCopyMemory(pdwRes, HmgNumberOfHandlesAllocated, (MAX_TYPE+1)*sizeof(ULONG));
RtlCopyMemory(&(pdwRes[MAX_TYPE + 1]), HmgNumberOfObjectsAllocated, (MAX_TYPE+1)*sizeof(ULONG)); break;
case (GS_LOOKASIDE_INFO) : RtlCopyMemory(pdwRes, HmgNumberOfLookAsideHits, (MAX_TYPE+1)*sizeof(ULONG));
RtlCopyMemory(&(pdwRes[MAX_TYPE + 1]), HmgNumberOfObjectsAllocated, (MAX_TYPE+1)*sizeof(ULONG)); break;
#else
// This info is not available in non-checked builds
case (GS_HANDOBJ_CURRENT) : case (GS_HANDOBJ_MAX) : case (GS_HANDOBJ_ALLOC) : case (GS_LOOKASIDE_INFO) : break; #endif
default : iRet = STATUS_NOT_IMPLEMENTED; break; } } __except (EXCEPTION_EXECUTE_HANDLER) { iRet = STATUS_ACCESS_VIOLATION; } }
return iRet; }
/******************************Public*Routine******************************\
* HmgAllocateSecureUserMemory * * Allocate and lock 1 page, add as free DC_ATTRs * * Arguments: * * None * * Return Value: * * Status * * History: * * 15-May-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
PVOID HmgAllocateSecureUserMemory(HANDLE *phSecure) { PVOID pvRet = NULL; NTSTATUS NtStatus;
//
// allocate 1 page of user mode memory
//
SIZE_T ViewSize = PAGE_SIZE;
NtStatus = ZwAllocateVirtualMemory( NtCurrentProcess(), &pvRet, 0L, &ViewSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
if (NT_SUCCESS(NtStatus)) { //
// secure virtual memory
//
HANDLE hSecure = *phSecure = MmSecureVirtualMemory(pvRet, ViewSize, PAGE_READWRITE);
if (hSecure != NULL) { RtlZeroMemory((PVOID)pvRet,ViewSize); } else { //
// free memory
//
ZwFreeVirtualMemory( NtCurrentProcess(), (PVOID*)&pvRet, &ViewSize, MEM_RELEASE);
pvRet = NULL; } }
return(pvRet); }
/******************************Public*Routine******************************\
* HmgFreeSecureUserMemory * * unlock 1 page, and free it * * Arguments: * * None * * Return Value: * * Status * * History: * * 30-Oct-2001 -by- Pravin Santiago[pravins] * \**************************************************************************/
VOID HmgFreeAllocateSecureUserMemory(PVOID pvAttr, HANDLE hSecure) { SIZE_T ViewSize = PAGE_SIZE;
MmUnsecureVirtualMemory(hSecure); ZwFreeVirtualMemory(NtCurrentProcess(), &pvAttr, &ViewSize, MEM_RELEASE);
return; } /******************************Public*Routine******************************\
* HmgAllocateDcAttr * * Look first for free DC_ATTR block on thread, else look on process * free list. If none is available, try to allocate user-mode memory. * * Arguments: * * None * * Return Value: * * pDC_ATTR or NULL * * History: * * 24-Aug-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
PDC_ATTR HmgAllocateDcAttr() { PW32THREAD pThread = W32GetCurrentThread(); PDC_ATTR pDCAttr = NULL;
#if defined(_GDIPLUS_)
pDCAttr = (DC_ATTR*) PALLOCMEM(sizeof(DC_ATTR), 'zzzG');
#else
//
// look on thread for free dcattr
//
if (pThread->pgdiDcattr) { pDCAttr = (PDC_ATTR)pThread->pgdiDcattr; pThread->pgdiDcattr = NULL; } else { PW32PROCESS Process = W32GetCurrentProcess();
GreAcquireHmgrSemaphore();
//
// look on process list for free dcattr, if none are
// available then allocate 1 page of memory and add
// to list.
//
PDC_ATTR *pdcaFreeList = 0; PDCAFLISTENTRY pFreeList = 0;
// If the free list empty try to allocate one:
if (Process->pDCAttrList == NULL) { ASSERTGDI(IsListEmpty(&Process->GDIDcAttrFreeList), "HmgAllocateDcAttr pDCAttrList is NULL but GDIDcAttrFreeList is not empty\n"); HANDLE hSecure;
PDC_ATTR pdca = (PDC_ATTR)HmgAllocateSecureUserMemory(&hSecure);
if (pdca != (PDC_ATTR)NULL) { ULONG ulIndex;
pFreeList = (PDCAFLISTENTRY)PALLOCNOZ(sizeof(DCAFLISTENTRY), 'fcdG');
if (pFreeList == 0) { HmgFreeAllocateSecureUserMemory(pdca, hSecure); GreReleaseHmgrSemaphore(); return 0; }
InsertHeadList(&Process->GDIDcAttrFreeList, (PLIST_ENTRY)pFreeList);
pFreeList->cFree = ULNUMDC;
pdcaFreeList = (PDC_ATTR *)(&pFreeList->a[0]);
//
// set the new free list pointing to the new dcattrs allocated
// in user mode memory.
//
Process->pDCAttrList = (PVOID)&pdca[ULNUMDC-1];
for (ulIndex=0;ulIndex<ULNUMDC;ulIndex++) { pdcaFreeList[ulIndex] = &pdca[ulIndex]; } } }
if (Process->pDCAttrList != NULL) { ASSERTGDI(!IsListEmpty(&Process->GDIDcAttrFreeList), "HmgAllocateDcAttr pDCAttrList != NULL but GDIDcAttrFreeList is empty\n");
pFreeList = (PDCAFLISTENTRY)Process->GDIDcAttrFreeList.Flink;
pdcaFreeList = (PDC_ATTR *)(&pFreeList->a[0]);
ASSERTGDI(Process->pDCAttrList == (PVOID)(pdcaFreeList[pFreeList->cFree-1]), "HmgAllocateDcAttr pdcAttrList and pdcaFreeList entries dont match\n"); //
// get dcattr and remove from list
//
pDCAttr = (PDC_ATTR)Process->pDCAttrList;
pFreeList->cFree--;
// If pFreeList has no more free entries remove it from the process
// free list. Else adjust the free list pointer to next available
// attr.
if (pFreeList->cFree == 0) { RemoveEntryList((PLIST_ENTRY)pFreeList);
VFREEMEM(pFreeList);
// If the process free list is not empty, update free list
// pointer to the next available entry. Else set it to NULL.
if (!IsListEmpty(&Process->GDIDcAttrFreeList)) { pFreeList = (PDCAFLISTENTRY)Process->GDIDcAttrFreeList.Flink; ASSERTGDI(pFreeList->cFree == ULNUMDC, "HmgAllocateDcAttr: next available free list does not have cFree of ULNUMDC\n");
pdcaFreeList = (PDC_ATTR *)(&pFreeList->a[0]);
Process->pDCAttrList = (PVOID) (pdcaFreeList[pFreeList->cFree-1]); } else { Process->pDCAttrList = 0; } } else { Process->pDCAttrList = (PVOID) (pdcaFreeList[pFreeList->cFree-1]); } }
GreReleaseHmgrSemaphore(); }
#endif
return(pDCAttr); }
/******************************Public*Routine******************************\
* HmgAllocateBrushAttr * * Look on thread for free brushattr, else look on process free * list. If none is available, allocate a dcattr and divide it * up. * * Arguments: * * none * * Return Value: * * pbrushattr or NULL * * History: * * 28-Aug-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
POBJECTATTR HmgAllocateObjectAttr() { PW32THREAD pThread = W32GetCurrentThread(); POBJECTATTR pobjattr = NULL;
#if defined(_GDIPLUS_)
pobjattr = (OBJECTATTR*) PALLOCMEM(sizeof(OBJECTATTR), 'zzzG');
#else
//
// quick check on thread for free OBJECTATTR
//
if (pThread->pgdiBrushAttr) { pobjattr = (POBJECTATTR)pThread->pgdiBrushAttr; pThread->pgdiBrushAttr = NULL; } else { PW32PROCESS Process = W32GetCurrentProcess();
GreAcquireHmgrSemaphore();
//
// look on process list for free dcattr, if none are
// available then allocate 1 page of memory and add
// to list.
//
POBJECTATTR *pobaFreeList = 0; POBAFLISTENTRY pFreeList = 0;
if (Process->pBrushAttrList == NULL) { ASSERTGDI(IsListEmpty(&Process->GDIBrushAttrFreeList), "HmgAllocateObjectAttr pBrushAttrList is NULL but GDIBrushAttrFreeList is not empty\n"); HANDLE hSecure;
POBJECTATTR psbr = (POBJECTATTR)HmgAllocateSecureUserMemory(&hSecure);
if (psbr != (POBJECTATTR)NULL) { ULONG ulIndex;
// No free list available. Allocate one which can handle
// ulNumBrush entries.
pFreeList = (POBAFLISTENTRY)PALLOCNOZ(sizeof(OBAFLISTENTRY), 'fabG');
if (pFreeList == 0) { HmgFreeAllocateSecureUserMemory(psbr, hSecure); GreReleaseHmgrSemaphore(); return 0; }
InsertHeadList(&Process->GDIBrushAttrFreeList, (PLIST_ENTRY)pFreeList);
pFreeList->cFree = ULNUMBRUSH;
pobaFreeList = (POBJECTATTR *)(&pFreeList->a[0]);
//
// init process list, then create a linked list of all
// the new dcattrs on the process list
//
Process->pBrushAttrList = (PVOID)&psbr[ULNUMBRUSH-1];
for (ulIndex=0;ulIndex<ULNUMBRUSH;ulIndex++) { pobaFreeList[ulIndex] = &psbr[ulIndex]; }
} }
if (Process->pBrushAttrList != NULL) { ASSERTGDI(!IsListEmpty(&Process->GDIBrushAttrFreeList), "HmgAllocateObjectAttr pBrushAttrList != NULL but GDIBrushAttrFreeList is empty\n");
pFreeList = (POBAFLISTENTRY)Process->GDIBrushAttrFreeList.Flink;
pobaFreeList = (POBJECTATTR*)(&pFreeList->a[0]);
ASSERTGDI(Process->pBrushAttrList == (PVOID)(pobaFreeList[pFreeList->cFree-1]), "HmgAllocateObjectAttr pBrushAttrList and pobjFreeList entries dont match\n");
//
// get dcattr and remove from list
//
pobjattr = (POBJECTATTR)Process->pBrushAttrList;
pFreeList->cFree--;
// If pFreeList has no more free entries remove it from the process
// free list. Else adjust the free list pointer to next available
// attr.
if (pFreeList->cFree == 0) { RemoveEntryList((PLIST_ENTRY)pFreeList);
VFREEMEM(pFreeList);
// If the process free list is not empty, update free list
// pointer to the next available entry. Else set it to NULL.
if (!IsListEmpty(&Process->GDIBrushAttrFreeList)) { pFreeList = (POBAFLISTENTRY)Process->GDIBrushAttrFreeList.Flink; ASSERTGDI(pFreeList->cFree == ULNUMBRUSH, "HmgAllocateObjextAttr: next available free list does not have cFree of ULNUMBRUSH\n");
pobaFreeList = (POBJECTATTR*)(&pFreeList->a[0]);
Process->pBrushAttrList = (PVOID) (pobaFreeList[pFreeList->cFree-1]); } else { Process->pBrushAttrList = 0; } } else { Process->pBrushAttrList = (PVOID) (pobaFreeList[pFreeList->cFree-1]); } }
GreReleaseHmgrSemaphore(); }
#endif
return(pobjattr); }
/******************************Public*Routine******************************\
* HmgFreeDcAttr * * Free the dcattr, try to put it on w32thread, if no space then push onto * w32process * * Arguments: * * pdcattr * * Return Value: * * none * * History: * * 28-Aug-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
VOID HmgFreeDcAttr( PDC_ATTR pdca ) { PW32THREAD pThread = W32GetCurrentThread();
#if defined(_GDIPLUS_)
VFREEMEM(pdca);
#else
if ((pdca != NULL) && (pThread != NULL)) {
//
// try to place on thread
//
if (pThread->pgdiDcattr == NULL) { pThread->pgdiDcattr = pdca; } else { PW32PROCESS Process = W32GetCurrentProcess();
//
// Need to check if W32 process pointer is valid. There are
// circumstances in which the process can die without setting
// the W32 process.
//
if (Process != NULL) { GreAcquireHmgrSemaphore();
//
// We have to allocate a new free List Entry or add to the one
// we already have.
PDCAFLISTENTRY pFreeList = 0; PDC_ATTR *pdcaFreeList = 0;
if (IsListEmpty(&Process->GDIDcAttrFreeList) || ((PDCAFLISTENTRY)Process->GDIDcAttrFreeList.Flink)->cFree == ULNUMDC) { pFreeList = (PDCAFLISTENTRY)PALLOCNOZ(sizeof(DCAFLISTENTRY), 'fcdG');
if (pFreeList == 0) { GreReleaseHmgrSemaphore(); return; }
InsertHeadList(&Process->GDIDcAttrFreeList, (PLIST_ENTRY)pFreeList);
pFreeList->cFree = 0;
} else { pFreeList = (PDCAFLISTENTRY)Process->GDIDcAttrFreeList.Flink; }
pdcaFreeList = (PDC_ATTR *)(&pFreeList->a[0]); // place on process single linked list
pFreeList->cFree++;
pdcaFreeList[pFreeList->cFree-1] = pdca;
Process->pDCAttrList = pdca;
GreReleaseHmgrSemaphore(); } } }
#endif
}
/******************************Public*Routine******************************\
* HmgFreeObjectAttr * * Free the objattr, try to put it on w32thread, if no space then * check other part(s) of block that make up a dcattr. If they are * free then place whole dcattr back on dcattr list. * * Arguments: * * pobjattr * * Return Value: * * none * * History: * * 28-Aug-1995 -by- Mark Enstrom [marke] * \**************************************************************************/
VOID HmgFreeObjectAttr( POBJECTATTR pobjattr ) { PW32THREAD pThread = W32GetCurrentThread();
#if defined(_GDIPLUS_)
VFREEMEM(pobjattr);
#else
if ((pobjattr != NULL) && (pThread != NULL)) { //
// try to place on thread
//
if (pThread->pgdiBrushAttr == NULL) { pThread->pgdiBrushAttr = pobjattr; } else { PW32PROCESS Process = W32GetCurrentProcess();
//
// Need to check if W32 process pointer is valid. There are
// circumstances in which the process can die without setting
// the W32 process.
//
if (Process != NULL) { GreAcquireHmgrSemaphore();
//
// We have to allocate a new free List Entry or add to the one
// we alread have.
POBAFLISTENTRY pFreeList = 0; POBJECTATTR *pobaFreeList = 0;
if (IsListEmpty(&Process->GDIBrushAttrFreeList) || ((POBAFLISTENTRY)Process->GDIBrushAttrFreeList.Flink)->cFree == ULNUMBRUSH) { pFreeList = (POBAFLISTENTRY)PALLOCNOZ(sizeof(OBAFLISTENTRY), 'fabG'); if (pFreeList == 0) { GreReleaseHmgrSemaphore(); return; }
InsertHeadList(&Process->GDIBrushAttrFreeList, (PLIST_ENTRY)pFreeList);
pFreeList->cFree = 0;
} else { pFreeList = (POBAFLISTENTRY)Process->GDIBrushAttrFreeList.Flink; }
pobaFreeList = (POBJECTATTR*)(&pFreeList->a[0]);
//
// place on process single linked list
//
pFreeList->cFree++;
pobaFreeList[pFreeList->cFree-1] = pobjattr;
Process->pBrushAttrList = pobjattr;
GreReleaseHmgrSemaphore(); } } }
#endif
}
/******************************Public*Routine******************************\
* GreIncQuotaCount GreDecQuotaCount * * This is called by user Inc or Dec cursor bitmaps against the creating process * * Notice that cursor bitmaps are marked as PUBLIC by USER but charged against * the creating process's handle quota * * History: * 2/10/98 - by Lingyun Wang [lingyunw] * \**************************************************************************/
VOID GreIncQuotaCount(PW32PROCESS pW32) { InterlockedIncrement(&pW32->GDIHandleCount); }
VOID GreDecQuotaCount(PW32PROCESS pW32) { InterlockedDecrement(&pW32->GDIHandleCount); }
/******************************Public*Routine******************************\
* HmgPrintBadHandle * * Simple routine that prints out a warning when a handle manager * lock fails due to a bad handle. * \**************************************************************************/
#if DBG
CONST CHAR* aszType[] = { "hdef", // DEF_TYPE
"HDC", // DC_TYPE
"hunused", // DD_DIRECTDRAW_TYPE
"hunused", // DD_SURFACE_TYPE
"HRGN", // RGN_TYPE
"HBITMAP", // SURF_TYPE
"HCLIENT", // CLIENTOBJ_TYPE
"hpath", // PATH_TYPE
"HPALETTE", // PAL_TYPE
"HCOLORSPACE", // ICMLCS_TYPE
"HFONT", // LFONT_TYPE
"hrfont", // RFONT_TYPE
"hpfe", // PFE_TYPE
"hpft", // PFT_TYPE
"hicmcxf", // ICMCXF_TYPE
"HSPRITE", // SPRITE_TYPE
"HBRUSH", // BRUSH_TYPE
"hunused", // D3D_HANDLE_TYPE
"hunused", // DD_VIDEOPORT_TYPE
"hspace", // SPACE_TYPE
"hunused", // DD_MOTIONCOMP_TYPE
"HENHMETAFILE", // META_TYPE
"hefstate", // EFSTATE_TYPE
"hunused", // BMFD_TYPE
"hunused", // VTFD_TYPE
"hunused", // TTFD_TYPE
"hunused", // RC_TYPE
"hunused", // TEMP_TYPE
"HDRVOBJ", // DRVOBJ_TYPE
"hunused", // DCIOBJ_TYPE
"hspool", // SPOOL_TYPE
"hunused", //
};
VOID HmgPrintBadHandle( HOBJ hobj, OBJTYPE objt ) { static CHAR *szSystem = "System"; static CHAR *szUnknown = "???"; CHAR *pszImage;
{ PETHREAD pet; PEPROCESS pep;
if (pep = PsGetCurrentProcess()) { pszImage = (CHAR *)PsGetProcessImageFileName(pep); if (*pszImage == '\0') { pszImage = szSystem; } } else { pszImage = szUnknown; } }
KdPrint(("GDI: %s or DLL gave bad handle 0x%p as an %s.\n", pszImage, hobj, aszType[objt]));
// Private debug code to catch invalid hpath handle in DC
// 6/24/98 - davidx
if (hobj != NULL && objt == PATH_TYPE) { RIP("Private debug breakpoint. Please contact ntgdi."); }
}
#endif
|