/* * PROJECT: ReactOS win32 kernel mode subsystem * LICENSE: GPL - See COPYING in the top level directory * FILE: subsystems/win32/win32k/objects/gdiobj.c * PURPOSE: General GDI object manipulation routines * PROGRAMMERS: ... */ /** INCLUDES ******************************************************************/ #define GDI_DEBUG #include #define NDEBUG #include #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 BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody); /** GLOBALS *******************************************************************/ typedef struct { BOOL bUseLookaside; ULONG_PTR ulBodySize; ULONG Tag; GDICLEANUPPROC CleanupProc; } OBJ_TYPE_INFO, *POBJ_TYPE_INFO; static const OBJ_TYPE_INFO ObjTypeInfo[BASE_OBJTYPE_COUNT] = { {0, 0, 0, NULL}, /* 00 reserved entry */ {1, sizeof(DC), TAG_DC, DC_Cleanup}, /* 01 DC */ {1, 0, 0, NULL}, /* 02 UNUSED1 */ {1, 0, 0, NULL}, /* 03 UNUSED2 */ {1, sizeof(ROSRGNDATA), TAG_REGION, REGION_Cleanup}, /* 04 RGN */ {1, sizeof(SURFACE), TAG_SURFACE, SURFACE_Cleanup}, /* 05 SURFACE */ {1, sizeof(CLIENTOBJ), TAG_CLIENTOBJ, GDI_CleanupDummy}, /* 06 CLIENTOBJ: METADC,... */ {1, sizeof(PATH), TAG_PATH, GDI_CleanupDummy}, /* 07 PATH */ {1, sizeof(PALETTE), TAG_PALETTE, PALETTE_Cleanup}, /* 08 PAL */ {1, sizeof(COLORSPACE), TAG_ICMLCS, GDI_CleanupDummy}, /* 09 ICMLCS, */ {1, sizeof(TEXTOBJ), TAG_LFONT, GDI_CleanupDummy}, /* 0a LFONT */ {0, 0, TAG_RFONT, NULL}, /* 0b RFONT, unused */ {0, 0, TAG_PFE, NULL}, /* 0c PFE, unused */ {0, 0, TAG_PFT, NULL}, /* 0d PFT, unused */ {0, sizeof(GDICLRXFORM), TAG_ICMCXF, GDI_CleanupDummy}, /* 0e ICMCXF, */ {0, 0, TAG_SPRITE, NULL}, /* 0f SPRITE, unused */ {1, sizeof(BRUSH), TAG_BRUSH, BRUSH_Cleanup}, /* 10 BRUSH, PEN, EXTPEN */ {0, 0, TAG_UMPD, NULL}, /* 11 UMPD, unused */ {0, 0, 0, NULL}, /* 12 UNUSED4 */ {0, 0, TAG_SPACE, NULL}, /* 13 SPACE, unused */ {0, 0, 0, NULL}, /* 14 UNUSED5 */ {0, 0, TAG_META, NULL}, /* 15 META, unused */ {0, 0, TAG_EFSTATE, NULL}, /* 16 EFSTATE, unused */ {0, 0, TAG_BMFD, NULL}, /* 17 BMFD, unused */ {0, 0, TAG_VTFD, NULL}, /* 18 VTFD, unused */ {0, 0, TAG_TTFD, NULL}, /* 19 TTFD, unused */ {0, 0, TAG_RC, NULL}, /* 1a RC, unused */ {0, 0, TAG_TEMP, NULL}, /* 1b TEMP, unused */ {0, sizeof(EDRIVEROBJ), TAG_DRVOBJ, DRIVEROBJ_Cleanup},/* 1c DRVOBJ */ {0, 0, TAG_DCIOBJ, NULL}, /* 1d DCIOBJ, unused */ {0, 0, TAG_SPOOL, NULL}, /* 1e SPOOL, unused */ {0, 0, 0, NULL}, /* 1f reserved entry */ }; static LARGE_INTEGER ShortDelay; /** 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 BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody) { return TRUE; } /*! * Allocate GDI object table. * \param Size - number of entries in the object table. */ PGDI_HANDLE_TABLE INTERNAL_CALL GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT *SectionObject) { PGDI_HANDLE_TABLE HandleTable = NULL; LARGE_INTEGER htSize; UINT ObjType; ULONG ViewSize = 0; NTSTATUS Status; ASSERT(SectionObject != NULL); htSize.QuadPart = sizeof(GDI_HANDLE_TABLE); Status = MmCreateSection((PVOID*)SectionObject, SECTION_ALL_ACCESS, NULL, &htSize, PAGE_READWRITE, SEC_COMMIT, NULL, NULL); if (!NT_SUCCESS(Status)) return NULL; /* FIXME - use MmMapViewInSessionSpace once available! */ Status = MmMapViewInSystemSpace(*SectionObject, (PVOID*)&HandleTable, &ViewSize); if (!NT_SUCCESS(Status)) { ObDereferenceObject(*SectionObject); *SectionObject = NULL; return NULL; } RtlZeroMemory(HandleTable, sizeof(GDI_HANDLE_TABLE)); HandleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool, BASE_OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST), TAG_GDIHNDTBLE); if (HandleTable->LookasideLists == NULL) { MmUnmapViewInSystemSpace(HandleTable); ObDereferenceObject(*SectionObject); *SectionObject = NULL; return NULL; } for (ObjType = 0; ObjType < BASE_OBJTYPE_COUNT; ObjType++) { if (ObjTypeInfo[ObjType].bUseLookaside) { ExInitializePagedLookasideList(HandleTable->LookasideLists + ObjType, NULL, NULL, 0, ObjTypeInfo[ObjType].ulBodySize, ObjTypeInfo[ObjType].Tag, 0); } } ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */ HandleTable->FirstFree = 0; HandleTable->FirstUnused = RESERVE_ENTRIES_COUNT; return HandleTable; } static void FASTCALL LockErrorDebugOutput(HGDIOBJ hObj, PGDI_TABLE_ENTRY Entry, LPSTR Function) { if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0) { DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function, hObj); GDIDBG_TRACEDELETER(hObj); } else if (GDI_HANDLE_GET_REUSECNT(hObj) != GDI_ENTRY_GET_REUSECNT(Entry->Type)) { DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n", Function, hObj, GDI_HANDLE_GET_REUSECNT(hObj), GDI_ENTRY_GET_REUSECNT(Entry->Type)); } else if (GDI_HANDLE_GET_TYPE(hObj) != ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK)) { DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n", Function, hObj, GDI_HANDLE_GET_TYPE(hObj), (Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK); } else { DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n", Function, hObj, Entry->Type); } GDIDBG_TRACECALLER(); } ULONG FASTCALL InterlockedPopFreeEntry(VOID) { ULONG idxFirst, idxNext, idxPrev; PGDI_TABLE_ENTRY pEntry; DWORD PrevProcId; DPRINT("Enter InterLockedPopFreeEntry\n"); while (TRUE) { idxFirst = GdiHandleTable->FirstFree; if (!idxFirst) { /* Increment FirstUnused and get the new index */ idxFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1; /* Check if we have entries left */ if (idxFirst >= GDI_HANDLE_COUNT) { DPRINT1("No more gdi handles left!\n"); return 0; } /* Return the old index */ return idxFirst; } /* 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; } /* Sanity check: is entry really free? */ ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0); /* 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; } } return idxFirst; } /* Pushes an entry of the handle table to the free list, The entry must be unlocked and the base type field must be 0 */ VOID FASTCALL InterlockedPushFreeEntry(ULONG idxToFree) { ULONG idxFirstFree, idxPrev; 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; do { idxFirstFree = GdiHandleTable->FirstFree; pFreeEntry->KernelData = (PVOID)(ULONG_PTR)idxFirstFree; idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree, idxToFree, idxFirstFree); } while (idxPrev != idxFirstFree); } BOOL INTERNAL_CALL GDIOBJ_ValidateHandle(HGDIOBJ hObj, ULONG ObjectType) { PGDI_TABLE_ENTRY Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj); if ((((ULONG_PTR)hObj & GDI_HANDLE_TYPE_MASK) == ObjectType) && (Entry->Type << GDI_ENTRY_UPPER_SHIFT) == GDI_HANDLE_GET_UPPER(hObj)) { HANDLE pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1); if (pid == NULL || pid == PsGetCurrentProcessId()) { return TRUE; } } return FALSE; } POBJ INTERNAL_CALL GDIOBJ_AllocObj(UCHAR BaseType) { POBJ pObject; ASSERT((BaseType & ~GDIObjTypeTotal) == 0); // BaseType &= GDI_HANDLE_BASETYPE_MASK; if (ObjTypeInfo[BaseType].bUseLookaside) { PPAGED_LOOKASIDE_LIST LookasideList; LookasideList = GdiHandleTable->LookasideLists + BaseType; pObject = ExAllocateFromPagedLookasideList(LookasideList); } else { pObject = ExAllocatePoolWithTag(PagedPool, ObjTypeInfo[BaseType].ulBodySize, ObjTypeInfo[BaseType].Tag); } if (pObject) { RtlZeroMemory(pObject, ObjTypeInfo[BaseType].ulBodySize); } return pObject; } /*! * Allocate memory for GDI object and return handle to it. * * \param ObjectType - type of object \ref GDI object types * * \return Pointer to the allocated object, which is locked. */ POBJ INTERNAL_CALL GDIOBJ_AllocObjWithHandle(ULONG ObjectType) { PPROCESSINFO W32Process; POBJ newObject = NULL; HANDLE CurrentProcessId, LockedProcessId; UCHAR TypeIndex; UINT Index; PGDI_TABLE_ENTRY Entry; LONG TypeInfo; GDIDBG_INITLOOPTRACE(); W32Process = PsGetCurrentProcessWin32Process(); /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process to take too many GDI objects, itself. */ 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; } ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE); TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(ObjectType); newObject = GDIOBJ_AllocObj(TypeIndex); if (!newObject) { DPRINT1("Not enough memory to allocate gdi object!\n"); return NULL; } CurrentProcessId = PsGetCurrentProcessId(); LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1); // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize); /* On Windows the higher 16 bit of the type field don't contain the full type from the handle, but the base type. (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */ TypeInfo = (ObjectType & GDI_HANDLE_BASETYPE_MASK) | (ObjectType >> GDI_ENTRY_UPPER_SHIFT); Index = InterlockedPopFreeEntry(); if (Index != 0) { HANDLE PrevProcId; Entry = &GdiHandleTable->Entries[Index]; LockHandle: PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, 0); if (PrevProcId == NULL) { PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread(); HGDIOBJ Handle; Entry->KernelData = newObject; /* copy the reuse-counter */ TypeInfo |= Entry->Type & GDI_ENTRY_REUSE_MASK; /* we found a free entry, no need to exchange this field atomically since we're holding the lock */ Entry->Type = TypeInfo; /* Create a handle */ Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo << GDI_ENTRY_UPPER_SHIFT)); /* Initialize BaseObject fields */ newObject->hHmgr = Handle; newObject->ulShareCount = 0; newObject->cExclusiveLock = 1; newObject->Tid = Thread; AllocTypeDataDump(TypeInfo); /* unlock the entry */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, CurrentProcessId); GDIDBG_CAPTUREALLOCATOR(Index); if (W32Process != NULL) { InterlockedIncrement(&W32Process->GDIHandleCount); } DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, newObject); return newObject; } else { GDIDBG_TRACELOOP(Index, PrevProcId, CurrentProcessId); /* damn, someone is trying to lock the object even though it doesn't even exist anymore, wait a little and try again! FIXME - we shouldn't loop forever! Give up after some time! */ DelayExecution(); /* try again */ goto LockHandle; } } GDIOBJ_FreeObj(newObject, TypeIndex); DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n"); GDIDBG_DUMPHANDLETABLE(); return NULL; } VOID INTERNAL_CALL GDIOBJ_FreeObj(POBJ pObject, UCHAR BaseType) { /* Object must not have a handle! */ ASSERT(pObject->hHmgr == NULL); if (ObjTypeInfo[BaseType].bUseLookaside) { PPAGED_LOOKASIDE_LIST LookasideList; LookasideList = GdiHandleTable->LookasideLists + BaseType; ExFreeToPagedLookasideList(LookasideList, pObject); } else { ExFreePool(pObject); } } /*! * Free memory allocated for the GDI object. For each object type this function calls the * appropriate cleanup routine. * * \param hObj - handle of the object to be deleted. * * \return Returns TRUE if succesful. * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong * to the calling process. * * \bug This function should return VOID and kill the object no matter what... */ BOOL INTERNAL_CALL GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType) { PGDI_TABLE_ENTRY Entry; HANDLE ProcessId, LockedProcessId, PrevProcId; ULONG HandleType, HandleUpper, TypeIndex; BOOL Silent; GDIDBG_INITLOOPTRACE(); 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); GDIDBG_TRACECALLER(); return FALSE; } ProcessId = PsGetCurrentProcessId(); LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1); Silent = (ExpectedType & GDI_OBJECT_TYPE_SILENT); ExpectedType &= ~GDI_OBJECT_TYPE_SILENT; HandleType = GDI_HANDLE_GET_TYPE(hObj); HandleUpper = GDI_HANDLE_GET_UPPER(hObj); /* Check if we have the requested type */ if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE && HandleType != ExpectedType) || HandleType == 0 ) { DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n", hObj, HandleType, ExpectedType); GDIDBG_TRACECALLER(); return FALSE; } Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj); LockHandle: /* lock the object, we must not delete global objects, so don't exchange the locking process ID to zero when attempting to lock a global object... */ PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId); if (PrevProcId == ProcessId) { if ( (Entry->KernelData != NULL) && ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) && ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) ) { POBJ Object; Object = Entry->KernelData; if ((Object->cExclusiveLock == 0 || Object->Tid == (PTHREADINFO)PsGetCurrentThreadWin32Thread()) && Object->ulShareCount == 0) { BOOL Ret; PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process(); /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */ Entry->Type = (Entry->Type + GDI_ENTRY_REUSE_INC) & ~GDI_ENTRY_BASETYPE_MASK; /* unlock the handle slot */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL); /* push this entry to the free list */ InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry)); Object->hHmgr = NULL; if (W32Process != NULL) { InterlockedDecrement(&W32Process->GDIHandleCount); } /* call the cleanup routine. */ 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); GDIDBG_CAPTUREDELETER(hObj); return Ret; } else if (Object->ulShareCount != 0) { 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); /* Don't wait on shared locks */ return FALSE; } else { /* * The object is currently locked by another thread, so freeing is forbidden! */ DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock); GDIDBG_TRACECALLER(); GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj)); (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); /* do not assert here for it will call again from dxg.sys it being call twice */ DelayExecution(); goto LockHandle; } } else { LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj"); (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); } } else if (PrevProcId == LockedProcessId) { GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId); /* the object is currently locked, wait some time and try again. FIXME - we shouldn't loop forever! Give up after some time! */ DelayExecution(); /* try again */ goto LockHandle; } else { if (!Silent) { if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0) { DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj); } else if (((ULONG_PTR)PrevProcId & ~0x1) == 0) { DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj); } else { DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, (ULONG_PTR)PrevProcId & ~0x1, (ULONG_PTR)ProcessId & ~0x1); } 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)); } } return FALSE; } BOOL FASTCALL IsObjectDead(HGDIOBJ hObject) { INT Index = GDI_HANDLE_GET_INDEX(hObject); PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index]; // We check to see if the objects are knocking on deaths door. if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0) return FALSE; else { DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject); return TRUE; // return true and move on. } } BOOL FASTCALL bPEBCacheHandle(HGDIOBJ Handle, int oType, PVOID pAttr) { PGDIHANDLECACHE GdiHandleCache; HGDIOBJ *hPtr; BOOL Ret = FALSE; int Offset = 0, Number; HANDLE Lock; GdiHandleCache = (PGDIHANDLECACHE)NtCurrentTeb()->ProcessEnvironmentBlock->GdiHandleBuffer; switch (oType) { case hctBrushHandle: Offset = 0; break; case hctPenHandle: Offset = CACHE_BRUSH_ENTRIES; break; case hctRegionHandle: Offset = CACHE_BRUSH_ENTRIES+CACHE_PEN_ENTRIES; break; default: return FALSE; } Lock = InterlockedCompareExchangePointer( (PVOID*)&GdiHandleCache->ulLock, NtCurrentTeb(), NULL ); if (Lock) return FALSE; _SEH2_TRY { Number = GdiHandleCache->ulNumHandles[oType]; hPtr = GdiHandleCache->Handle + Offset; 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 PEB 0x%x\n", GdiHandleCache->ulNumHandles[oType], NtCurrentTeb()->ProcessEnvironmentBlock); Ret = TRUE; } } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Ret = FALSE; } _SEH2_END; (void)InterlockedExchangePointer((PVOID*)&GdiHandleCache->ulLock, Lock); return Ret; } /*! * Delete GDI object * \param hObject object handle * \return if the function fails the returned value is FALSE. */ BOOL FASTCALL GreDeleteObject(HGDIOBJ hObject) { INT Index; PGDI_TABLE_ENTRY Entry; DWORD dwObjectType; PVOID pAttr = NULL; DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject); if (!IsObjectDead(hObject)) { dwObjectType = GDIOBJ_GetObjectType(hObject); Index = GDI_HANDLE_GET_INDEX(hObject); Entry = &GdiHandleTable->Entries[Index]; pAttr = Entry->UserData; switch (dwObjectType) { case GDI_OBJECT_TYPE_BRUSH: break; case GDI_OBJECT_TYPE_REGION: /* If pAttr NULL, the probability is high for System Region. */ if ( pAttr && bPEBCacheHandle(hObject, hctRegionHandle, pAttr)) { /* User space handle only! */ return TRUE; } if (pAttr) { FreeObjectAttr(pAttr); Entry->UserData = NULL; } break; case GDI_OBJECT_TYPE_DC: // DC_FreeDcAttr(hObject); break; } return NULL != hObject ? GDIOBJ_FreeObjByHandle(hObject, dwObjectType) : FALSE; } else { DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject); return TRUE; // return true and move on. } } VOID FASTCALL IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType) { PGDI_TABLE_ENTRY Entry, End; ULONG Index = RESERVE_ENTRIES_COUNT; HANDLE ProcId; PPROCESSINFO W32Process; W32Process = (PPROCESSINFO)Process->Win32Process; ASSERT(W32Process); if (W32Process->GDIHandleCount > 0) { ProcId = Process->UniqueProcessId; /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj we should delete it directly here! */ End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT]; for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT]; Entry != End; Entry++, Index++) { /* ignore the lock bit */ if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId) { if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) == ObjectType || ObjectType == GDI_OBJECT_TYPE_DONTCARE) { HGDIOBJ ObjectHandle; /* Create the object handle for the entry, the lower(!) 16 bit of the Type field includes the type of the object including the stock object flag - but since stock objects don't have a process id we can simply ignore this fact here. */ ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT)); if (!GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE)) { DPRINT1("Failed to delete object %p!\n", ObjectHandle); } if (W32Process->GDIHandleCount == 0) { /* there are no more gdi handles for this process, bail */ break; } } } } } } /*! * Internal function. Called when the process is destroyed to free the remaining GDI handles. * \param Process - PID of the process that will be destroyed. */ BOOL INTERNAL_CALL GDI_CleanupForProcess(struct _EPROCESS *Process) { PEPROCESS CurrentProcess; PPROCESSINFO W32Process; DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId); CurrentProcess = PsGetCurrentProcess(); if (CurrentProcess != Process) { KeAttachProcess(&Process->Pcb); } W32Process = (PPROCESSINFO)CurrentProcess->Win32Process; /* Delete objects. Begin with types that are not referenced by other types */ IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE); IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE); IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BITMAP_TYPE); /* Finally finish with what's left */ IntDeleteHandlesForProcess(Process, GDI_OBJECT_TYPE_DONTCARE); if (CurrentProcess != Process) { KeDetachProcess(); } #ifdef GDI_DEBUG GdiDbgHTIntegrityCheck(); #endif DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId); if (W32Process->GDIHandleCount > 0) { DPRINT1("Leaking %d handles!\n", W32Process->GDIHandleCount); } return TRUE; } /*! * Return pointer to the object by handle. * * \param hObj Object handle * \return Pointer to the object. * * \note Process can only get pointer to the objects it created or global objects. * * \todo Get rid of the ExpectedType parameter! */ PGDIOBJ INTERNAL_CALL GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType) { ULONG HandleIndex; PGDI_TABLE_ENTRY Entry; HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId; POBJ Object = NULL; ULONG HandleType, HandleUpper; 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) return NULL; Entry = &GdiHandleTable->Entries[HandleIndex]; /* Check if we have the requested type */ if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE && HandleType != ExpectedType) || HandleType == 0 ) { DPRINT("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n", hObj, HandleType, ExpectedType); GDIDBG_TRACECALLER(); GDIDBG_TRACEALLOCATOR(hObj); GDIDBG_TRACEDELETER(hObj); return NULL; } 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. * It would result in undesired effects and inconsistency of the global * handle table. */ KeEnterCriticalRegion(); /* * Loop until we either successfully lock the handle entry & object or * fail some of the check. */ for (;;) { /* Lock the handle table entry. */ LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1); PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, HandleProcessId); if (PrevProcId == HandleProcessId) { /* * We're locking an object that belongs to our process or it's a * global object if HandleProcessId is 0 here. */ if ( (Entry->KernelData != NULL) && ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) ) { PTHREADINFO Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread(); Object = Entry->KernelData; if (Object->cExclusiveLock == 0) { Object->Tid = Thread; Object->cExclusiveLock = 1; GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj)) } else { if (Object->Tid != Thread) { GDIDBG_TRACELOOP(hObj, Object->Tid, Thread); GDIDBG_TRACECALLER(); GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj)); GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj)); /* Unlock the handle table entry. */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); DelayExecution(); continue; } InterlockedIncrement((PLONG)&Object->cExclusiveLock); } } else { /* * Debugging code. Report attempts to lock deleted handles and * locking type mismatches. */ LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj"); } /* Unlock the handle table entry. */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); break; } else { /* * The handle is currently locked, wait some time and try again. */ GDIDBG_TRACELOOP(hObj, PrevProcId, NULL); DelayExecution(); continue; } } KeLeaveCriticalRegion(); return Object; } /*! * Return pointer to the object by handle (and allow sharing of the handle * across threads). * * \param hObj Object handle * \return Pointer to the object. * * \note Process can only get pointer to the objects it created or global objects. * * \todo Get rid of the ExpectedType parameter! */ PGDIOBJ INTERNAL_CALL GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType) { ULONG HandleIndex; PGDI_TABLE_ENTRY Entry; HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId; POBJ Object = NULL; ULONG_PTR HandleType, HandleUpper; 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) return NULL; /* Check if we have the requested type */ if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE && HandleType != ExpectedType) || HandleType == 0 ) { DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n", hObj, HandleType, ExpectedType); 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) { return NULL; } /* * Prevent the thread from being terminated during the locking process. * It would result in undesired effects and inconsistency of the global * handle table. */ KeEnterCriticalRegion(); /* * Loop until we either successfully lock the handle entry & object or * fail some of the check. */ for (;;) { /* Lock the handle table entry. */ LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1); PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, HandleProcessId); if (PrevProcId == HandleProcessId) { /* * We're locking an object that belongs to our process or it's a * global object if HandleProcessId is 0 here. */ if ( (Entry->KernelData != NULL) && (HandleUpper == (Entry->Type << GDI_ENTRY_UPPER_SHIFT)) ) { Object = (POBJ)Entry->KernelData; GDIDBG_CAPTURESHARELOCKER(HandleIndex); #ifdef GDI_DEBUG3 if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1) { memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG)); RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleShareLocker[HandleIndex], NULL); } #else InterlockedIncrement((PLONG)&Object->ulShareCount); #endif } else { /* * Debugging code. Report attempts to lock deleted handles and * locking type mismatches. */ LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj"); } /* Unlock the handle table entry. */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); break; } else { /* * The handle is currently locked, wait some time and try again. */ DelayExecution(); continue; } } KeLeaveCriticalRegion(); return Object; } BOOL INTERNAL_CALL GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle) { PGDI_TABLE_ENTRY Entry; HANDLE ProcessId; BOOL Ret; DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle); if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle)) { ProcessId = PsGetCurrentProcessId(); Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle); Ret = Entry->KernelData != NULL && (Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 && (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId; return Ret; } return FALSE; } BOOL INTERNAL_CALL GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj) { /* * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!! */ PGDI_TABLE_ENTRY Entry; HANDLE ProcessId, LockedProcessId, PrevProcId; PTHREADINFO Thread; HGDIOBJ hObj; GDIDBG_INITLOOPTRACE(); ASSERT(phObj); hObj = *phObj; DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj); Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread(); if (!GDI_HANDLE_IS_STOCKOBJ(hObj)) { ProcessId = PsGetCurrentProcessId(); LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1); Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj); LockHandle: /* lock the object, we must not convert stock objects, so don't check!!! */ PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId); if (PrevProcId == ProcessId) { LONG NewType, PrevType, OldType; /* we're locking an object that belongs to our process. First calculate the new object type including the stock object flag and then try to exchange it.*/ /* On Windows the higher 16 bit of the type field don't contain the full type from the handle, but the base type. (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */ OldType = ((ULONG)hObj & GDI_HANDLE_BASETYPE_MASK) | ((ULONG)hObj >> GDI_ENTRY_UPPER_SHIFT); /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility we copy them as we can't get them from the handle */ OldType |= Entry->Type & GDI_ENTRY_FLAGS_MASK; /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */ NewType = OldType | GDI_ENTRY_STOCK_MASK; /* Try to exchange the type field - but only if the old (previous type) matches! */ PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType); if (PrevType == OldType && Entry->KernelData != NULL) { PTHREADINFO PrevThread; POBJ Object; /* We successfully set the stock object flag. KernelData should never be NULL here!!! */ ASSERT(Entry->KernelData); Object = Entry->KernelData; PrevThread = Object->Tid; if (Object->cExclusiveLock == 0 || PrevThread == Thread) { /* dereference the process' object counter */ if (PrevProcId != GDI_GLOBAL_PROCESS) { PEPROCESS OldProcess; PPROCESSINFO W32Process; NTSTATUS Status; /* FIXME */ Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess); if (NT_SUCCESS(Status)) { W32Process = (PPROCESSINFO)OldProcess->Win32Process; if (W32Process != NULL) { InterlockedDecrement(&W32Process->GDIHandleCount); } ObDereferenceObject(OldProcess); } } hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK); *phObj = hObj; Object->hHmgr = hObj; /* remove the process id lock and make it global */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS); /* we're done, successfully converted the object */ return TRUE; } else { GDIDBG_TRACELOOP(hObj, PrevThread, Thread); /* WTF?! The object is already locked by a different thread! Release the lock, wait a bit and try again! FIXME - we should give up after some time unless we want to wait forever! */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); DelayExecution(); goto LockHandle; } } else { DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj); DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType, Entry->Type, NewType, Entry->KernelData); } } else if (PrevProcId == LockedProcessId) { GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId); /* the object is currently locked, wait some time and try again. FIXME - we shouldn't loop forever! Give up after some time! */ DelayExecution(); /* try again */ goto LockHandle; } else { DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj); } } return FALSE; } BOOL INTERNAL_CALL GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner) { PGDI_TABLE_ENTRY Entry; HANDLE ProcessId, LockedProcessId, PrevProcId; PTHREADINFO Thread; BOOL Ret = TRUE; GDIDBG_INITLOOPTRACE(); DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0)); Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread(); if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle)) { ProcessId = PsGetCurrentProcessId(); LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1); Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle); LockHandle: /* lock the object, we must not convert stock objects, so don't check!!! */ PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId); if (PrevProcId == ProcessId) { PTHREADINFO PrevThread; if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0) { POBJ Object = Entry->KernelData; PrevThread = Object->Tid; if (Object->cExclusiveLock == 0 || PrevThread == Thread) { PEPROCESS OldProcess; PPROCESSINFO W32Process; NTSTATUS Status; /* dereference the process' object counter */ /* FIXME */ if ((ULONG_PTR)PrevProcId & ~0x1) { Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess); if (NT_SUCCESS(Status)) { W32Process = (PPROCESSINFO)OldProcess->Win32Process; if (W32Process != NULL) { InterlockedDecrement(&W32Process->GDIHandleCount); } ObDereferenceObject(OldProcess); } } 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; /* remove the process id lock and change it to the new process id */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId); /* we're done! */ return Ret; } else { GDIDBG_TRACELOOP(ObjectHandle, PrevThread, Thread); /* WTF?! The object is already locked by a different thread! Release the lock, wait a bit and try again! DO reset the pid lock so we make sure we don't access invalid memory in case the object is being deleted in the meantime (because we don't have aquired a reference at this point). FIXME - we should give up after some time unless we want to wait forever! */ (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId); DelayExecution(); goto LockHandle; } } else { DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle); DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry->Type, Entry->KernelData); Ret = FALSE; } } else if (PrevProcId == LockedProcessId) { GDIDBG_TRACELOOP(ObjectHandle, PrevProcId, ProcessId); /* the object is currently locked, wait some time and try again. FIXME - we shouldn't loop forever! Give up after some time! */ DelayExecution(); /* try again */ goto LockHandle; } else if (((ULONG_PTR)PrevProcId & ~0x1) == 0) { /* allow changing ownership of global objects */ ProcessId = NULL; LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1); goto LockHandle; } else if ((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId()) { DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId()); Ret = FALSE; } else { DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle); Ret = FALSE; } } return Ret; } BOOL INTERNAL_CALL GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo) { PGDI_TABLE_ENTRY FromEntry; PTHREADINFO Thread; HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId; BOOL Ret = TRUE; GDIDBG_INITLOOPTRACE(); DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo); Thread = (PTHREADINFO)PsGetCurrentThreadWin32Thread(); if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo)) { FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom); FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1); FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1); LockHandleFrom: /* lock the object, we must not convert stock objects, so don't check!!! */ FromPrevProcId = InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId); if (FromPrevProcId == FromProcessId) { PTHREADINFO PrevThread; POBJ Object; if ((FromEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0) { Object = FromEntry->KernelData; /* save the pointer to the calling thread so we know it was this thread that locked the object */ PrevThread = Object->Tid; if (Object->cExclusiveLock == 0 || PrevThread == Thread) { /* now let's change the ownership of the target object */ if (((ULONG_PTR)FromPrevProcId & ~0x1) != 0) { PEPROCESS ProcessTo; /* FIXME */ if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo))) { GDIOBJ_SetOwnership(CopyTo, ProcessTo); ObDereferenceObject(ProcessTo); } } else { /* mark the object as global */ GDIOBJ_SetOwnership(CopyTo, NULL); } (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId); } else { GDIDBG_TRACELOOP(CopyFrom, PrevThread, Thread); /* WTF?! The object is already locked by a different thread! Release the lock, wait a bit and try again! DO reset the pid lock so we make sure we don't access invalid memory in case the object is being deleted in the meantime (because we don't have aquired a reference at this point). FIXME - we should give up after some time unless we want to wait forever! */ (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId); DelayExecution(); goto LockHandleFrom; } } else { DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom); Ret = FALSE; } } else if (FromPrevProcId == FromLockedProcessId) { GDIDBG_TRACELOOP(CopyFrom, FromPrevProcId, FromProcessId); /* the object is currently locked, wait some time and try again. FIXME - we shouldn't loop forever! Give up after some time! */ DelayExecution(); /* try again */ goto LockHandleFrom; } else if ((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId()) { /* FIXME - should we really allow copying ownership from objects that we don't even own? */ DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId()); FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1); FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1); goto LockHandleFrom; } else { DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom); Ret = FALSE; } } return Ret; } PVOID INTERNAL_CALL GDI_MapHandleTable(PSECTION_OBJECT SectionObject, PEPROCESS Process) { PVOID MappedView = NULL; NTSTATUS Status; LARGE_INTEGER Offset; ULONG ViewSize = sizeof(GDI_HANDLE_TABLE); Offset.QuadPart = 0; ASSERT(SectionObject != NULL); ASSERT(Process != NULL); Status = MmMapViewOfSection(SectionObject, Process, &MappedView, 0, 0, &Offset, &ViewSize, ViewUnmap, SEC_NO_CHANGE, PAGE_READONLY); if (!NT_SUCCESS(Status)) return NULL; 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; iEntries[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) { HBRUSH hBR; PEPROCESS Owner = NULL; PGDI_TABLE_ENTRY pEntry = NULL; if (!pbr) return FALSE; hBR = pbr->BaseObject.hHmgr; if (!hBR || (GDI_HANDLE_GET_TYPE(hBR) != GDI_OBJECT_TYPE_BRUSH)) return FALSE; else { INT Index = GDI_HANDLE_GET_INDEX((HGDIOBJ)hBR); pEntry = &GdiHandleTable->Entries[Index]; } if (pbr->flAttrs & GDIBRUSH_IS_GLOBAL) { GDIOBJ_ShareUnlockObjByPtr((POBJ)pbr); return TRUE; } if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE) { // Set this Brush to inaccessible mode and to an Owner of NONE. // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask; if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, Owner)) return FALSE; // Deny user access to User Data. pEntry->UserData = NULL; // This hBR is inaccessible! } if (OwnerMask == GDI_OBJ_HMGR_POWNED) { if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, PsGetCurrentProcess() )) return FALSE; // Allow user access to User Data. pEntry->UserData = pbr->pBrushAttr; } return TRUE; } BOOL FASTCALL IntGdiSetDCOwnerEx( HDC hDC, DWORD OwnerMask, BOOL NoSetBrush) { PDC pDC; BOOL Ret = FALSE; if (!hDC || (GDI_HANDLE_GET_TYPE(hDC) != GDI_OBJECT_TYPE_DC)) return FALSE; if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE) { pDC = DC_LockDc ( hDC ); MmCopyFromCaller(&pDC->dcattr, pDC->pdcattr, sizeof(DC_ATTR)); DC_vFreeDcAttr(pDC); DC_UnlockDc( pDC ); if (!DC_SetOwnership( hDC, NULL )) // This hDC is inaccessible! return Ret; } if (OwnerMask == GDI_OBJ_HMGR_POWNED) { pDC = DC_LockDc ( hDC ); ASSERT(pDC->pdcattr == &pDC->dcattr); DC_UnlockDc( pDC ); if (!DC_SetOwnership( hDC, PsGetCurrentProcess() )) return Ret; DC_AllocateDcAttr( hDC ); // Allocate new dcattr DCU_SynchDcAttrtoUser( hDC ); // Copy data from dc to dcattr } if ((OwnerMask != GDI_OBJ_HMGR_NONE) && !NoSetBrush) { pDC = DC_LockDc ( hDC ); if (IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrFill, OwnerMask)) IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrLine, OwnerMask); DC_UnlockDc( pDC ); } return TRUE; } INT FASTCALL GreGetObjectOwner(HGDIOBJ Handle, GDIOBJTYPE ObjType) { INT Ret = GDI_OBJ_HMGR_RESTRICTED; if ( GDI_HANDLE_GET_INDEX(Handle) < GDI_HANDLE_COUNT ) { PGDI_TABLE_ENTRY pEntry = &GdiHandleTable->Entries[GDI_HANDLE_GET_INDEX(Handle)]; if (pEntry->ObjectType == ObjType) { if (pEntry->FullUnique == (GDI_HANDLE_GET_UPPER(Handle) >> GDI_ENTRY_UPPER_SHIFT)) Ret = pEntry->ProcessId & ~1; } } return Ret; } W32KAPI HANDLE APIENTRY NtGdiCreateClientObj( IN ULONG ulType ) { POBJ pObject; HANDLE handle; /* Mask out everything that would change the type in a wrong manner */ ulType &= (GDI_HANDLE_TYPE_MASK & ~GDI_HANDLE_BASETYPE_MASK); /* Allocate a new object */ pObject = GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ | ulType); if (!pObject) { return NULL; } /* get the handle */ handle = pObject->hHmgr; /* Unlock it */ GDIOBJ_UnlockObjByPtr(pObject); return handle; } W32KAPI BOOL APIENTRY NtGdiDeleteClientObj( IN HANDLE h ) { /* We first need to get the real type from the handle */ ULONG type = GDI_HANDLE_GET_TYPE(h); /* Check if it's really a CLIENTOBJ */ if ((type & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE) { /* FIXME: SetLastError? */ return FALSE; } return GDIOBJ_FreeObjByHandle(h, type); } INT FASTCALL IntGdiGetObject(IN HANDLE Handle, IN INT cbCount, IN LPVOID lpBuffer) { PVOID pGdiObject; INT Result = 0; DWORD dwObjectType; pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE); if (!pGdiObject) { SetLastWin32Error(ERROR_INVALID_HANDLE); return 0; } dwObjectType = GDIOBJ_GetObjectType(Handle); switch (dwObjectType) { case GDI_OBJECT_TYPE_PEN: case GDI_OBJECT_TYPE_EXTPEN: Result = PEN_GetObject((PBRUSH) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect break; case GDI_OBJECT_TYPE_BRUSH: Result = BRUSH_GetObject((PBRUSH ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer); break; case GDI_OBJECT_TYPE_BITMAP: Result = BITMAP_GetObject((SURFACE *) pGdiObject, cbCount, lpBuffer); break; case GDI_OBJECT_TYPE_FONT: Result = FontGetObject((PTEXTOBJ) pGdiObject, cbCount, lpBuffer); #if 0 // Fix the LOGFONT structure for the stock fonts if (FIRST_STOCK_HANDLE <= Handle && Handle <= LAST_STOCK_HANDLE) { FixStockFontSizeW(Handle, cbCount, lpBuffer); } #endif break; case GDI_OBJECT_TYPE_PALETTE: Result = PALETTE_GetObject((PPALETTE) pGdiObject, cbCount, lpBuffer); break; default: DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType); break; } GDIOBJ_UnlockObjByPtr(pGdiObject); return Result; } W32KAPI INT APIENTRY NtGdiExtGetObjectW(IN HANDLE hGdiObj, IN INT cbCount, OUT LPVOID lpBuffer) { INT iRetCount = 0; INT cbCopyCount; union { BITMAP bitmap; DIBSECTION dibsection; LOGPEN logpen; LOGBRUSH logbrush; LOGFONTW logfontw; EXTLOGFONTW extlogfontw; ENUMLOGFONTEXDVW enumlogfontexdvw; } Object; // Normalize to the largest supported object size cbCount = min((UINT)cbCount, sizeof(Object)); // Now do the actual call iRetCount = IntGdiGetObject(hGdiObj, cbCount, lpBuffer ? &Object : NULL); cbCopyCount = min((UINT)cbCount, (UINT)iRetCount); // Make sure we have a buffer and a copy size if ((cbCopyCount) && (lpBuffer)) { // Enter SEH for buffer transfer _SEH2_TRY { // Probe the buffer and copy it ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD)); RtlCopyMemory(lpBuffer, &Object, cbCopyCount); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { // Clear the return value. // Do *NOT* set last error here! iRetCount = 0; } _SEH2_END; } // Return the count return iRetCount; } /* EOF */