/** INCLUDES ******************************************************************/
-//#define GDI_DEBUG
-
-#include <w32k.h>
+#include <win32k.h>
#define NDEBUG
#include <debug.h>
-#define GDI_ENTRY_TO_INDEX(ht, e) \
- (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
-#define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
- (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
-
-/* apparently the first 10 entries are never used in windows as they are empty */
-#define RESERVE_ENTRIES_COUNT 10
-
#define BASE_OBJTYPE_COUNT 32
#define DelayExecution() \
DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
-#include "gdidbg.c"
-
-/* static */ /* FIXME: -fno-unit-at-a-time breaks this */
+static
BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody);
/** GLOBALS *******************************************************************/
};
static LARGE_INTEGER ShortDelay;
+PGDI_HANDLE_TABLE GdiHandleTable = NULL;
+PSECTION_OBJECT GdiTableSection = NULL;
/** INTERNAL FUNCTIONS ********************************************************/
+// Audit Functions
+int tDC = 0;
+int tBRUSH = 0;
+int tBITMAP = 0;
+int tFONT = 0;
+int tRGN = 0;
+
+VOID
+AllocTypeDataDump(INT TypeInfo)
+{
+ switch( TypeInfo & GDI_HANDLE_TYPE_MASK )
+ {
+ case GDILoObjType_LO_BRUSH_TYPE:
+ tBRUSH++;
+ break;
+ case GDILoObjType_LO_DC_TYPE:
+ tDC++;
+ break;
+ case GDILoObjType_LO_BITMAP_TYPE:
+ tBITMAP++;
+ break;
+ case GDILoObjType_LO_FONT_TYPE:
+ tFONT++;
+ break;
+ case GDILoObjType_LO_REGION_TYPE:
+ tRGN++;
+ break;
+ }
+}
+
+VOID
+DeAllocTypeDataDump(INT TypeInfo)
+{
+ switch( TypeInfo & GDI_HANDLE_TYPE_MASK )
+ {
+ case GDILoObjType_LO_BRUSH_TYPE:
+ tBRUSH--;
+ break;
+ case GDILoObjType_LO_DC_TYPE:
+ tDC--;
+ break;
+ case GDILoObjType_LO_BITMAP_TYPE:
+ tBITMAP--;
+ break;
+ case GDILoObjType_LO_FONT_TYPE:
+ tFONT--;
+ break;
+ case GDILoObjType_LO_REGION_TYPE:
+ tRGN--;
+ break;
+ }
+}
+
/*
* Dummy GDI Cleanup Callback
*/
-/* static */ /* FIXME: -fno-unit-at-a-time breaks this */
+static
BOOL INTERNAL_CALL
GDI_CleanupDummy(PVOID ObjectBody)
{
* Allocate GDI object table.
* \param Size - number of entries in the object table.
*/
-PGDI_HANDLE_TABLE INTERNAL_CALL
+INIT_FUNCTION
+PGDI_HANDLE_TABLE
+INTERNAL_CALL
GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT *SectionObject)
{
PGDI_HANDLE_TABLE HandleTable = NULL;
return HandleTable;
}
+INIT_FUNCTION
+NTSTATUS
+NTAPI
+InitGdiHandleTable()
+{
+ /* Create the GDI handle table */
+ GdiHandleTable = GDIOBJ_iAllocHandleTable(&GdiTableSection);
+ if (GdiHandleTable == NULL)
+ {
+ DPRINT1("Failed to initialize the GDI handle table.\n");
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
static void FASTCALL
LockErrorDebugOutput(HGDIOBJ hObj, PGDI_TABLE_ENTRY Entry, LPSTR Function)
{
FASTCALL
InterlockedPopFreeEntry(VOID)
{
- ULONG idxFirst, idxNext, idxPrev;
+ ULONG iFirst, iNext, iPrev;
PGDI_TABLE_ENTRY pEntry;
- DWORD PrevProcId;
DPRINT("Enter InterLockedPopFreeEntry\n");
- while (TRUE)
+ do
{
- idxFirst = GdiHandleTable->FirstFree;
+ /* Get the index and sequence number of the first free entry */
+ iFirst = GdiHandleTable->FirstFree;
- if (!idxFirst)
+ /* Check if we have a free entry */
+ if (!(iFirst & GDI_HANDLE_INDEX_MASK))
{
/* Increment FirstUnused and get the new index */
- idxFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
+ iFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
- /* Check if we have entries left */
- if (idxFirst >= GDI_HANDLE_COUNT)
+ /* Check if we have unused entries left */
+ if (iFirst >= GDI_HANDLE_COUNT)
{
DPRINT1("No more gdi handles left!\n");
return 0;
}
/* Return the old index */
- return idxFirst;
+ return iFirst;
}
/* Get a pointer to the first free entry */
- pEntry = GdiHandleTable->Entries + idxFirst;
-
- /* Try to lock the entry */
- PrevProcId = InterlockedCompareExchange((LONG*)&pEntry->ProcessId, 1, 0);
- if (PrevProcId != 0)
- {
- /* The entry was locked or not free, wait and start over */
- DelayExecution();
- continue;
- }
+ pEntry = GdiHandleTable->Entries + (iFirst & GDI_HANDLE_INDEX_MASK);
- /* Sanity check: is entry really free? */
- ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
+ /* Create a new value with an increased sequence number */
+ iNext = (USHORT)(ULONG_PTR)pEntry->KernelData;
+ iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
/* Try to exchange the FirstFree value */
- idxNext = (ULONG_PTR)pEntry->KernelData;
- idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
- idxNext,
- idxFirst);
-
- /* Unlock the free entry */
- (void)InterlockedExchange((LONG*)&pEntry->ProcessId, 0);
-
- /* If we succeeded, break out of the loop */
- if (idxPrev == idxFirst)
- {
- break;
- }
+ iPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
+ iNext,
+ iFirst);
}
+ while (iPrev != iFirst);
- return idxFirst;
+ /* Sanity check: is entry really free? */
+ ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
+
+ return iFirst & GDI_HANDLE_INDEX_MASK;
}
/* Pushes an entry of the handle table to the free list,
FASTCALL
InterlockedPushFreeEntry(ULONG idxToFree)
{
- ULONG idxFirstFree, idxPrev;
+ ULONG iToFree, iFirst, iPrev;
PGDI_TABLE_ENTRY pFreeEntry;
DPRINT("Enter InterlockedPushFreeEntry\n");
pFreeEntry = GdiHandleTable->Entries + idxToFree;
ASSERT((pFreeEntry->Type & GDI_ENTRY_BASETYPE_MASK) == 0);
ASSERT(pFreeEntry->ProcessId == 0);
- pFreeEntry->UserData = NULL;
+ pFreeEntry->UserData = NULL; // FIXME
+ ASSERT(pFreeEntry->UserData == NULL);
do
{
- idxFirstFree = GdiHandleTable->FirstFree;
- pFreeEntry->KernelData = (PVOID)(ULONG_PTR)idxFirstFree;
+ /* Get the current first free index and sequence number */
+ iFirst = GdiHandleTable->FirstFree;
+
+ /* Set the KernelData member to the index of the first free entry */
+ pFreeEntry->KernelData = UlongToPtr(iFirst & GDI_HANDLE_INDEX_MASK);
- idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
- idxToFree,
- idxFirstFree);
+ /* Combine new index and increased sequence number in iToFree */
+ iToFree = idxToFree | ((iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000);
+
+ /* Try to atomically update the first free entry */
+ iPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
+ iToFree,
+ iFirst);
}
- while (idxPrev != idxFirstFree);
+ while (iPrev != iFirst);
}
if (W32Process && W32Process->GDIHandleCount >= 0x2710)
{
DPRINT1("Too many objects for process!!!\n");
+ DPRINT1("DC %d BRUSH %d BITMAP %d FONT %d RGN %d\n",tDC,tBRUSH,tBITMAP,tFONT,tRGN);
GDIDBG_DUMPHANDLETABLE();
return NULL;
}
newObject->ulShareCount = 0;
newObject->cExclusiveLock = 1;
newObject->Tid = Thread;
+#if DBG
+ if (Thread) Thread->cExclusiveLocks++;
+#endif
+
+ AllocTypeDataDump(TypeInfo);
/* unlock the entry */
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, CurrentProcessId);
((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
{
POBJ Object;
+ PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
Object = Entry->KernelData;
- if ((Object->cExclusiveLock == 0 ||
- Object->Tid == (PTHREADINFO)PsGetCurrentThreadWin32Thread()) &&
+ if ((Object->cExclusiveLock == 0 || Object->Tid == Thread) &&
Object->ulShareCount == 0)
{
BOOL Ret;
InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
Object->hHmgr = NULL;
+#if DBG
+ if (Thread)
+ {
+ if (Thread->cExclusiveLocks < Object->cExclusiveLock)
+ {
+ DPRINT1("cExclusiveLocks = %ld, object: %ld\n",
+ Thread->cExclusiveLocks, Object->cExclusiveLock);
+ ASSERT(FALSE);
+ }
+ Thread->cExclusiveLocks -= Object->cExclusiveLock;
+ }
+#endif
if (W32Process != NULL)
{
TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(HandleType);
Ret = ObjTypeInfo[TypeIndex].CleanupProc(Object);
+ DeAllocTypeDataDump(HandleType);
+
/* Now it's time to free the memory */
GDIOBJ_FreeObj(Object, TypeIndex);
}
else if (Object->ulShareCount != 0)
{
+ NTSTATUS Status;
+ PEPROCESS OldProcess;
Object->BaseFlags |= BASEFLAG_READY_TO_DIE;
DPRINT("Object %p, ulShareCount = %d\n", Object->hHmgr, Object->ulShareCount);
- //GDIDBG_TRACECALLER();
- //GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
- (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
+ /* Set NULL owner. Do the work here to avoid race conditions */
+ Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
+ if (NT_SUCCESS(Status))
+ {
+ PPROCESSINFO W32Process = (PPROCESSINFO)OldProcess->Win32Process;
+ if (W32Process != NULL)
+ {
+ InterlockedDecrement(&W32Process->GDIHandleCount);
+ }
+ ObDereferenceObject(OldProcess);
+ }
+ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
/* Don't wait on shared locks */
return FALSE;
}
*/
DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock);
GDIDBG_TRACECALLER();
- GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj));
+ GDIDBG_TRACELOCKER(hObj);
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
/* do not assert here for it will call again from dxg.sys it being call twice */
}
DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry->Type, Entry->KernelData, Entry->ProcessId);
GDIDBG_TRACECALLER();
- GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
+ GDIDBG_TRACEALLOCATOR(hObj);
}
}
hPtr = GdiHandleCache->Handle + Offset;
- if ( oType == hctRegionHandle)
+ if ( pAttr && oType == hctRegionHandle)
{
if ( Number < CACHE_REGION_ENTRIES )
{
((PRGN_ATTR)pAttr)->AttrFlags |= ATTR_CACHED;
hPtr[Number] = Handle;
GdiHandleCache->ulNumHandles[oType]++;
- DPRINT("Put Handle Count %d\n", GdiHandleCache->ulNumHandles[oType]);
+ DPRINT("Put Handle Count %d PEB 0x%x\n", GdiHandleCache->ulNumHandles[oType], NtCurrentTeb()->ProcessEnvironmentBlock);
Ret = TRUE;
}
}
break;
case GDI_OBJECT_TYPE_REGION:
- if (bPEBCacheHandle(hObject, hctRegionHandle, pAttr))
+ /* If pAttr NULL, the probability is high for System Region. */
+ if ( pAttr &&
+ bPEBCacheHandle(hObject, hctRegionHandle, pAttr))
{
+ /* User space handle only! */
return TRUE;
}
if (pAttr)
{
- KeEnterCriticalRegion();
FreeObjectAttr(pAttr);
- KeLeaveCriticalRegion();
+ Entry->UserData = NULL;
}
break;
case GDI_OBJECT_TYPE_DC:
- DC_FreeDcAttr(hObject);
+// DC_FreeDcAttr(hObject);
break;
}
POBJ Object = NULL;
ULONG HandleType, HandleUpper;
+ /* Check for dummy call */
+ if(hObj == NULL)
+ return NULL ;
+
+ GDIDBG_INITLOOPTRACE();
+
HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
HandleType = GDI_HANDLE_GET_TYPE(hObj);
HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
/* Check that the handle index is valid. */
- if (HandleIndex >= GDI_HANDLE_COUNT)
+ if (HandleIndex >= GDI_HANDLE_COUNT )
return NULL;
Entry = &GdiHandleTable->Entries[HandleIndex];
}
ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
- HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
-
- /* Check for invalid owner. */
- if (ProcessId != HandleProcessId && HandleProcessId != NULL)
- {
- DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
- GDIDBG_TRACECALLER();
- GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
- return NULL;
- }
/*
* Prevent the thread from being terminated during the locking process.
for (;;)
{
+ HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
+
+ /* Check for invalid owner. */
+ if (ProcessId != HandleProcessId && HandleProcessId != NULL)
+ {
+ DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
+ GDIDBG_TRACECALLER();
+ GDIDBG_TRACEALLOCATOR(hObj);
+ break;
+ }
+
/* Lock the handle table entry. */
LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
Object->Tid = Thread;
Object->cExclusiveLock = 1;
GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
+#if DBG
+ if (Thread) Thread->cExclusiveLocks++;
+#endif
}
else
{
continue;
}
InterlockedIncrement((PLONG)&Object->cExclusiveLock);
+#if DBG
+ if (Thread) Thread->cExclusiveLocks++;
+#endif
}
}
else
/*
* The handle is currently locked, wait some time and try again.
*/
+ GDIDBG_TRACELOOP(hObj, PrevProcId, NULL);
DelayExecution();
continue;
POBJ Object = NULL;
ULONG_PTR HandleType, HandleUpper;
+ /* Check for dummy call */
+ if(hObj == NULL)
+ return NULL ;
+
HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
HandleType = GDI_HANDLE_GET_TYPE(hObj);
HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
PPROCESSINFO W32Process;
NTSTATUS Status;
+ if (NewOwner != NULL)
+ {
+ ProcessId = PsGetProcessId(NewOwner);
+ }
+ else
+ ProcessId = 0;
+
+ if((ULONG_PTR)ProcessId == ((ULONG_PTR)PrevProcId & ~0x1))
+ {
+ DPRINT("Setting same process than previous one, nothing to do\n");
+ goto done;
+ }
+
/* dereference the process' object counter */
/* FIXME */
if ((ULONG_PTR)PrevProcId & ~0x1)
if (NewOwner != NULL)
{
- ProcessId = PsGetProcessId(NewOwner);
-
/* Increase the new process' object counter */
W32Process = (PPROCESSINFO)NewOwner->Win32Process;
if (W32Process != NULL)
InterlockedIncrement(&W32Process->GDIHandleCount);
}
}
- else
- ProcessId = 0;
+ done:
/* remove the process id lock and change it to the new process id */
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
return MappedView;
}
+/* Locks 2 or 3 objects at a time */
+VOID
+INTERNAL_CALL
+GDIOBJ_LockMultipleObjs(ULONG ulCount,
+ IN HGDIOBJ* ahObj,
+ OUT PGDIOBJ* apObj)
+{
+ UINT auiIndices[3] = {0,1,2};
+ UINT i, tmp ;
+ BOOL bUnsorted = TRUE;
+
+ /* First is greatest */
+ while(bUnsorted)
+ {
+ bUnsorted = FALSE;
+ for(i=1; i<ulCount; i++)
+ {
+ if((ULONG_PTR)ahObj[auiIndices[i-1]] < (ULONG_PTR)ahObj[auiIndices[i]])
+ {
+ tmp = auiIndices[i-1];
+ auiIndices[i-1] = auiIndices[i];
+ auiIndices[i] = tmp;
+ bUnsorted = TRUE;
+ }
+ }
+ }
+
+ for(i=0;i<ulCount;i++)
+ apObj[auiIndices[i]] = GDIOBJ_LockObj(ahObj[auiIndices[i]], GDI_OBJECT_TYPE_DONTCARE);
+}
+
+
/** PUBLIC FUNCTIONS **********************************************************/
+BOOL
+FASTCALL
+IntGdiSetRegionOwner(HRGN hRgn, DWORD OwnerMask)
+{
+ INT Index;
+ PGDI_TABLE_ENTRY Entry;
+/*
+ System Regions:
+ These regions do not use attribute sections and when allocated, use gdiobj
+ level functions.
+ */
+ // FIXME! HAX!!! Remove this once we get everything right!
+ Index = GDI_HANDLE_GET_INDEX(hRgn);
+ Entry = &GdiHandleTable->Entries[Index];
+ if (Entry->UserData) FreeObjectAttr(Entry->UserData);
+ Entry->UserData = NULL;
+ //
+ if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE)
+ {
+ return GDIOBJ_SetOwnership(hRgn, NULL);
+ }
+ if (OwnerMask == GDI_OBJ_HMGR_POWNED)
+ {
+ return GDIOBJ_SetOwnership((HGDIOBJ) hRgn, PsGetCurrentProcess() );
+ }
+ return FALSE;
+}
+
BOOL
FASTCALL
IntGdiSetBrushOwner(PBRUSH pbr, DWORD OwnerMask)
return TRUE;
}
-
BOOL
FASTCALL
IntGdiSetDCOwnerEx( HDC hDC, DWORD OwnerMask, BOOL NoSetBrush)
{
pDC = DC_LockDc ( hDC );
MmCopyFromCaller(&pDC->dcattr, pDC->pdcattr, sizeof(DC_ATTR));
+ DC_vFreeDcAttr(pDC);
DC_UnlockDc( pDC );
- DC_FreeDcAttr( hDC ); // Free the dcattr!
-
if (!DC_SetOwnership( hDC, NULL )) // This hDC is inaccessible!
return Ret;
}
return Ret;
}
-
W32KAPI
HANDLE
APIENTRY
pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE);
if (!pGdiObject)
{
- SetLastWin32Error(ERROR_INVALID_HANDLE);
+ EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}