/*
* GDIOBJ.C - GDI object manipulation routines
*
- * $Id: gdiobj.c,v 1.76 2004/12/13 12:51:51 weiden Exp $
+ * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
*/
#include <w32k.h>
typedef struct _GDI_HANDLE_TABLE
{
- LONG HandlesCount;
- LONG nEntries;
PPAGED_LOOKASIDE_LIST LookasideLists;
- PGDI_TABLE_ENTRY EntriesEnd;
+ SLIST_HEADER FreeEntriesHead;
+ SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
+ (sizeof(SLIST_ENTRY) << 3)];
- GDI_TABLE_ENTRY Entries[1];
+ GDI_TABLE_ENTRY Entries[GDI_HANDLE_COUNT];
} GDI_HANDLE_TABLE, *PGDI_HANDLE_TABLE;
typedef struct
* \param Size - number of entries in the object table.
*/
static PGDI_HANDLE_TABLE INTERNAL_CALL
-GDIOBJ_iAllocHandleTable(ULONG Entries)
+GDIOBJ_iAllocHandleTable(VOID)
{
PGDI_HANDLE_TABLE handleTable;
UINT ObjType;
- ULONG MemSize = sizeof(GDI_HANDLE_TABLE) + (sizeof(GDI_TABLE_ENTRY) * (Entries - 1));
+ UINT i;
+ PGDI_TABLE_ENTRY Entry;
- handleTable = ExAllocatePoolWithTag(NonPagedPool, MemSize, TAG_GDIHNDTBLE);
+ handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
ASSERT( handleTable );
- RtlZeroMemory(handleTable, MemSize);
-
- handleTable->HandlesCount = 0;
- handleTable->nEntries = Entries;
-
- handleTable->EntriesEnd = &handleTable->Entries[Entries];
+ RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
+
+ /*
+ * initialize the free entry cache
+ */
+ InitializeSListHead(&handleTable->FreeEntriesHead);
+ Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
+ for(i = GDI_HANDLE_COUNT - 1; i >= RESERVE_ENTRIES_COUNT; i--)
+ {
+ InterlockedPushEntrySList(&handleTable->FreeEntriesHead, &handleTable->FreeEntries[i]);
+ }
handleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
return 0;
}
+#ifdef DBG
+
+static int leak_reported = 0;
+#define GDI_STACK_LEVELS 4
+static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
+struct DbgOpenGDIHandle
+{
+ ULONG loc;
+ int count;
+};
+#define H 1024
+static struct DbgOpenGDIHandle h[H];
+
+void IntDumpHandleTable ( int which )
+{
+ int i, n = 0, j;
+
+ // step through GDI handle table and find out who our culprit is...
+ for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
+ {
+ for ( j = 0; j < n; j++ )
+ {
+ if ( GDIHandleAllocator[which][i] == h[j].loc )
+ break;
+ }
+ if ( j < H )
+ {
+ if ( j == n )
+ {
+ h[j].loc = GDIHandleAllocator[which][i];
+ h[j].count = 1;
+ n = n + 1;
+ }
+ else
+ h[j].count++;
+ }
+ }
+ // bubble sort time! weeeeee!!
+ for ( i = 0; i < n-1; i++ )
+ {
+ if ( h[i].count < h[i+1].count )
+ {
+ struct DbgOpenGDIHandle t;
+ t.loc = h[i+1].loc;
+ t.count = h[i+1].count;
+ h[i+1].loc = h[i].loc;
+ h[i+1].count = h[i].count;
+ j = i;
+ while ( j > 0 && h[j-1].count < t.count )
+ j--;
+ h[j] = t;
+ }
+ }
+ // print the first 30 offenders...
+ DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
+ for ( i = 0; i < 30 && i < n; i++ )
+ {
+ DbgPrint ( "\t" );
+ if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
+ DbgPrint ( "<%X>", h[i].loc );
+ DbgPrint ( " (%i allocations)\n", h[i].count );
+ }
+}
+#endif//DBG
+
/*!
* Allocate memory for GDI object and return handle to it.
*
* \todo return the object pointer and lock it by default.
*/
HGDIOBJ INTERNAL_CALL
+#ifdef GDI_DEBUG
+GDIOBJ_AllocObjDbg(const char* file, int line, ULONG ObjectType)
+#else /* !GDI_DEBUG */
GDIOBJ_AllocObj(ULONG ObjectType)
+#endif /* GDI_DEBUG */
{
PW32PROCESS W32Process;
PGDIOBJHDR newObject;
PPAGED_LOOKASIDE_LIST LookasideList;
LONG CurrentProcessId, LockedProcessId;
+#ifdef GDI_DEBUG
+ ULONG Attempts = 0;
+#endif
ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
newObject = ExAllocateFromPagedLookasideList(LookasideList);
if(newObject != NULL)
{
+ PSLIST_ENTRY FreeEntry;
PGDI_TABLE_ENTRY Entry;
PGDIOBJ ObjectBody;
LONG TypeInfo;
newObject->Locks = 0;
#ifdef GDI_DEBUG
+ newObject->createdfile = file;
+ newObject->createdline = line;
newObject->lockfile = NULL;
newObject->lockline = 0;
#endif
TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
- /* Search for a free handle entry */
- for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
- Entry < HandleTable->EntriesEnd;
- Entry++)
+ FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
+ if(FreeEntry != NULL)
{
- LONG PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
+ LONG PrevProcId;
+ UINT Index;
+ HGDIOBJ Handle;
+
+ /* calculate the entry from the address of the entry in the free slot array */
+ Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
+ sizeof(HandleTable->FreeEntries[0]);
+ Entry = &HandleTable->Entries[Index];
+ Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
+
+LockHandle:
+ PrevProcId = InterlockedCompareExchange(&Entry->ProcessId, LockedProcessId, 0);
if(PrevProcId == 0)
{
- if(InterlockedCompareExchangePointer(&Entry->KernelData, ObjectBody, NULL) == NULL)
- {
- HGDIOBJ Handle;
- UINT Index = GDI_ENTRY_TO_INDEX(HandleTable, Entry);
-
- Handle = (HGDIOBJ)((Index & 0xFFFF) | (ObjectType & 0xFFFF0000));
+ ASSERT(Entry->KernelData == NULL);
- /* we found a free entry, no need to exchange this field atomically
- since we're holding the lock */
- Entry->Type = TypeInfo;
+ Entry->KernelData = ObjectBody;
- InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
+ /* we found a free entry, no need to exchange this field atomically
+ since we're holding the lock */
+ Entry->Type = TypeInfo;
- if(W32Process != NULL)
- {
- InterlockedIncrement(&W32Process->GDIObjects);
- }
+ /* unlock the entry */
+ InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
- DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
- return Handle;
+#ifdef DBG
+ {
+ PULONG Frame;
+ int which;
+#if defined __GNUC__
+ __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+#elif defined(_MSC_VER)
+ __asm mov [Frame], ebp
+#endif
+ Frame = (PULONG)Frame[0]; // step out of AllocObj()
+ for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
+ {
+ GDIHandleAllocator[which][Index] = Frame[1]; // step out of AllocObj()
+ Frame = ((PULONG)Frame[0]);
+ }
+ for ( ; which < GDI_STACK_LEVELS; which++ )
+ GDIHandleAllocator[which][Index] = 0xDEADBEEF;
}
- else
+#endif//DBG
+ //ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
+
+ if(W32Process != NULL)
+ {
+ InterlockedIncrement(&W32Process->GDIObjects);
+ }
+
+ DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
+ return Handle;
+ }
+ else
+ {
+#ifdef GDI_DEBUG
+ if(++Attempts > 20)
{
- InterlockedExchange(&Entry->ProcessId, PrevProcId);
+ DPRINT1("[%d]Waiting on 0x%x\n", Attempts, Handle);
}
+#endif
+ /* damn, someone is trying to lock the object even though it doesn't
+ eve nexist anymore, wait a little and try again!
+ FIXME - we shouldn't loop forever! Give up after some time! */
+ DelayExecution();
+ /* try again */
+ goto LockHandle;
}
}
ExFreeToPagedLookasideList(LookasideList, newObject);
DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
+#ifdef DBG
+ if ( !leak_reported )
+ {
+ DPRINT1("reporting gdi handle abusers:\n");
+ int which;
+ for ( which = 0; which < GDI_STACK_LEVELS; which++ )
+ IntDumpHandleTable(which);
+ leak_reported = 1;
+ }
+ else
+ {
+ DPRINT1("gdi handle abusers already reported!\n");
+ }
+#endif//DBG
}
else
{
#endif
DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
-
+
if(GDI_HANDLE_IS_STOCKOBJ(hObj))
{
DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
/* Clear the type field so when unlocking the handle it gets finally deleted */
Entry->Type = 0;
Entry->KernelData = NULL;
-
+
/* unlock the handle slot */
InterlockedExchange(&Entry->ProcessId, 0);
-
+
+ /* push this entry to the free list */
+ InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+ &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
if(W32Process != NULL)
{
InterlockedDecrement(&W32Process->GDIObjects);
/* the object is currently locked. just clear the type field so when the
object gets unlocked it will be finally deleted from the table. */
Entry->Type = 0;
-
+
/* unlock the handle slot */
InterlockedExchange(&Entry->ProcessId, 0);
{
DPRINT("InitGdiObjectHandleTable\n");
- HandleTable = GDIOBJ_iAllocHandleTable (GDI_HANDLE_COUNT);
+ HandleTable = GDIOBJ_iAllocHandleTable();
DPRINT("HandleTable: %x\n", HandleTable);
}
BOOL INTERNAL_CALL
GDI_CleanupForProcess (struct _EPROCESS *Process)
{
- PGDI_TABLE_ENTRY Entry;
+ PGDI_TABLE_ENTRY Entry, End;
PEPROCESS CurrentProcess;
PW32PROCESS W32Process;
LONG ProcId;
we should delete it directly here! */
ProcId = ((LONG)Process->UniqueProcessId << 1);
+ End = &HandleTable->Entries[GDI_HANDLE_COUNT];
for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
- Entry < HandleTable->EntriesEnd;
+ Entry < End;
Entry++, Index++)
{
/* ignore the lock bit */
#endif
DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
-
+
Thread = PsGetCurrentThread();
/* shift the process id to the left so we can use the first bit to lock the object.
that locked the object. There's no need to do this atomically as we're
holding the lock of the handle slot, but this way it's easier ;) */
PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
-
+
if(PrevThread == NULL || PrevThread == Thread)
{
if(++GdiHdr->Locks == 1)
}
InterlockedExchange(&Entry->ProcessId, PrevProcId);
-
+
/* we're done, return the object body */
return GDIHdrToBdy(GdiHdr);
}
else
{
InterlockedExchange(&Entry->ProcessId, PrevProcId);
-
+
#ifdef GDI_DEBUG
if(++Attempts > 20)
{
else
{
InterlockedExchange(&Entry->ProcessId, PrevProcId);
-
+
if(EntryType == 0)
{
DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
+ KeRosDumpStackFrames ( NULL, 20 );
}
else
{
DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
+ KeRosDumpStackFrames ( NULL, 20 );
}
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
else
{
DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
+ KeRosDumpStackFrames ( NULL, 20 );
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
#endif
PGDIOBJHDR GdiHdr;
GdiHdr = GDIBdyToHdr(Entry->KernelData);
-
+ //ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
+
PrevThread = GdiHdr->LockingThread;
if(PrevThread == Thread)
{
if(--GdiHdr->Locks == 0)
{
GdiHdr->LockingThread = NULL;
-
+
#ifdef GDI_DEBUG
GdiHdr->lockfile = NULL;
GdiHdr->lockline = 0;
#endif
}
-
+
if(Entry->Type == 0 && GdiHdr->Locks == 0)
{
PPAGED_LOOKASIDE_LIST LookasideList;
PW32PROCESS W32Process = PsGetWin32Process();
DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
-
+
ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
-
+
/* we should delete the handle */
Entry->KernelData = NULL;
InterlockedExchange(&Entry->ProcessId, 0);
-
+
+ InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
+ &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
+
if(W32Process != NULL)
{
InterlockedDecrement(&W32Process->GDIObjects);
}
-
+
/* call the cleanup routine. */
Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
#ifdef GDI_DEBUG
ULONG Attempts = 0;
#endif
-
+
ASSERT(hObj);
DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
-
+
Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
#endif
DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
-
+
Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
if(Entry->Type != 0 && Entry->KernelData != NULL)
{
PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
-
+
PrevThread = GdiHdr->LockingThread;
if(PrevThread == NULL || PrevThread == Thread)
{
}
else if((PrevProcId >> 1) != (LONG)PsGetCurrentProcessId())
{
- /* FIXME - should we really allow changing the ownership of objects we don't own? */
- DPRINT1("WARNING! Changing ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
- ProcessId = PrevProcId & ~0x1;
- LockedProcessId = ProcessId | 0x1;
- goto LockHandle;
+ DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, PrevProcId >> 1, PsGetCurrentProcessId());
}
else
{
#endif
DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
-
+
Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))