mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
1657 lines
46 KiB
C
1657 lines
46 KiB
C
/*
|
|
* PROJECT: ReactOS win32 kernel mode subsystem
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: win32ss/gdi/ntgdi/gdiobj.c
|
|
* PURPOSE: General GDI object manipulation routines
|
|
* PROGRAMMERS: Timo Kreuzer
|
|
*/
|
|
|
|
/*
|
|
* If you want to understand this code, you need to start thinking in portals.
|
|
* - gpaulRefCount is a global pointer to an allocated array of ULONG values,
|
|
* one for each handle. Bits 0 - 22 contain a reference count for the handle.
|
|
* It gets increased for each handle lock / reference. Bit 23 contains a valid
|
|
* bit. If this bit is 0, the handle got deleted and will be pushed to the free
|
|
* list, once all references are gone. Bits 24 - 31 contain the reuse value of
|
|
* the handle, which allows to check if the entry was changed before atomically
|
|
* exchanging the reference count.
|
|
* - Objects can exist with or without a handle
|
|
* - Objects with a handle can be locked either exclusively or shared.
|
|
* Both locks increase the handle reference count in gpaulRefCount.
|
|
* Exclusive locks also increase the BASEOBJECT's cExclusiveLock field
|
|
* and the first lock (can be acquired recursively) acquires a pushlock
|
|
* that is also stored in the BASEOBJECT.
|
|
* - Objects without a handle cannot have exclusive locks. Their reference
|
|
* count is tracked in the BASEOBJECT's ulShareCount field.
|
|
* - An object that is inserted in the handle table automatically has an
|
|
* exclusive lock. For objects that are "shared objects" (BRUSH, PALETTE, ...)
|
|
* this is the only way it can ever be exclusively locked. It prevents the
|
|
* object from being locked by another thread. A shared lock will simply fail,
|
|
* while an exclusive lock will succeed after the object was unlocked.
|
|
*
|
|
* Ownership:
|
|
*
|
|
* Owner: POWNED PUBLIC NONE spec
|
|
* ---------------------------------------------------
|
|
* LockForRead + + - PUBLIC
|
|
* LockForWrite + - - POWNED
|
|
* LockAny + + + NONE
|
|
* NtGdiDeleteObjectApp + - - PUBLIC
|
|
* GreDeleteObject + + + NONE
|
|
* GreSetOwner(POWNED) - - + -
|
|
* GreSetOwner(PUBLIC) + - + -
|
|
* GreSetOwner(NONE) + - - -
|
|
*
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <win32k.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
FORCEINLINE
|
|
ULONG
|
|
InterlockedReadUlong(
|
|
_In_ _Interlocked_operand_ ULONG volatile *Source)
|
|
{
|
|
return *Source;
|
|
}
|
|
|
|
FORCEINLINE
|
|
void
|
|
INCREASE_THREAD_LOCK_COUNT(
|
|
_In_ HANDLE hobj)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
DBG_UNREFERENCED_PARAMETER(hobj);
|
|
if (pti)
|
|
{
|
|
#if DBG
|
|
pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]++;
|
|
#endif
|
|
pti->cExclusiveLocks++;
|
|
}
|
|
}
|
|
|
|
FORCEINLINE
|
|
void
|
|
DECREASE_THREAD_LOCK_COUNT(
|
|
_In_ HANDLE hobj)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
DBG_UNREFERENCED_PARAMETER(hobj);
|
|
if (pti)
|
|
{
|
|
#if DBG
|
|
pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]--;
|
|
#endif
|
|
pti->cExclusiveLocks--;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
ASSERT_LOCK_ORDER(
|
|
_In_ UCHAR objt)
|
|
{
|
|
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
|
|
ULONG i;
|
|
|
|
if (pti)
|
|
{
|
|
/* Ensure correct locking order! */
|
|
for (i = objt + 1; i < GDIObjTypeTotal; i++)
|
|
{
|
|
NT_ASSERT(pti->acExclusiveLockCount[i] == 0);
|
|
}
|
|
}
|
|
}
|
|
#define ASSERT_SHARED_OBJECT_TYPE(objt) \
|
|
ASSERT((objt) == GDIObjType_SURF_TYPE || \
|
|
(objt) == GDIObjType_PAL_TYPE || \
|
|
(objt) == GDIObjType_LFONT_TYPE || \
|
|
(objt) == GDIObjType_PATH_TYPE || \
|
|
(objt) == GDIObjType_BRUSH_TYPE)
|
|
#define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt) \
|
|
ASSERT((objt) == GDIObjType_DC_TYPE || \
|
|
(objt) == GDIObjType_RGN_TYPE || \
|
|
(objt) == GDIObjType_UMPD_TYPE || \
|
|
(objt) == GDIObjType_META_TYPE)
|
|
#define ASSERT_TRYLOCK_OBJECT_TYPE(objt) \
|
|
ASSERT((objt) == GDIObjType_DRVOBJ_TYPE)
|
|
#else
|
|
#define ASSERT_LOCK_ORDER(hobj)
|
|
#define ASSERT_SHARED_OBJECT_TYPE(objt)
|
|
#define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
|
|
#define ASSERT_TRYLOCK_OBJECT_TYPE(objt)
|
|
#endif
|
|
|
|
#if defined(_M_IX86) || defined(_M_AMD64)
|
|
#define InterlockedOr16 _InterlockedOr16
|
|
#endif
|
|
|
|
#define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
|
|
|
|
enum
|
|
{
|
|
REF_MASK_REUSE = 0xff000000,
|
|
REF_INC_REUSE = 0x01000000,
|
|
REF_MASK_VALID = 0x00800000,
|
|
REF_MASK_COUNT = 0x007fffff,
|
|
REF_MASK_INUSE = 0x00ffffff,
|
|
};
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
/* Per session handle table globals */
|
|
static PVOID gpvGdiHdlTblSection = NULL;
|
|
PENTRY gpentHmgr;
|
|
PULONG gpaulRefCount;
|
|
volatile ULONG gulFirstFree;
|
|
volatile ULONG gulFirstUnused;
|
|
static PPAGED_LOOKASIDE_LIST gpaLookasideList;
|
|
|
|
static VOID NTAPI GDIOBJ_vCleanup(PVOID ObjectBody);
|
|
|
|
static const
|
|
GDICLEANUPPROC
|
|
apfnCleanup[] =
|
|
{
|
|
NULL, /* 00 GDIObjType_DEF_TYPE */
|
|
DC_vCleanup, /* 01 GDIObjType_DC_TYPE */
|
|
NULL, /* 02 GDIObjType_UNUSED1_TYPE */
|
|
NULL, /* 03 GDIObjType_UNUSED2_TYPE */
|
|
REGION_vCleanup, /* 04 GDIObjType_RGN_TYPE */
|
|
SURFACE_vCleanup, /* 05 GDIObjType_SURF_TYPE */
|
|
GDIOBJ_vCleanup, /* 06 GDIObjType_CLIENTOBJ_TYPE */
|
|
GDIOBJ_vCleanup, /* 07 GDIObjType_PATH_TYPE */
|
|
PALETTE_vCleanup, /* 08 GDIObjType_PAL_TYPE */
|
|
GDIOBJ_vCleanup, /* 09 GDIObjType_ICMLCS_TYPE */
|
|
GDIOBJ_vCleanup, /* 0a GDIObjType_LFONT_TYPE */
|
|
NULL, /* 0b GDIObjType_RFONT_TYPE, unused */
|
|
NULL, /* 0c GDIObjType_PFE_TYPE, unused */
|
|
NULL, /* 0d GDIObjType_PFT_TYPE, unused */
|
|
GDIOBJ_vCleanup, /* 0e GDIObjType_ICMCXF_TYPE */
|
|
NULL, /* 0f GDIObjType_SPRITE_TYPE, unused */
|
|
NULL, /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
|
|
NULL, /* 11 GDIObjType_UMPD_TYPE, unused */
|
|
NULL, /* 12 GDIObjType_UNUSED4_TYPE */
|
|
NULL, /* 13 GDIObjType_SPACE_TYPE, unused */
|
|
NULL, /* 14 GDIObjType_UNUSED5_TYPE */
|
|
GDIOBJ_vCleanup, /* 15 GDIObjType_META_TYPE */
|
|
NULL, /* 16 GDIObjType_EFSTATE_TYPE, unused */
|
|
NULL, /* 17 GDIObjType_BMFD_TYPE, unused */
|
|
NULL, /* 18 GDIObjType_VTFD_TYPE, unused */
|
|
NULL, /* 19 GDIObjType_TTFD_TYPE, unused */
|
|
NULL, /* 1a GDIObjType_RC_TYPE, unused */
|
|
NULL, /* 1b GDIObjType_TEMP_TYPE, unused */
|
|
DRIVEROBJ_vCleanup,/* 1c GDIObjType_DRVOBJ_TYPE */
|
|
NULL, /* 1d GDIObjType_DCIOBJ_TYPE, unused */
|
|
NULL, /* 1e GDIObjType_SPOOL_TYPE, unused */
|
|
NULL, /* 1f reserved entry */
|
|
};
|
|
|
|
static const
|
|
GDIOBJDELETEPROC
|
|
apfnDelete[] =
|
|
{
|
|
NULL, /* 00 GDIObjType_DEF_TYPE */
|
|
NULL, /* 01 GDIObjType_DC_TYPE */
|
|
NULL, /* 02 GDIObjType_UNUSED1_TYPE */
|
|
NULL, /* 03 GDIObjType_UNUSED2_TYPE */
|
|
NULL, /* 04 GDIObjType_RGN_TYPE */
|
|
NULL, /* 05 GDIObjType_SURF_TYPE */
|
|
NULL, /* 06 GDIObjType_CLIENTOBJ_TYPE */
|
|
NULL, /* 07 GDIObjType_PATH_TYPE */
|
|
NULL, /* 08 GDIObjType_PAL_TYPE */
|
|
NULL, /* 09 GDIObjType_ICMLCS_TYPE */
|
|
NULL, /* 0a GDIObjType_LFONT_TYPE */
|
|
NULL, /* 0b GDIObjType_RFONT_TYPE, unused */
|
|
NULL, /* 0c GDIObjType_PFE_TYPE, unused */
|
|
NULL, /* 0d GDIObjType_PFT_TYPE, unused */
|
|
NULL, /* 0e GDIObjType_ICMCXF_TYPE */
|
|
NULL, /* 0f GDIObjType_SPRITE_TYPE, unused */
|
|
BRUSH_vDeleteObject, /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
|
|
NULL, /* 11 GDIObjType_UMPD_TYPE, unused */
|
|
NULL, /* 12 GDIObjType_UNUSED4_TYPE */
|
|
NULL, /* 13 GDIObjType_SPACE_TYPE, unused */
|
|
NULL, /* 14 GDIObjType_UNUSED5_TYPE */
|
|
NULL, /* 15 GDIObjType_META_TYPE, unused */
|
|
NULL, /* 16 GDIObjType_EFSTATE_TYPE, unused */
|
|
NULL, /* 17 GDIObjType_BMFD_TYPE, unused */
|
|
NULL, /* 18 GDIObjType_VTFD_TYPE, unused */
|
|
NULL, /* 19 GDIObjType_TTFD_TYPE, unused */
|
|
NULL, /* 1a GDIObjType_RC_TYPE, unused */
|
|
NULL, /* 1b GDIObjType_TEMP_TYPE, unused */
|
|
NULL, /* 1c GDIObjType_DRVOBJ_TYPE */
|
|
NULL, /* 1d GDIObjType_DCIOBJ_TYPE, unused */
|
|
NULL, /* 1e GDIObjType_SPOOL_TYPE, unused */
|
|
NULL, /* 1f reserved entry */
|
|
};
|
|
|
|
/* INTERNAL FUNCTIONS ********************************************************/
|
|
|
|
static
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vCleanup(PVOID ObjectBody)
|
|
{
|
|
/* Nothing to do */
|
|
}
|
|
|
|
static
|
|
VOID
|
|
InitLookasideList(UCHAR objt, ULONG cjSize)
|
|
{
|
|
ExInitializePagedLookasideList(&gpaLookasideList[objt],
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
cjSize,
|
|
GDITAG_HMGR_LOOKASIDE_START + (objt << 24),
|
|
0);
|
|
}
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
InitGdiHandleTable(void)
|
|
{
|
|
NTSTATUS status;
|
|
LARGE_INTEGER liSize;
|
|
PVOID pvSection;
|
|
SIZE_T cjViewSize = 0;
|
|
|
|
/* Create a section for the shared handle table */
|
|
liSize.QuadPart = sizeof(GDI_HANDLE_TABLE); // GDI_HANDLE_COUNT * sizeof(ENTRY);
|
|
status = MmCreateSection(&gpvGdiHdlTblSection,
|
|
SECTION_ALL_ACCESS,
|
|
NULL,
|
|
&liSize,
|
|
PAGE_READWRITE,
|
|
SEC_COMMIT | 0x1,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
|
|
return status;
|
|
}
|
|
|
|
/* Map the section in session space */
|
|
status = MmMapViewInSessionSpace(gpvGdiHdlTblSection,
|
|
(PVOID*)&gpentHmgr,
|
|
&cjViewSize);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DPRINT1("INITGDI: Failed to map handle table section\n");
|
|
ObDereferenceObject(gpvGdiHdlTblSection);
|
|
return status;
|
|
}
|
|
|
|
/* Allocate memory for the reference counter table */
|
|
gpaulRefCount = EngAllocSectionMem(&pvSection,
|
|
FL_ZERO_MEMORY,
|
|
GDI_HANDLE_COUNT * sizeof(ULONG),
|
|
'frHG');
|
|
if (!gpaulRefCount)
|
|
{
|
|
DPRINT1("INITGDI: Failed to allocate reference table.\n");
|
|
ObDereferenceObject(gpvGdiHdlTblSection);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
gulFirstFree = 0;
|
|
gulFirstUnused = RESERVE_ENTRIES_COUNT;
|
|
|
|
GdiHandleTable = (PVOID)gpentHmgr;
|
|
|
|
/* Initialize the lookaside lists */
|
|
gpaLookasideList = ExAllocatePoolWithTag(NonPagedPool,
|
|
GDIObjTypeTotal * sizeof(PAGED_LOOKASIDE_LIST),
|
|
TAG_GDIHNDTBLE);
|
|
if(!gpaLookasideList)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
InitLookasideList(GDIObjType_DC_TYPE, sizeof(DC));
|
|
InitLookasideList(GDIObjType_RGN_TYPE, sizeof(REGION));
|
|
InitLookasideList(GDIObjType_SURF_TYPE, sizeof(SURFACE));
|
|
InitLookasideList(GDIObjType_CLIENTOBJ_TYPE, sizeof(CLIENTOBJ));
|
|
InitLookasideList(GDIObjType_PATH_TYPE, sizeof(PATH));
|
|
InitLookasideList(GDIObjType_PAL_TYPE, sizeof(PALETTE));
|
|
InitLookasideList(GDIObjType_ICMLCS_TYPE, sizeof(COLORSPACE));
|
|
InitLookasideList(GDIObjType_LFONT_TYPE, sizeof(TEXTOBJ));
|
|
InitLookasideList(GDIObjType_BRUSH_TYPE, sizeof(BRUSH));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
IncrementCurrentProcessGdiHandleCount(void)
|
|
{
|
|
PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
|
|
if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
|
|
}
|
|
|
|
FORCEINLINE
|
|
VOID
|
|
DecrementCurrentProcessGdiHandleCount(void)
|
|
{
|
|
PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
|
|
if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
|
|
}
|
|
|
|
static inline
|
|
VOID
|
|
IncrementGdiHandleCount(ULONG ulProcessId)
|
|
{
|
|
PEPROCESS pep;
|
|
PPROCESSINFO ppi;
|
|
NTSTATUS Status;
|
|
|
|
Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
|
|
NT_ASSERT(NT_SUCCESS(Status));
|
|
__analysis_assume(NT_SUCCESS(Status));
|
|
|
|
ppi = PsGetProcessWin32Process(pep);
|
|
if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
|
|
if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
|
|
}
|
|
|
|
static inline
|
|
VOID
|
|
DecrementGdiHandleCount(ULONG ulProcessId)
|
|
{
|
|
PEPROCESS pep;
|
|
PPROCESSINFO ppi;
|
|
NTSTATUS Status;
|
|
|
|
Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
|
|
NT_ASSERT(NT_SUCCESS(Status));
|
|
__analysis_assume(NT_SUCCESS(Status));
|
|
|
|
ppi = PsGetProcessWin32Process(pep);
|
|
if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
|
|
if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
|
|
}
|
|
|
|
static
|
|
PENTRY
|
|
ENTRY_pentPopFreeEntry(VOID)
|
|
{
|
|
ULONG iFirst, iNext, iPrev;
|
|
PENTRY pentFree;
|
|
|
|
DPRINT("Enter InterLockedPopFreeEntry\n");
|
|
|
|
do
|
|
{
|
|
/* Get the index and sequence number of the first free entry */
|
|
iFirst = InterlockedReadUlong(&gulFirstFree);
|
|
|
|
/* Check if we have a free entry */
|
|
if (!(iFirst & GDI_HANDLE_INDEX_MASK))
|
|
{
|
|
/* Increment FirstUnused and get the new index */
|
|
iFirst = InterlockedIncrement((LONG*)&gulFirstUnused) - 1;
|
|
|
|
/* Check if we have unused entries left */
|
|
if (iFirst >= GDI_HANDLE_COUNT)
|
|
{
|
|
DPRINT1("No more GDI handles left!\n");
|
|
#if DBG_ENABLE_GDIOBJ_BACKTRACES
|
|
DbgDumpGdiHandleTableWithBT();
|
|
#endif
|
|
InterlockedDecrement((LONG*)&gulFirstUnused);
|
|
return 0;
|
|
}
|
|
|
|
/* Return the old entry */
|
|
return &gpentHmgr[iFirst];
|
|
}
|
|
|
|
/* Get a pointer to the first free entry */
|
|
pentFree = &gpentHmgr[iFirst & GDI_HANDLE_INDEX_MASK];
|
|
|
|
/* Create a new value with an increased sequence number */
|
|
iNext = GDI_HANDLE_GET_INDEX(pentFree->einfo.hFree);
|
|
iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
|
|
|
|
/* Try to exchange the FirstFree value */
|
|
iPrev = InterlockedCompareExchange((LONG*)&gulFirstFree,
|
|
iNext,
|
|
iFirst);
|
|
}
|
|
while (iPrev != iFirst);
|
|
|
|
/* Sanity check: is entry really free? */
|
|
ASSERT(((ULONG_PTR)pentFree->einfo.pobj & ~GDI_HANDLE_INDEX_MASK) == 0);
|
|
|
|
return pentFree;
|
|
}
|
|
|
|
/* Pushes an entry of the handle table to the free list,
|
|
The entry must not have any references left */
|
|
static
|
|
VOID
|
|
ENTRY_vPushFreeEntry(PENTRY pentFree)
|
|
{
|
|
ULONG iToFree, iFirst, iPrev, idxToFree;
|
|
|
|
DPRINT("Enter ENTRY_vPushFreeEntry\n");
|
|
|
|
idxToFree = pentFree - gpentHmgr;
|
|
ASSERT((gpaulRefCount[idxToFree] & REF_MASK_INUSE) == 0);
|
|
|
|
/* Initialize entry */
|
|
pentFree->Objt = GDIObjType_DEF_TYPE;
|
|
pentFree->ObjectOwner.ulObj = 0;
|
|
pentFree->pUser = NULL;
|
|
|
|
/* Increase reuse counter in entry and reference counter */
|
|
InterlockedExchangeAdd((LONG*)&gpaulRefCount[idxToFree], REF_INC_REUSE);
|
|
pentFree->FullUnique += 0x0100;
|
|
|
|
do
|
|
{
|
|
/* Get the current first free index and sequence number */
|
|
iFirst = InterlockedReadUlong(&gulFirstFree);
|
|
|
|
/* Set the einfo.pobj member to the index of the first free entry */
|
|
pentFree->einfo.pobj = UlongToPtr(iFirst & GDI_HANDLE_INDEX_MASK);
|
|
|
|
/* 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*)&gulFirstFree,
|
|
iToFree,
|
|
iFirst);
|
|
}
|
|
while (iPrev != iFirst);
|
|
}
|
|
|
|
static
|
|
PENTRY
|
|
ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj, FLONG fl)
|
|
{
|
|
ULONG ulIndex, cNewRefs, cOldRefs;
|
|
PENTRY pentry;
|
|
|
|
/* Get the handle index and check if its too big */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(hobj);
|
|
|
|
/* Get pointer to the entry */
|
|
pentry = &gpentHmgr[ulIndex];
|
|
|
|
/* Get the current reference count */
|
|
cOldRefs = gpaulRefCount[ulIndex];
|
|
|
|
do
|
|
{
|
|
/* Check if the slot is deleted */
|
|
if ((cOldRefs & REF_MASK_VALID) == 0)
|
|
{
|
|
DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs, hobj);
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if the unique value matches */
|
|
if (pentry->FullUnique != (USHORT)((ULONG_PTR)hobj >> 16))
|
|
{
|
|
DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
|
|
(USHORT)((ULONG_PTR)hobj >> 16), pentry->FullUnique);
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if the object owner is this process or public */
|
|
if (!(fl & GDIOBJFLAG_IGNOREPID) &&
|
|
pentry->ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
|
|
pentry->ObjectOwner.ulObj != PtrToUlong(PsGetCurrentProcessId()))
|
|
{
|
|
DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
|
|
hobj, pentry, pentry->ObjectOwner.ulObj);
|
|
return NULL;
|
|
}
|
|
|
|
/* Try to atomically increment the reference count */
|
|
cNewRefs = cOldRefs + 1;
|
|
cOldRefs = InterlockedCompareExchange((PLONG)&gpaulRefCount[ulIndex],
|
|
cNewRefs,
|
|
cOldRefs);
|
|
}
|
|
while (cNewRefs != cOldRefs + 1);
|
|
|
|
/* Integrity checks */
|
|
ASSERT((pentry->FullUnique & 0x1f) == pentry->Objt);
|
|
ASSERT(pentry->einfo.pobj != NULL);
|
|
|
|
/* Check if lower 32 bits match, the upper 32 bits are ignored */
|
|
ASSERT(pentry->einfo.pobj->hHmgr == UlongToPtr(PtrToUlong(hobj)));
|
|
|
|
return pentry;
|
|
}
|
|
|
|
static
|
|
HGDIOBJ
|
|
ENTRY_hInsertObject(PENTRY pentry, POBJ pobj, UCHAR objt, ULONG ulOwner)
|
|
{
|
|
ULONG ulIndex;
|
|
|
|
/* Calculate the handle index */
|
|
ulIndex = pentry - gpentHmgr;
|
|
|
|
/* Update the fields in the ENTRY */
|
|
pentry->einfo.pobj = pobj;
|
|
pentry->Objt = objt & 0x1f;
|
|
pentry->FullUnique = (pentry->FullUnique & 0xff00) | objt;
|
|
pentry->ObjectOwner.ulObj = ulOwner;
|
|
|
|
/* Make the handle valid with 1 reference */
|
|
ASSERT((gpaulRefCount[ulIndex] & REF_MASK_INUSE) == 0);
|
|
InterlockedOr((LONG*)&gpaulRefCount[ulIndex], REF_MASK_VALID | 1);
|
|
|
|
/* Return the handle */
|
|
return (HGDIOBJ)(((ULONG_PTR)pentry->FullUnique << 16) | ulIndex);
|
|
}
|
|
|
|
POBJ
|
|
NTAPI
|
|
GDIOBJ_AllocateObject(UCHAR objt, ULONG cjSize, FLONG fl)
|
|
{
|
|
POBJ pobj;
|
|
|
|
if (fl & BASEFLAG_LOOKASIDE)
|
|
{
|
|
/* Allocate the object from a lookaside list */
|
|
pobj = ExAllocateFromPagedLookasideList(&gpaLookasideList[objt & 0x1f]);
|
|
}
|
|
else
|
|
{
|
|
/* Allocate the object from paged pool */
|
|
pobj = ExAllocatePoolWithTag(PagedPool, cjSize, GDIOBJ_POOL_TAG(objt));
|
|
}
|
|
|
|
if (!pobj) return NULL;
|
|
|
|
/* Initialize the object */
|
|
RtlZeroMemory(pobj, cjSize);
|
|
pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)objt << 16);
|
|
pobj->cExclusiveLock = 0;
|
|
pobj->ulShareCount = 1;
|
|
pobj->BaseFlags = fl & 0xffff;
|
|
DBG_INITLOG(&pobj->slhLog);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_ALLOCATE, 0);
|
|
#if DBG_ENABLE_GDIOBJ_BACKTRACES
|
|
DbgCaptureStackBackTace(pobj->apvBackTrace, 1, GDI_OBJECT_STACK_LEVELS);
|
|
#endif /* GDI_DEBUG */
|
|
|
|
return pobj;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vFreeObject(POBJ pobj)
|
|
{
|
|
UCHAR objt;
|
|
|
|
DBG_CLEANUP_EVENT_LIST(&pobj->slhLog);
|
|
|
|
/* Get the object type */
|
|
objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0x1f;
|
|
|
|
/* Check if we have a delete procedure (for C++ based objects) */
|
|
if (apfnDelete[objt] != NULL)
|
|
{
|
|
/* Invoke the delete procedure */
|
|
apfnDelete[objt](pobj);
|
|
}
|
|
else
|
|
{
|
|
/* Call the cleanup procedure */
|
|
NT_ASSERT(apfnCleanup[objt]);
|
|
apfnCleanup[objt](pobj);
|
|
|
|
/* Check if the object is allocated from a lookaside list */
|
|
if (pobj->BaseFlags & BASEFLAG_LOOKASIDE)
|
|
{
|
|
ExFreeToPagedLookasideList(&gpaLookasideList[objt], pobj);
|
|
}
|
|
else
|
|
{
|
|
ExFreePoolWithTag(pobj, GDIOBJ_POOL_TAG(objt));
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vDereferenceObject(POBJ pobj)
|
|
{
|
|
ULONG cRefs, ulIndex;
|
|
|
|
/* Calculate the index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
|
|
/* Check if the object has a handle */
|
|
if (ulIndex)
|
|
{
|
|
/* Decrement reference count */
|
|
if ((gpaulRefCount[ulIndex] & REF_MASK_COUNT) == 0)
|
|
{
|
|
DBG_DUMP_EVENT_LIST(&pobj->slhLog);
|
|
}
|
|
ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
|
|
cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
|
|
|
|
/* Check if we reached 0 and handle bit is not set */
|
|
if ((cRefs & REF_MASK_INUSE) == 0)
|
|
{
|
|
/* Make sure it's ok to delete the object */
|
|
ASSERT(pobj->BaseFlags & BASEFLAG_READY_TO_DIE);
|
|
|
|
/* Check if the handle was process owned */
|
|
if (gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
|
|
gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_NONE)
|
|
{
|
|
/* Decrement the process handle count */
|
|
ASSERT(gpentHmgr[ulIndex].ObjectOwner.ulObj ==
|
|
HandleToUlong(PsGetCurrentProcessId()));
|
|
DecrementCurrentProcessGdiHandleCount();
|
|
}
|
|
|
|
/* Push entry to the free list */
|
|
ENTRY_vPushFreeEntry(&gpentHmgr[ulIndex]);
|
|
|
|
/* Free the object */
|
|
GDIOBJ_vFreeObject(pobj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Decrement the objects reference count */
|
|
ASSERT(pobj->ulShareCount > 0);
|
|
cRefs = InterlockedDecrement((LONG*)&pobj->ulShareCount);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
|
|
|
|
/* Check if we reached 0 */
|
|
if (cRefs == 0)
|
|
{
|
|
/* Free the object */
|
|
GDIOBJ_vFreeObject(pobj);
|
|
}
|
|
}
|
|
}
|
|
|
|
POBJ
|
|
NTAPI
|
|
GDIOBJ_ReferenceObjectByHandle(
|
|
HGDIOBJ hobj,
|
|
UCHAR objt)
|
|
{
|
|
PENTRY pentry;
|
|
POBJ pobj;
|
|
|
|
/* Check if the handle type matches */
|
|
ASSERT_SHARED_OBJECT_TYPE(objt);
|
|
if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
|
|
{
|
|
DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj, objt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the pointer to the BASEOBJECT */
|
|
pobj = pentry->einfo.pobj;
|
|
|
|
/* Check if the object is exclusively locked */
|
|
if (pobj->cExclusiveLock != 0)
|
|
{
|
|
DPRINT1("GDIOBJ: Cannot reference object %p with exclusive lock.\n", hobj);
|
|
GDIOBJ_vDereferenceObject(pobj);
|
|
DBG_DUMP_EVENT_LIST(&pobj->slhLog);
|
|
return NULL;
|
|
}
|
|
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, gpaulRefCount[pentry - gpentHmgr]);
|
|
|
|
/* All is well, return the object */
|
|
return pobj;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vReferenceObjectByPointer(POBJ pobj)
|
|
{
|
|
ULONG cRefs;
|
|
|
|
/* Check if the object has a handle */
|
|
if (GDI_HANDLE_GET_INDEX(pobj->hHmgr))
|
|
{
|
|
/* Increase the handle's reference count */
|
|
ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
|
|
cRefs = InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
|
|
}
|
|
else
|
|
{
|
|
/* Increase the object's reference count */
|
|
cRefs = InterlockedIncrement((LONG*)&pobj->ulShareCount);
|
|
}
|
|
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, cRefs);
|
|
}
|
|
|
|
PGDIOBJ
|
|
NTAPI
|
|
GDIOBJ_TryLockObject(
|
|
HGDIOBJ hobj,
|
|
UCHAR objt)
|
|
{
|
|
PENTRY pentry;
|
|
POBJ pobj;
|
|
DWORD dwThreadId;
|
|
|
|
/* Check if the handle type matches */
|
|
ASSERT_TRYLOCK_OBJECT_TYPE(objt);
|
|
if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
|
|
{
|
|
DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Make sure lock order is correct */
|
|
ASSERT_LOCK_ORDER(objt);
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the pointer to the BASEOBJECT */
|
|
pobj = pentry->einfo.pobj;
|
|
|
|
/* Check if we already own the lock */
|
|
dwThreadId = PtrToUlong(PsGetCurrentThreadId());
|
|
if (pobj->dwThreadId != dwThreadId)
|
|
{
|
|
/* Disable APCs and try acquiring the push lock */
|
|
KeEnterCriticalRegion();
|
|
if(!ExTryAcquirePushLockExclusive(&pobj->pushlock))
|
|
{
|
|
ULONG cRefs, ulIndex;
|
|
/* Already owned. Clean up and leave. */
|
|
KeLeaveCriticalRegion();
|
|
|
|
/* Calculate the index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
|
|
/* Decrement reference count */
|
|
ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
|
|
cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
|
|
ASSERT(cRefs & REF_MASK_VALID);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Set us as lock owner */
|
|
ASSERT(pobj->dwThreadId == 0);
|
|
pobj->dwThreadId = dwThreadId;
|
|
}
|
|
|
|
/* Increase lock count */
|
|
pobj->cExclusiveLock++;
|
|
INCREASE_THREAD_LOCK_COUNT(hobj);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
|
|
|
|
/* Return the object */
|
|
return pobj;
|
|
}
|
|
|
|
PGDIOBJ
|
|
NTAPI
|
|
GDIOBJ_LockObject(
|
|
HGDIOBJ hobj,
|
|
UCHAR objt)
|
|
{
|
|
PENTRY pentry;
|
|
POBJ pobj;
|
|
DWORD dwThreadId;
|
|
|
|
/* Check if the handle type matches */
|
|
ASSERT_EXCLUSIVE_OBJECT_TYPE(objt);
|
|
if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
|
|
{
|
|
DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Make sure lock order is correct */
|
|
ASSERT_LOCK_ORDER(objt);
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the pointer to the BASEOBJECT */
|
|
pobj = pentry->einfo.pobj;
|
|
|
|
/* Check if we already own the lock */
|
|
dwThreadId = PtrToUlong(PsGetCurrentThreadId());
|
|
if (pobj->dwThreadId != dwThreadId)
|
|
{
|
|
/* Disable APCs and acquire the push lock */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockExclusive(&pobj->pushlock);
|
|
|
|
/* Set us as lock owner */
|
|
ASSERT(pobj->dwThreadId == 0);
|
|
pobj->dwThreadId = dwThreadId;
|
|
}
|
|
|
|
/* Increase lock count */
|
|
pobj->cExclusiveLock++;
|
|
INCREASE_THREAD_LOCK_COUNT(hobj);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
|
|
|
|
/* Return the object */
|
|
return pobj;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vUnlockObject(POBJ pobj)
|
|
{
|
|
ULONG cRefs, ulIndex;
|
|
ASSERT(pobj->cExclusiveLock > 0);
|
|
|
|
/* Decrease lock count */
|
|
pobj->cExclusiveLock--;
|
|
DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_UNLOCK, 0);
|
|
|
|
/* Check if this was the last lock */
|
|
if (pobj->cExclusiveLock == 0)
|
|
{
|
|
/* Reset lock owner */
|
|
pobj->dwThreadId = 0;
|
|
|
|
/* Release the pushlock and reenable APCs */
|
|
ExReleasePushLockExclusive(&pobj->pushlock);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
/* Calculate the index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
|
|
/* Decrement reference count */
|
|
ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
|
|
cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
|
|
ASSERT(cRefs & REF_MASK_VALID);
|
|
}
|
|
|
|
HGDIOBJ
|
|
NTAPI
|
|
GDIOBJ_hInsertObject(
|
|
POBJ pobj,
|
|
ULONG ulOwner)
|
|
{
|
|
PENTRY pentry;
|
|
UCHAR objt;
|
|
|
|
/* Must have no handle and only one reference */
|
|
ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr) == 0);
|
|
ASSERT(pobj->cExclusiveLock == 0);
|
|
ASSERT(pobj->ulShareCount == 1);
|
|
|
|
/* Get a free handle entry */
|
|
pentry = ENTRY_pentPopFreeEntry();
|
|
if (!pentry)
|
|
{
|
|
DPRINT1("GDIOBJ: Could not get a free entry.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Make the object exclusively locked */
|
|
ExInitializePushLock(&pobj->pushlock);
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockExclusive(&pobj->pushlock);
|
|
pobj->cExclusiveLock = 1;
|
|
pobj->dwThreadId = PtrToUlong(PsGetCurrentThreadId());
|
|
INCREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
|
|
|
|
/* Get object type from the hHmgr field */
|
|
objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0xff;
|
|
ASSERT(objt != GDIObjType_DEF_TYPE);
|
|
|
|
/* Check if current process is requested owner */
|
|
if (ulOwner == GDI_OBJ_HMGR_POWNED)
|
|
{
|
|
/* Increment the process handle count */
|
|
IncrementCurrentProcessGdiHandleCount();
|
|
|
|
/* Use Process id */
|
|
ulOwner = HandleToUlong(PsGetCurrentProcessId());
|
|
}
|
|
|
|
/* Insert the object into the handle table */
|
|
pobj->hHmgr = ENTRY_hInsertObject(pentry, pobj, objt, ulOwner);
|
|
|
|
/* Return the handle */
|
|
DPRINT("GDIOBJ: Created handle: %p\n", pobj->hHmgr);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_CREATE_HANDLE, 0);
|
|
return pobj->hHmgr;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vSetObjectOwner(
|
|
POBJ pobj,
|
|
ULONG ulNewOwner)
|
|
{
|
|
PENTRY pentry;
|
|
ULONG ulOldOwner;
|
|
|
|
/* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
|
|
if (GDI_HANDLE_IS_STOCKOBJ(pobj->hHmgr))
|
|
{
|
|
DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj->hHmgr, ulNewOwner);
|
|
return;
|
|
}
|
|
|
|
/* Get the handle entry */
|
|
NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr));
|
|
pentry = &gpentHmgr[GDI_HANDLE_GET_INDEX(pobj->hHmgr)];
|
|
|
|
/* Check if the new owner is the same as the old one */
|
|
ulOldOwner = pentry->ObjectOwner.ulObj;
|
|
if (ulOldOwner == ulNewOwner)
|
|
{
|
|
/* Nothing to do */
|
|
return;
|
|
}
|
|
|
|
/* Is the current process requested? */
|
|
if (ulNewOwner == GDI_OBJ_HMGR_POWNED)
|
|
{
|
|
/* Use process id */
|
|
ulNewOwner = HandleToUlong(PsGetCurrentProcessId());
|
|
}
|
|
|
|
// HACK
|
|
if (ulNewOwner == GDI_OBJ_HMGR_NONE)
|
|
ulNewOwner = GDI_OBJ_HMGR_PUBLIC;
|
|
|
|
/* Was the object process owned? */
|
|
if ((ulOldOwner != GDI_OBJ_HMGR_PUBLIC) &&
|
|
(ulOldOwner != GDI_OBJ_HMGR_NONE))
|
|
{
|
|
/* Decrement the previous owners handle count */
|
|
DecrementGdiHandleCount(ulOldOwner);
|
|
}
|
|
|
|
/* Is the new owner a process? */
|
|
if ((ulNewOwner != GDI_OBJ_HMGR_PUBLIC) &&
|
|
(ulNewOwner != GDI_OBJ_HMGR_NONE))
|
|
{
|
|
/* Increment the new owners handle count */
|
|
IncrementGdiHandleCount(ulNewOwner);
|
|
}
|
|
else
|
|
{
|
|
/* Make sure we don't leak user mode memory */
|
|
NT_ASSERT(pentry->pUser == NULL);
|
|
}
|
|
|
|
/* Set new owner */
|
|
pentry->ObjectOwner.ulObj = ulNewOwner;
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_SET_OWNER, 0);
|
|
}
|
|
|
|
/* Locks 2 or 3 objects at a time */
|
|
BOOL
|
|
NTAPI
|
|
GDIOBJ_bLockMultipleObjects(
|
|
IN ULONG ulCount,
|
|
IN HGDIOBJ* ahObj,
|
|
OUT PGDIOBJ* apObj,
|
|
IN UCHAR objt)
|
|
{
|
|
UINT auiIndices[3] = {0, 1, 2};
|
|
UINT i, j, tmp;
|
|
|
|
ASSERT(ulCount <= 3);
|
|
|
|
/* Sort the handles */
|
|
for (i = 0; i < ulCount - 1; i++)
|
|
{
|
|
for (j = i + 1; j < ulCount; j++)
|
|
{
|
|
if ((ULONG_PTR)ahObj[auiIndices[i]] <
|
|
(ULONG_PTR)ahObj[auiIndices[j]])
|
|
{
|
|
tmp = auiIndices[i];
|
|
auiIndices[i] = auiIndices[j];
|
|
auiIndices[j] = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Lock the objects in safe order */
|
|
for (i = 0; i < ulCount; i++)
|
|
{
|
|
/* Skip NULL handles */
|
|
if (ahObj[auiIndices[i]] == NULL)
|
|
{
|
|
apObj[auiIndices[i]] = NULL;
|
|
continue;
|
|
}
|
|
|
|
/* Lock the object */
|
|
apObj[auiIndices[i]] = GDIOBJ_LockObject(ahObj[auiIndices[i]], objt);
|
|
|
|
/* Check for failure */
|
|
if (apObj[auiIndices[i]] == NULL)
|
|
{
|
|
/* Cleanup */
|
|
while (i--)
|
|
{
|
|
if (apObj[auiIndices[i]])
|
|
GDIOBJ_vUnlockObject(apObj[auiIndices[i]]);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
GDIOBJ_pvGetObjectAttr(POBJ pobj)
|
|
{
|
|
ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
return gpentHmgr[ulIndex].pUser;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vSetObjectAttr(POBJ pobj, PVOID pvObjAttr)
|
|
{
|
|
ULONG ulIndex;
|
|
|
|
ASSERT(pobj->hHmgr);
|
|
|
|
/* Get the handle index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
|
|
/* Set pointer to the usermode attribute */
|
|
gpentHmgr[ulIndex].pUser = pvObjAttr;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
GDIOBJ_vDeleteObject(POBJ pobj)
|
|
{
|
|
ULONG ulIndex;
|
|
|
|
/* Set the object's delete flag */
|
|
InterlockedOr16((SHORT*)&pobj->BaseFlags, BASEFLAG_READY_TO_DIE);
|
|
DBG_LOGEVENT(&pobj->slhLog, EVENT_DELETE, 0);
|
|
|
|
/* Get the handle index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
|
|
if (ulIndex)
|
|
{
|
|
/* Reset the handle valid bit */
|
|
InterlockedAnd((LONG*)&gpaulRefCount[ulIndex], ~REF_MASK_VALID);
|
|
|
|
/* Check if the object is exclusively locked */
|
|
if (pobj->cExclusiveLock != 0)
|
|
{
|
|
/* Reset lock owner and lock count */
|
|
pobj->dwThreadId = 0;
|
|
pobj->cExclusiveLock = 0;
|
|
|
|
/* Release the pushlock and reenable APCs */
|
|
ExReleasePushLockExclusive(&pobj->pushlock);
|
|
KeLeaveCriticalRegion();
|
|
DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
|
|
}
|
|
}
|
|
|
|
/* Dereference the object (will take care of deletion) */
|
|
GDIOBJ_vDereferenceObject(pobj);
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
GreIsHandleValid(HGDIOBJ hobj)
|
|
{
|
|
PENTRY pentry;
|
|
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
|
|
if (!pentry) return FALSE;
|
|
GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
GreDeleteObject(HGDIOBJ hobj)
|
|
{
|
|
PENTRY pentry;
|
|
|
|
/* Check for stock objects */
|
|
if (GDI_HANDLE_IS_STOCKOBJ(hobj))
|
|
{
|
|
DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check for public owner */
|
|
if (pentry->ObjectOwner.ulObj == GDI_OBJ_HMGR_PUBLIC)
|
|
{
|
|
DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj);
|
|
GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Delete the object */
|
|
GDIOBJ_vDeleteObject(pentry->einfo.pobj);
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
GreGetObjectOwner(HGDIOBJ hobj)
|
|
{
|
|
ULONG ulIndex, ulOwner;
|
|
|
|
/* Get the handle index */
|
|
ulIndex = GDI_HANDLE_GET_INDEX(hobj);
|
|
|
|
/* Check if the handle is valid */
|
|
if (ulIndex >= GDI_HANDLE_COUNT ||
|
|
gpentHmgr[ulIndex].Objt == GDIObjType_DEF_TYPE ||
|
|
((ULONG_PTR)hobj >> 16) != gpentHmgr[ulIndex].FullUnique)
|
|
{
|
|
DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj);
|
|
return GDI_OBJ_HMGR_RESTRICTED;
|
|
}
|
|
|
|
/* Get the object owner */
|
|
ulOwner = gpentHmgr[ulIndex].ObjectOwner.ulObj;
|
|
|
|
if (ulOwner == HandleToUlong(PsGetCurrentProcessId()))
|
|
return GDI_OBJ_HMGR_POWNED;
|
|
|
|
if (ulOwner == GDI_OBJ_HMGR_PUBLIC)
|
|
return GDI_OBJ_HMGR_PUBLIC;
|
|
|
|
return GDI_OBJ_HMGR_RESTRICTED;
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
GreSetObjectOwnerEx(
|
|
HGDIOBJ hobj,
|
|
ULONG ulOwner,
|
|
ULONG Flags)
|
|
{
|
|
PENTRY pentry;
|
|
|
|
/* Check for stock objects */
|
|
if (GDI_HANDLE_IS_STOCKOBJ(hobj))
|
|
{
|
|
DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(hobj, Flags);
|
|
if (!pentry)
|
|
{
|
|
DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Call internal function */
|
|
GDIOBJ_vSetObjectOwner(pentry->einfo.pobj, ulOwner);
|
|
|
|
/* Dereference the object */
|
|
GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
GreSetObjectOwner(
|
|
HGDIOBJ hobj,
|
|
ULONG ulOwner)
|
|
{
|
|
return GreSetObjectOwnerEx(hobj, ulOwner, 0);
|
|
}
|
|
|
|
INT
|
|
NTAPI
|
|
GreGetObject(
|
|
IN HGDIOBJ hobj,
|
|
IN INT cbCount,
|
|
OUT PVOID pvBuffer)
|
|
{
|
|
PVOID pvObj;
|
|
UCHAR objt;
|
|
INT iResult = 0;
|
|
|
|
/* Verify object type */
|
|
objt = ((ULONG_PTR)hobj >> 16) & 0x1f;
|
|
if (objt != GDIObjType_BRUSH_TYPE &&
|
|
objt != GDIObjType_SURF_TYPE &&
|
|
objt != GDIObjType_LFONT_TYPE &&
|
|
objt != GDIObjType_PAL_TYPE)
|
|
{
|
|
DPRINT1("GreGetObject: Invalid object type\n");
|
|
return 0;
|
|
}
|
|
|
|
pvObj = GDIOBJ_ReferenceObjectByHandle(hobj, objt);
|
|
if (!pvObj)
|
|
{
|
|
DPRINT("GreGetObject: Could not lock object\n");
|
|
return 0;
|
|
}
|
|
|
|
switch (GDI_HANDLE_GET_TYPE(hobj))
|
|
{
|
|
case GDILoObjType_LO_PEN_TYPE:
|
|
case GDILoObjType_LO_EXTPEN_TYPE:
|
|
iResult = PEN_GetObject(pvObj, cbCount, pvBuffer);
|
|
break;
|
|
|
|
case GDILoObjType_LO_BRUSH_TYPE:
|
|
iResult = BRUSH_GetObject(pvObj, cbCount, pvBuffer);
|
|
break;
|
|
|
|
case GDILoObjType_LO_BITMAP_TYPE:
|
|
iResult = BITMAP_GetObject(pvObj, cbCount, pvBuffer);
|
|
break;
|
|
|
|
case GDILoObjType_LO_FONT_TYPE:
|
|
iResult = FontGetObject(pvObj, cbCount, pvBuffer);
|
|
break;
|
|
|
|
case GDILoObjType_LO_PALETTE_TYPE:
|
|
iResult = PALETTE_GetObject(pvObj, cbCount, pvBuffer);
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("GDI object type of 0x%p not implemented\n", hobj);
|
|
break;
|
|
}
|
|
|
|
GDIOBJ_vDereferenceObject(pvObj);
|
|
return iResult;
|
|
}
|
|
|
|
W32KAPI
|
|
INT
|
|
APIENTRY
|
|
NtGdiExtGetObjectW(
|
|
IN HANDLE hobj,
|
|
IN INT cjBufferSize,
|
|
OUT LPVOID lpBuffer)
|
|
{
|
|
UINT iResult, cjMaxSize;
|
|
union
|
|
{
|
|
BITMAP bitmap;
|
|
DIBSECTION dibsection;
|
|
LOGPEN logpen;
|
|
LOGBRUSH logbrush;
|
|
LOGFONTW logfontw;
|
|
EXTLOGFONTW extlogfontw;
|
|
ENUMLOGFONTEXDVW enumlogfontexdvw;
|
|
} object;
|
|
|
|
/* Normalize to the largest supported object size */
|
|
cjMaxSize = min((UINT)cjBufferSize, sizeof(object));
|
|
|
|
/* Now do the actual call */
|
|
iResult = GreGetObject(hobj, cjMaxSize, lpBuffer ? &object : NULL);
|
|
|
|
/* Check if we have a buffer and data */
|
|
if ((lpBuffer != NULL) && (iResult != 0))
|
|
{
|
|
/* Enter SEH for buffer transfer */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the buffer and copy it */
|
|
cjMaxSize = min(cjMaxSize, iResult);
|
|
ProbeForWrite(lpBuffer, cjMaxSize, sizeof(WORD));
|
|
RtlCopyMemory(lpBuffer, &object, cjMaxSize);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Clear the return value.
|
|
* Do *NOT* set last error here! */
|
|
iResult = 0;
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Return the count */
|
|
return iResult;
|
|
}
|
|
|
|
W32KAPI
|
|
HANDLE
|
|
APIENTRY
|
|
NtGdiCreateClientObj(
|
|
IN ULONG ulType)
|
|
{
|
|
POBJ pObject;
|
|
HANDLE handle;
|
|
|
|
/* Check if ulType is valid */
|
|
if ((ulType != GDILoObjType_LO_METAFILE16_TYPE) &&
|
|
(ulType != GDILoObjType_LO_METAFILE_TYPE) &&
|
|
(ulType != GDILoObjType_LO_METADC16_TYPE))
|
|
{
|
|
DPRINT1("NtGdiCreateClientObj: Invalid object type 0x%lx.\n", ulType);
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate a new object */
|
|
pObject = GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE,
|
|
sizeof(CLIENTOBJ),
|
|
BASEFLAG_LOOKASIDE);
|
|
if (!pObject)
|
|
{
|
|
DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Set the real object type */
|
|
pObject->hHmgr = UlongToHandle(ulType | GDILoObjType_LO_CLIENTOBJ_TYPE);
|
|
|
|
/* Create a handle */
|
|
handle = GDIOBJ_hInsertObject(pObject, GDI_OBJ_HMGR_POWNED);
|
|
if (!handle)
|
|
{
|
|
DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
|
|
GDIOBJ_vFreeObject(pObject);
|
|
return NULL;
|
|
}
|
|
|
|
/* Unlock it */
|
|
GDIOBJ_vUnlockObject(pObject);
|
|
|
|
return handle;
|
|
}
|
|
|
|
W32KAPI
|
|
BOOL
|
|
APIENTRY
|
|
NtGdiDeleteClientObj(
|
|
IN HANDLE hobj)
|
|
{
|
|
/* We first need to get the real type from the handle */
|
|
ULONG ulType = GDI_HANDLE_GET_TYPE(hobj);
|
|
|
|
/* Check if it's really a CLIENTOBJ */
|
|
if ((ulType & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
|
|
{
|
|
/* FIXME: SetLastError? */
|
|
return FALSE;
|
|
}
|
|
|
|
return GreDeleteObject(hobj);
|
|
}
|
|
|
|
|
|
|
|
PGDI_HANDLE_TABLE GdiHandleTable = NULL;
|
|
|
|
PGDIOBJ NTAPI
|
|
GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
|
|
{
|
|
if (ExpectedType == GDI_OBJECT_TYPE_DONTCARE)
|
|
ExpectedType = GDI_HANDLE_GET_TYPE(hObj);
|
|
return GDIOBJ_ReferenceObjectByHandle(hObj, (ExpectedType >> 16) & 0x1f);
|
|
}
|
|
|
|
// This function is not safe to use with concurrent deleting attempts
|
|
// That shouldn't be a problem, since we don't have any processes yet,
|
|
// that could delete the handle
|
|
BOOL
|
|
NTAPI
|
|
GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
|
|
{
|
|
PENTRY pentry;
|
|
POBJ pobj;
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Update the entry */
|
|
pentry->FullUnique |= GDI_ENTRY_STOCK_MASK;
|
|
pentry->ObjectOwner.ulObj = 0;
|
|
|
|
/* Get the pointer to the BASEOBJECT */
|
|
pobj = pentry->einfo.pobj;
|
|
|
|
/* Calculate the new handle */
|
|
pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr | GDI_HANDLE_STOCK_MASK);
|
|
|
|
/* Return the new handle */
|
|
*phObj = pobj->hHmgr;
|
|
|
|
/* Dereference the handle */
|
|
GDIOBJ_vDereferenceObject(pobj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
NTAPI
|
|
GDIOBJ_ConvertFromStockObj(HGDIOBJ *phObj)
|
|
{
|
|
PENTRY pentry;
|
|
POBJ pobj;
|
|
|
|
/* Reference the handle entry */
|
|
pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
|
|
if (!pentry)
|
|
{
|
|
DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Update the entry */
|
|
pentry->FullUnique &= ~GDI_ENTRY_STOCK_MASK;
|
|
pentry->ObjectOwner.ulObj = PtrToUlong(PsGetCurrentProcessId());
|
|
|
|
/* Get the pointer to the BASEOBJECT */
|
|
pobj = pentry->einfo.pobj;
|
|
|
|
/* Calculate the new handle */
|
|
pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr & ~GDI_HANDLE_STOCK_MASK);
|
|
|
|
/* Return the new handle */
|
|
*phObj = pobj->hHmgr;
|
|
|
|
/* Dereference the handle */
|
|
GDIOBJ_vDereferenceObject(pobj);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
POBJ NTAPI
|
|
GDIOBJ_AllocObjWithHandle(ULONG ObjectType, ULONG cjSize)
|
|
{
|
|
POBJ pobj;
|
|
FLONG fl = 0;
|
|
UCHAR objt = (ObjectType >> 16) & 0xFF;
|
|
|
|
if ((objt == GDIObjType_DC_TYPE && cjSize == sizeof(DC)) ||
|
|
(objt == GDIObjType_PAL_TYPE && cjSize == sizeof(PALETTE)) ||
|
|
(objt == GDIObjType_RGN_TYPE && cjSize == sizeof(REGION)) ||
|
|
(objt == GDIObjType_SURF_TYPE && cjSize == sizeof(SURFACE)) ||
|
|
(objt == GDIObjType_PATH_TYPE && cjSize == sizeof(PATH)))
|
|
{
|
|
fl |= BASEFLAG_LOOKASIDE;
|
|
}
|
|
|
|
pobj = GDIOBJ_AllocateObject(objt, cjSize, fl);
|
|
if (!pobj)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!GDIOBJ_hInsertObject(pobj, GDI_OBJ_HMGR_POWNED))
|
|
{
|
|
GDIOBJ_vFreeObject(pobj);
|
|
return NULL;
|
|
}
|
|
return pobj;
|
|
}
|
|
|
|
PVOID NTAPI
|
|
GDI_MapHandleTable(PEPROCESS pProcess)
|
|
{
|
|
PVOID pvMappedView = NULL;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER liOffset;
|
|
SIZE_T cjViewSize = sizeof(GDI_HANDLE_TABLE);
|
|
|
|
liOffset.QuadPart = 0;
|
|
|
|
ASSERT(gpvGdiHdlTblSection != NULL);
|
|
ASSERT(pProcess != NULL);
|
|
|
|
Status = MmMapViewOfSection(gpvGdiHdlTblSection,
|
|
pProcess,
|
|
&pvMappedView,
|
|
0,
|
|
0,
|
|
&liOffset,
|
|
&cjViewSize,
|
|
ViewUnmap,
|
|
SEC_NO_CHANGE,
|
|
PAGE_READONLY);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return NULL;
|
|
|
|
return pvMappedView;
|
|
}
|
|
|
|
BOOL NTAPI
|
|
GDI_CleanupForProcess(struct _EPROCESS *Process)
|
|
{
|
|
PENTRY pentry;
|
|
ULONG ulIndex;
|
|
DWORD dwProcessId;
|
|
PPROCESSINFO ppi;
|
|
|
|
DPRINT("CleanupForProcess prochandle %p Pid %p\n",
|
|
Process, Process->UniqueProcessId);
|
|
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
/* Get the current process Id */
|
|
dwProcessId = PtrToUlong(PsGetCurrentProcessId());
|
|
|
|
/* Loop all handles in the handle table */
|
|
for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
|
|
{
|
|
pentry = &gpentHmgr[ulIndex];
|
|
|
|
/* Check if the object is owned by the process */
|
|
if (pentry->ObjectOwner.ulObj == dwProcessId)
|
|
{
|
|
ASSERT(pentry->einfo.pobj->cExclusiveLock == 0);
|
|
|
|
/* Reference the object and delete it */
|
|
InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
|
|
GDIOBJ_vDeleteObject(pentry->einfo.pobj);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
DbgGdiHTIntegrityCheck();
|
|
#endif
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
DPRINT("Completed cleanup for process %p\n", Process->UniqueProcessId);
|
|
if (ppi->GDIHandleCount != 0)
|
|
{
|
|
DPRINT1("Leaking %d handles!\n", ppi->GDIHandleCount);
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Loop all handles in the handle table */
|
|
for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
|
|
{
|
|
pentry = &gpentHmgr[ulIndex];
|
|
|
|
/* Check if the object is owned by the process */
|
|
if (pentry->ObjectOwner.ulObj == dwProcessId)
|
|
{
|
|
DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
|
|
ulIndex, pentry->Objt, gpaulRefCount[ulIndex]);
|
|
DBG_DUMP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
|
|
//DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/// HACK!
|
|
PGDI_POOL
|
|
GetBrushAttrPool(VOID)
|
|
{
|
|
PPROCESSINFO ppi;
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
NT_ASSERT(ppi != NULL);
|
|
|
|
return ppi->pPoolBrushAttr;
|
|
}
|
|
|
|
/* EOF */
|