📄 gdiobj.c
字号:
/* 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(HandleTable, ObjectType);
if(LookasideList != NULL)
{
newObject = ExAllocateFromPagedLookasideList(LookasideList);
if(newObject != NULL)
{
PSLIST_ENTRY FreeEntry;
PGDI_TABLE_ENTRY Entry;
PGDIOBJ ObjectBody;
LONG TypeInfo;
CurrentProcessId = PsGetCurrentProcessId();
LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);
newObject->LockingThread = NULL;
newObject->Locks = 0;
#ifdef GDI_DEBUG
newObject->createdfile = file;
newObject->createdline = line;
newObject->lockfile = NULL;
newObject->lockline = 0;
#endif
ObjectBody = GDIHdrToBdy(newObject);
RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
/* FIXME: On Windows the higher 16 bit of the type field don't always match
the type from the handle, it is probably a storage type
(type = pen, storage = brush) */
TypeInfo = (ObjectType & GDI_HANDLE_TYPE_MASK) | (ObjectType >> GDI_ENTRY_UPPER_SHIFT);
FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
if(FreeEntry != NULL)
{
HANDLE PrevProcId;
UINT Index;
/* 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];
LockHandle:
PrevProcId = InterlockedCompareExchangePointer(&Entry->ProcessId, LockedProcessId, 0);
if(PrevProcId == NULL)
{
HGDIOBJ Handle;
ASSERT(Entry->KernelData == NULL);
Entry->KernelData = ObjectBody;
/* 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;
/* unlock the entry */
(void)InterlockedExchangePointer(&Entry->ProcessId, CurrentProcessId);
#ifdef GDI_DEBUG
memset ( GDIHandleAllocator[Index], 0xcd, GDI_STACK_LEVELS * sizeof(ULONG) );
KeRosGetStackFrames ( GDIHandleAllocator[Index], GDI_STACK_LEVELS );
#endif /* GDI_DEBUG */
if(W32Process != NULL)
{
InterlockedIncrement(&W32Process->GDIObjects);
}
Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo << GDI_ENTRY_UPPER_SHIFT));
DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, ObjectBody);
return Handle;
}
else
{
#ifdef GDI_DEBUG
if(++Attempts > 20)
{
DPRINT1("[%d]Waiting on handle in index 0x%x\n", Attempts, Index);
}
#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 GDI_DEBUG
IntDumpHandleTable(HandleTable);
#endif /* GDI_DEBUG */
}
else
{
DPRINT1("Not enough memory to allocate gdi object!\n");
}
}
else
{
DPRINT1("Failed to find lookaside list for object type 0x%x\n", ObjectType);
}
return NULL;
}
/*!
* 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.
*/
BOOL INTERNAL_CALL
#ifdef GDI_DEBUG
GDIOBJ_FreeObjDbg(PGDI_HANDLE_TABLE HandleTable, const char* file, int line, HGDIOBJ hObj, DWORD ExpectedType)
#else /* !GDI_DEBUG */
GDIOBJ_FreeObj(PGDI_HANDLE_TABLE HandleTable, HGDIOBJ hObj, DWORD ExpectedType)
#endif /* GDI_DEBUG */
{
PGDI_TABLE_ENTRY Entry;
PPAGED_LOOKASIDE_LIST LookasideList;
HANDLE ProcessId, LockedProcessId, PrevProcId;
ULONG HandleType, HandleUpper;
BOOL Silent;
#ifdef GDI_DEBUG
ULONG Attempts = 0;
#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);
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
#endif
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);
return FALSE;
}
Entry = GDI_HANDLE_GET_ENTRY(HandleTable, 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(&Entry->ProcessId, LockedProcessId, ProcessId);
if(PrevProcId == ProcessId)
{
if( (Entry->KernelData != NULL) &&
((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) )
{
PGDIOBJHDR GdiHdr;
GdiHdr = GDIBdyToHdr(Entry->KernelData);
if(GdiHdr->Locks == 0)
{
BOOL Ret;
PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
/* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
Entry->Type = (Entry->Type + GDI_ENTRY_REUSE_INC) & GDI_ENTRY_REUSE_MASK;
Entry->KernelData = NULL;
/* unlock the handle slot */
(void)InterlockedExchangePointer(&Entry->ProcessId, NULL);
/* push this entry to the free list */
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), HandleType);
/* Now it's time to free the memory */
LookasideList = FindLookasideList(HandleTable, HandleType);
if(LookasideList != NULL)
{
ExFreeToPagedLookasideList(LookasideList, GdiHdr);
}
return Ret;
}
else
{
/*
* The object is currently locked, so freeing is forbidden!
*/
DPRINT1("GdiHdr->Locks: %d\n", GdiHdr->Locks);
#ifdef GDI_DEBUG
DPRINT1("Locked from: %s:%d\n", GdiHdr->lockfile, GdiHdr->lockline);
#endif
ASSERT(FALSE);
}
}
else
{
LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj");
(void)InterlockedExchangePointer(&Entry->ProcessId, PrevProcId);
}
}
else if(PrevProcId == LockedProcessId)
{
#ifdef GDI_DEBUG
if(++Attempts > 20)
{
DPRINT1("[%d]Waiting on 0x%x\n", Attempts, hObj);
}
#endif
/* 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(((ULONG_PTR)PrevProcId & ~0x1) == 0)
{
DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj);
KeRosDumpStackFrames(NULL, 20);
}
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);
KeRosDumpStackFrames(NULL, 20);
}
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
#endif
}
}
return FALSE;
}
/*!
* Delete GDI object
* \param hObject object handle
* \return if the function fails the returned value is FALSE.
*/
BOOL STDCALL
NtGdiDeleteObject(HGDIOBJ hObject)
{
DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
return NULL != hObject
? GDIOBJ_FreeObj(GdiHandleTable, hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
}
/*!
* 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 (PGDI_HANDLE_TABLE HandleTable, struct _EPROCESS *Process)
{
PGDI_TABLE_ENTRY Entry, End;
PEPROCESS CurrentProcess;
PW32PROCESS W32Process;
HANDLE ProcId;
ULONG Index = RESERVE_ENTRIES_COUNT;
DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
CurrentProcess = PsGetCurrentProcess();
if (CurrentProcess != Process)
{
KeAttachProcess(&Process->Pcb);
}
W32Process = (PW32PROCESS)Process->Win32Process;
ASSERT(W32Process);
if(W32Process->GDIObjects > 0)
{
/* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
we should delete it directly here! */
ProcId = Process->UniqueProcessId;
End = &HandleTable->Entries[GDI_HANDLE_COUNT];
for(Entry = &HandleTable->Entries[RESERVE_ENTRIES_COUNT];
Entry != End;
Entry++, Index++)
{
/* ignore the lock bit */
if((HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId && (Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0)
{
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_FreeObj(HandleTable, ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
W32Process->GDIObjects == 0)
{
/* there are no more gdi handles for this process, bail */
break;
}
}
}
}
if (CurrentProcess != Process)
{
KeDetachProcess();
}
DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
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
#ifdef GDI_DEBUG
GDIOBJ_LockObjDbg (PGDI_HANDLE_TABLE HandleTable, const char* file, int line, HGDIOBJ hObj, DWORD ExpectedType)
#else /* !GDI_DEBUG */
GDIOBJ_LockObj (PGDI_HANDLE_TABLE HandleTable, HGDIOBJ hObj, DWORD ExpectedType)
#endif /* GDI_DEBUG */
{
USHORT HandleIndex;
PGDI_TABLE_ENTRY Entry;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -