📄 gdiobj.c
字号:
PGDI_TABLE_ENTRY Entry;
PPAGED_LOOKASIDE_LIST LookasideList;
HANDLE ProcessId, LockedProcessId, PrevProcId;
LONG ExpectedType;
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 = (ObjectType & GDI_OBJECT_TYPE_SILENT);
ObjectType &= ~GDI_OBJECT_TYPE_SILENT;
ExpectedType = ((ObjectType != GDI_OBJECT_TYPE_DONTCARE) ? ObjectType : 0);
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->Type != 0 && Entry->KernelData != NULL &&
(ExpectedType == 0 || ((Entry->Type << 16) == ExpectedType)) &&
(Entry->Type & (GDI_HANDLE_TYPE_MASK | GDI_HANDLE_REUSE_MASK)) ==
((ULONG_PTR)hObj & (GDI_HANDLE_TYPE_MASK | GDI_HANDLE_REUSE_MASK)))
{
PGDIOBJHDR GdiHdr;
GdiHdr = GDIBdyToHdr(Entry->KernelData);
if(GdiHdr->Locks == 0)
{
BOOL Ret;
PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
ULONG Type = Entry->Type << 16;
/* Clear the type field so when unlocking the handle it gets finally deleted and increment reuse counter */
Entry->Type = ((Entry->Type >> GDI_HANDLE_REUSECNT_SHIFT) + 1) << GDI_HANDLE_REUSECNT_SHIFT;
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), Type);
/* Now it's time to free the memory */
LookasideList = FindLookasideList(HandleTable, Type);
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
{
if((Entry->Type & ~GDI_HANDLE_REUSE_MASK) != 0)
{
DPRINT1("Attempted to delete object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, ObjectType, ExpectedType);
KeRosDumpStackFrames(NULL, 20);
}
else
{
DPRINT1("Attempted to delete object 0x%x which was already deleted!\n", hObj);
KeRosDumpStackFrames(NULL, 20);
}
(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 upper 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 & 0xFFFF0000));
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 ObjectType parameter!
*/
PGDIOBJ INTERNAL_CALL
#ifdef GDI_DEBUG
GDIOBJ_LockObjDbg (PGDI_HANDLE_TABLE HandleTable, const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
#else /* !GDI_DEBUG */
GDIOBJ_LockObj (PGDI_HANDLE_TABLE HandleTable, HGDIOBJ hObj, DWORD ObjectType)
#endif /* GDI_DEBUG */
{
USHORT HandleIndex;
PGDI_TABLE_ENTRY HandleEntry;
HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
PGDIOBJ Object = NULL;
HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
/* Check that the handle index is valid. */
if (HandleIndex >= GDI_HANDLE_COUNT)
return NULL;
HandleEntry = &HandleTable->Entries[HandleIndex];
ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
HandleProcessId = (HANDLE)((ULONG_PTR)HandleEntry->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(&HandleEntry->ProcessId,
LockedProcessId,
HandleProcessId);
if (PrevProcId == HandleProcessId)
{
LONG HandleType = HandleEntry->Type << 16;
/*
* We're locking an object that belongs to our process or it's a
* global object if HandleProcessId is 0 here.
*/
/* FIXME: Check the upper 16-bits of handle number! */
if (HandleType != 0 && HandleEntry->KernelData != NULL &&
(ObjectType == GDI_OBJECT_TYPE_DONTCARE ||
HandleType == ObjectType))
{
PGDIOBJHDR GdiHdr = GDIBdyToHdr(HandleEntry->KernelData);
PETHREAD Thread = PsGetCurrentThread();
if (GdiHdr->Locks == 0)
{
GdiHdr->LockingThread = Thread;
GdiHdr->Locks = 1;
#ifdef GDI_DEBUG
GdiHdr->lockfile = file;
GdiHdr->lockline = line;
#endif
Object = HandleEntry->KernelData;
}
else
{
InterlockedIncrement((PLONG)&GdiHdr->Locks);
if (GdiHdr->LockingThread != Thread)
{
InterlockedDecrement((PLONG)&GdiHdr->Locks);
/* Unlock the handle table entry. */
(void)InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
DelayExecution();
continue;
}
Object = HandleEntry->KernelData;
}
}
else
{
/*
* Debugging code. Report attempts to lock deleted handles and
* locking type mismatches.
*/
if ((HandleType & ~GDI_HANDLE_REUSE_MASK) == 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, HandleType & ~GDI_HANDLE_REUSE_MASK, ObjectType & ~GDI_HANDLE_REUSE_MASK);
KeRosDumpStackFrames(NULL, 20);
}
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
#endif
}
/* Unlock the handle table entry. */
(void)InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
break;
}
else
{
/*
* The handle is currently locked, wait some time and try again.
*/
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 ObjectType parameter!
*/
PGDIOBJ INTERNAL_CALL
#ifdef GDI_DEBUG
GDIOBJ_ShareLockObjDbg (PGDI_HANDLE_TABLE HandleTable, const char* file, int line, HGDIOBJ hObj, DWORD ObjectType)
#else /* !GDI_DEBUG */
GDIOBJ_ShareLockObj (PGDI_HANDLE_TABLE HandleTable, HGDIOBJ hObj, DWORD ObjectType)
#endif /* GDI_DEBUG */
{
USHORT HandleIndex;
PGDI_TABLE_ENTRY HandleEntry;
HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
PGDIOBJ Object = NULL;
HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
/* Check that the handle index is valid. */
if (HandleIndex >= GDI_HANDLE_COUNT)
return NULL;
HandleEntry = &HandleTable->Entries[HandleIndex];
ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
HandleProcessId = (HANDLE)((ULONG_PTR)HandleEntry->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(&HandleEntry->ProcessId,
LockedProcessId,
HandleProcessId);
if (PrevProcId == HandleProcessId)
{
LONG HandleType = HandleEntry->Type << 16;
/*
* We're locking an object that belongs to our process or it's a
* global object if HandleProcessId is 0 here.
*/
/* FIXME: Check the upper 16-bits of handle number! */
if (HandleType != 0 && HandleEntry->KernelData != NULL &&
(ObjectType == GDI_OBJECT_TYPE_DONTCARE ||
HandleType == ObjectType))
{
PGDIOBJHDR GdiHdr = GDIBdyToHdr(HandleEntry->KernelData);
#ifdef GDI_DEBUG
if (InterlockedIncrement((PLONG)&GdiHdr->Locks) == 1)
{
GdiHdr->lockfile = file;
GdiHdr->lockline = line;
}
#else
InterlockedIncrement((PLONG)&GdiHdr->Locks);
#endif
Object = HandleEntry->KernelData;
}
else
{
/*
* Debugging code. Report attempts to lock deleted handles and
* locking type mismatches.
*/
if ((HandleType & ~GDI_HANDLE_REUSE_MASK) == 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, HandleType & ~GDI_HANDLE_REUSE_MASK, ObjectType & ~GDI_HANDLE_REUSE_MASK);
KeRosDumpStackFrames(NULL, 20);
}
#ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line);
#endif
}
/* Unlock the handle table entry. */
(void)InterlockedExchangePointer(&HandleEntry->ProcessId, PrevProcId);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -