/*
* GDIOBJ.C - GDI object manipulation routines
*
- * $Id: gdiobj.c,v 1.77 2004/12/13 21:59:28 weiden Exp $
+ * $Id: gdiobj.c,v 1.80 2004/12/19 00:03:56 royce Exp $
*/
#include <w32k.h>
typedef struct _GDI_HANDLE_TABLE
{
PPAGED_LOOKASIDE_LIST LookasideLists;
-
+
SLIST_HEADER FreeEntriesHead;
SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
(sizeof(SLIST_ENTRY) << 3)];
handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
ASSERT( handleTable );
RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
-
+
/*
* initialize the free entry cache
*/
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.
*
ULONG Attempts = 0;
#endif
+ W32Process = PsGetWin32Process();
+ /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
+ to take too many GDI objects, itself. */
+ if ( W32Process && W32Process->GDIObjects >= 0x2710 )
+ return NULL;
+
ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
LookasideList = FindLookasideList(ObjectType);
FIXME - don't shift once ROS' PIDs match with nt! */
CurrentProcessId = (LONG)PsGetCurrentProcessId() << 1;
LockedProcessId = CurrentProcessId | 0x1;
- W32Process = PsGetWin32Process();
newObject->LockingThread = NULL;
newObject->Locks = 0;
RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
-
+
FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
if(FreeEntry != NULL)
{
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]);
if(PrevProcId == 0)
{
ASSERT(Entry->KernelData == NULL);
-
+
Entry->KernelData = ObjectBody;
/* we found a free entry, no need to exchange this field atomically
/* unlock the entry */
InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
+#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;
+ }
+#endif /* DBG */
+
if(W32Process != NULL)
{
InterlockedIncrement(&W32Process->GDIObjects);
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);
#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);
-
+
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))