xref: /reactos/win32ss/gdi/ntgdi/gdiobj.c (revision 72f115d6)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/gdi/ntgdi/gdiobj.c
5  * PURPOSE:         General GDI object manipulation routines
6  * PROGRAMMERS:     Timo Kreuzer
7  */
8 
9 /*
10  * If you want to understand this code, you need to start thinking in portals.
11  * - gpaulRefCount is a global pointer to an allocated array of ULONG values,
12  * one for each handle. Bits 0 - 22 contain a reference count for the handle.
13  * It gets increased for each handle lock / reference. Bit 23 contains a valid
14  * bit. If this bit is 0, the handle got deleted and will be pushed to the free
15  * list, once all references are gone. Bits 24 - 31 contain the reuse value of
16  * the handle, which allows to check if the entry was changed before atomically
17  * exchanging the reference count.
18  * - Objects can exist with or without a handle
19  *   - Objects with a handle can be locked either exclusively or shared.
20  *     Both locks increase the handle reference count in gpaulRefCount.
21  *     Exclusive locks also increase the BASEOBJECT's cExclusiveLock field
22  *     and the first lock (can be acquired recursively) acquires a pushlock
23  *     that is also stored in the BASEOBJECT.
24  *   - Objects without a handle cannot have exclusive locks. Their reference
25  *     count is tracked in the BASEOBJECT's ulShareCount field.
26  * - An object that is inserted in the handle table automatically has an
27  *   exclusive lock. For objects that are "shared objects" (BRUSH, PALETTE, ...)
28  *   this is the only way it can ever be exclusively locked. It prevents the
29  *   object from being locked by another thread. A shared lock will simply fail,
30  *   while an exclusive lock will succeed after the object was unlocked.
31  *
32  * Ownership:
33  *
34  * Owner:               POWNED  PUBLIC  NONE    spec
35  * ---------------------------------------------------
36  * LockForRead          +       +       -       PUBLIC
37  * LockForWrite         +       -       -       POWNED
38  * LockAny              +       +       +       NONE
39  * NtGdiDeleteObjectApp +       -       -       PUBLIC
40  * GreDeleteObject      +       +       +       NONE
41  * GreSetOwner(POWNED)  -       -       +       -
42  * GreSetOwner(PUBLIC)  +       -       +       -
43  * GreSetOwner(NONE)    +       -       -       -
44  *
45  */
46 
47 /* INCLUDES ******************************************************************/
48 
49 #include <win32k.h>
50 #define NDEBUG
51 #include <debug.h>
52 
53 FORCEINLINE
54 ULONG
InterlockedReadUlong(_In_ _Interlocked_operand_ ULONG volatile * Source)55 InterlockedReadUlong(
56     _In_ _Interlocked_operand_ ULONG volatile *Source)
57 {
58     return *Source;
59 }
60 
61 FORCEINLINE
62 void
INCREASE_THREAD_LOCK_COUNT(_In_ HANDLE hobj)63 INCREASE_THREAD_LOCK_COUNT(
64     _In_ HANDLE hobj)
65 {
66     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
67     DBG_UNREFERENCED_PARAMETER(hobj);
68     if (pti)
69     {
70 #if DBG
71         pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]++;
72 #endif
73         pti->cExclusiveLocks++;
74     }
75 }
76 
77 FORCEINLINE
78 void
DECREASE_THREAD_LOCK_COUNT(_In_ HANDLE hobj)79 DECREASE_THREAD_LOCK_COUNT(
80     _In_ HANDLE hobj)
81 {
82     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
83     DBG_UNREFERENCED_PARAMETER(hobj);
84     if (pti)
85     {
86 #if DBG
87         pti->acExclusiveLockCount[((ULONG_PTR)hobj >> 16) & 0x1f]--;
88 #endif
89         pti->cExclusiveLocks--;
90     }
91 }
92 
93 #if DBG
94 VOID
ASSERT_LOCK_ORDER(_In_ UCHAR objt)95 ASSERT_LOCK_ORDER(
96     _In_ UCHAR objt)
97 {
98     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
99     ULONG i;
100 
101     if (pti)
102     {
103         /* Ensure correct locking order! */
104         for (i = objt + 1; i < GDIObjTypeTotal; i++)
105         {
106             NT_ASSERT(pti->acExclusiveLockCount[i] == 0);
107         }
108     }
109 }
110 #define ASSERT_SHARED_OBJECT_TYPE(objt) \
111     ASSERT((objt) == GDIObjType_SURF_TYPE || \
112            (objt) == GDIObjType_PAL_TYPE || \
113            (objt) == GDIObjType_LFONT_TYPE || \
114            (objt) == GDIObjType_PATH_TYPE || \
115            (objt) == GDIObjType_BRUSH_TYPE)
116 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt) \
117     ASSERT((objt) == GDIObjType_DC_TYPE || \
118            (objt) == GDIObjType_RGN_TYPE || \
119            (objt) == GDIObjType_UMPD_TYPE || \
120            (objt) == GDIObjType_META_TYPE)
121 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt) \
122     ASSERT((objt) == GDIObjType_DRVOBJ_TYPE)
123 #else
124 #define ASSERT_LOCK_ORDER(hobj)
125 #define ASSERT_SHARED_OBJECT_TYPE(objt)
126 #define ASSERT_EXCLUSIVE_OBJECT_TYPE(objt)
127 #define ASSERT_TRYLOCK_OBJECT_TYPE(objt)
128 #endif
129 
130 #if defined(_M_IX86) || defined(_M_AMD64)
131 #define InterlockedOr16 _InterlockedOr16
132 #endif
133 
134 #define GDIOBJ_POOL_TAG(type) ('00hG' + (((type) & 0x1f) << 24))
135 
136 enum
137 {
138     REF_MASK_REUSE = 0xff000000,
139     REF_INC_REUSE  = 0x01000000,
140     REF_MASK_VALID = 0x00800000,
141     REF_MASK_COUNT = 0x007fffff,
142     REF_MASK_INUSE = 0x00ffffff,
143 };
144 
145 /* GLOBALS *******************************************************************/
146 
147 /* Per session handle table globals */
148 static PVOID gpvGdiHdlTblSection = NULL;
149 PENTRY gpentHmgr;
150 PULONG gpaulRefCount;
151 volatile ULONG gulFirstFree;
152 volatile ULONG gulFirstUnused;
153 static PPAGED_LOOKASIDE_LIST gpaLookasideList;
154 
155 static VOID NTAPI GDIOBJ_vCleanup(PVOID ObjectBody);
156 
157 static const
158 GDICLEANUPPROC
159 apfnCleanup[] =
160 {
161     NULL,              /* 00 GDIObjType_DEF_TYPE */
162     DC_vCleanup,       /* 01 GDIObjType_DC_TYPE */
163     NULL,              /* 02 GDIObjType_UNUSED1_TYPE */
164     NULL,              /* 03 GDIObjType_UNUSED2_TYPE */
165     REGION_vCleanup,   /* 04 GDIObjType_RGN_TYPE */
166     SURFACE_vCleanup,  /* 05 GDIObjType_SURF_TYPE */
167     GDIOBJ_vCleanup,   /* 06 GDIObjType_CLIENTOBJ_TYPE */
168     GDIOBJ_vCleanup,   /* 07 GDIObjType_PATH_TYPE */
169     PALETTE_vCleanup,  /* 08 GDIObjType_PAL_TYPE */
170     GDIOBJ_vCleanup,   /* 09 GDIObjType_ICMLCS_TYPE */
171     GDIOBJ_vCleanup,   /* 0a GDIObjType_LFONT_TYPE */
172     NULL,              /* 0b GDIObjType_RFONT_TYPE, unused */
173     NULL,              /* 0c GDIObjType_PFE_TYPE, unused */
174     NULL,              /* 0d GDIObjType_PFT_TYPE, unused */
175     GDIOBJ_vCleanup,   /* 0e GDIObjType_ICMCXF_TYPE */
176     NULL,              /* 0f GDIObjType_SPRITE_TYPE, unused */
177     NULL,              /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
178     NULL,              /* 11 GDIObjType_UMPD_TYPE, unused */
179     NULL,              /* 12 GDIObjType_UNUSED4_TYPE */
180     NULL,              /* 13 GDIObjType_SPACE_TYPE, unused */
181     NULL,              /* 14 GDIObjType_UNUSED5_TYPE */
182     GDIOBJ_vCleanup,   /* 15 GDIObjType_META_TYPE */
183     NULL,              /* 16 GDIObjType_EFSTATE_TYPE, unused */
184     NULL,              /* 17 GDIObjType_BMFD_TYPE, unused */
185     NULL,              /* 18 GDIObjType_VTFD_TYPE, unused */
186     NULL,              /* 19 GDIObjType_TTFD_TYPE, unused */
187     NULL,              /* 1a GDIObjType_RC_TYPE, unused */
188     NULL,              /* 1b GDIObjType_TEMP_TYPE, unused */
189     DRIVEROBJ_vCleanup,/* 1c GDIObjType_DRVOBJ_TYPE */
190     NULL,              /* 1d GDIObjType_DCIOBJ_TYPE, unused */
191     NULL,              /* 1e GDIObjType_SPOOL_TYPE, unused */
192     NULL,              /* 1f reserved entry */
193 };
194 
195 static const
196 GDIOBJDELETEPROC
197 apfnDelete[] =
198 {
199     NULL,              /* 00 GDIObjType_DEF_TYPE */
200     NULL,              /* 01 GDIObjType_DC_TYPE */
201     NULL,              /* 02 GDIObjType_UNUSED1_TYPE */
202     NULL,              /* 03 GDIObjType_UNUSED2_TYPE */
203     NULL,              /* 04 GDIObjType_RGN_TYPE */
204     NULL,              /* 05 GDIObjType_SURF_TYPE */
205     NULL,              /* 06 GDIObjType_CLIENTOBJ_TYPE */
206     NULL,              /* 07 GDIObjType_PATH_TYPE */
207     NULL,              /* 08 GDIObjType_PAL_TYPE */
208     NULL,              /* 09 GDIObjType_ICMLCS_TYPE */
209     NULL,              /* 0a GDIObjType_LFONT_TYPE */
210     NULL,              /* 0b GDIObjType_RFONT_TYPE, unused */
211     NULL,              /* 0c GDIObjType_PFE_TYPE, unused */
212     NULL,              /* 0d GDIObjType_PFT_TYPE, unused */
213     NULL,              /* 0e GDIObjType_ICMCXF_TYPE */
214     NULL,                 /* 0f GDIObjType_SPRITE_TYPE, unused */
215     BRUSH_vDeleteObject,  /* 10 GDIObjType_BRUSH_TYPE, BRUSH, PEN, EXTPEN */
216     NULL,              /* 11 GDIObjType_UMPD_TYPE, unused */
217     NULL,              /* 12 GDIObjType_UNUSED4_TYPE */
218     NULL,              /* 13 GDIObjType_SPACE_TYPE, unused */
219     NULL,              /* 14 GDIObjType_UNUSED5_TYPE */
220     NULL,              /* 15 GDIObjType_META_TYPE, unused */
221     NULL,              /* 16 GDIObjType_EFSTATE_TYPE, unused */
222     NULL,              /* 17 GDIObjType_BMFD_TYPE, unused */
223     NULL,              /* 18 GDIObjType_VTFD_TYPE, unused */
224     NULL,              /* 19 GDIObjType_TTFD_TYPE, unused */
225     NULL,              /* 1a GDIObjType_RC_TYPE, unused */
226     NULL,              /* 1b GDIObjType_TEMP_TYPE, unused */
227     NULL,              /* 1c GDIObjType_DRVOBJ_TYPE */
228     NULL,              /* 1d GDIObjType_DCIOBJ_TYPE, unused */
229     NULL,              /* 1e GDIObjType_SPOOL_TYPE, unused */
230     NULL,              /* 1f reserved entry */
231 };
232 
233 /* INTERNAL FUNCTIONS ********************************************************/
234 
235 static
236 VOID
237 NTAPI
GDIOBJ_vCleanup(PVOID ObjectBody)238 GDIOBJ_vCleanup(PVOID ObjectBody)
239 {
240     /* Nothing to do */
241 }
242 
243 static
244 VOID
InitLookasideList(UCHAR objt,ULONG cjSize)245 InitLookasideList(UCHAR objt, ULONG cjSize)
246 {
247     ExInitializePagedLookasideList(&gpaLookasideList[objt],
248                                    NULL,
249                                    NULL,
250                                    0,
251                                    cjSize,
252                                    GDITAG_HMGR_LOOKASIDE_START + (objt << 24),
253                                    0);
254 }
255 
256 CODE_SEG("INIT")
257 NTSTATUS
258 NTAPI
InitGdiHandleTable(void)259 InitGdiHandleTable(void)
260 {
261     NTSTATUS status;
262     LARGE_INTEGER liSize;
263     PVOID pvSection;
264     SIZE_T cjViewSize = 0;
265 
266     /* Create a section for the shared handle table */
267     liSize.QuadPart = sizeof(GDI_HANDLE_TABLE); // GDI_HANDLE_COUNT * sizeof(ENTRY);
268     status = MmCreateSection(&gpvGdiHdlTblSection,
269                              SECTION_ALL_ACCESS,
270                              NULL,
271                              &liSize,
272                              PAGE_READWRITE,
273                              SEC_COMMIT | 0x1,
274                              NULL,
275                              NULL);
276     if (!NT_SUCCESS(status))
277     {
278         DPRINT1("INITGDI: Could not allocate a GDI handle table.\n");
279         return status;
280     }
281 
282     /* Map the section in session space */
283     status = MmMapViewInSessionSpace(gpvGdiHdlTblSection,
284                                      (PVOID*)&gpentHmgr,
285                                      &cjViewSize);
286     if (!NT_SUCCESS(status))
287     {
288         DPRINT1("INITGDI: Failed to map handle table section\n");
289         ObDereferenceObject(gpvGdiHdlTblSection);
290         return status;
291     }
292 
293     /* Allocate memory for the reference counter table */
294     gpaulRefCount = EngAllocSectionMem(&pvSection,
295                                      FL_ZERO_MEMORY,
296                                      GDI_HANDLE_COUNT * sizeof(ULONG),
297                                      'frHG');
298     if (!gpaulRefCount)
299     {
300         DPRINT1("INITGDI: Failed to allocate reference table.\n");
301         ObDereferenceObject(gpvGdiHdlTblSection);
302         return STATUS_INSUFFICIENT_RESOURCES;
303     }
304 
305     gulFirstFree = 0;
306     gulFirstUnused = RESERVE_ENTRIES_COUNT;
307 
308     GdiHandleTable = (PVOID)gpentHmgr;
309 
310     /* Initialize the lookaside lists */
311     gpaLookasideList = ExAllocatePoolWithTag(NonPagedPool,
312                            GDIObjTypeTotal * sizeof(PAGED_LOOKASIDE_LIST),
313                            TAG_GDIHNDTBLE);
314     if(!gpaLookasideList)
315         return STATUS_NO_MEMORY;
316 
317     InitLookasideList(GDIObjType_DC_TYPE, sizeof(DC));
318     InitLookasideList(GDIObjType_RGN_TYPE, sizeof(REGION));
319     InitLookasideList(GDIObjType_SURF_TYPE, sizeof(SURFACE));
320     InitLookasideList(GDIObjType_CLIENTOBJ_TYPE, sizeof(CLIENTOBJ));
321     InitLookasideList(GDIObjType_PATH_TYPE, sizeof(PATH));
322     InitLookasideList(GDIObjType_PAL_TYPE, sizeof(PALETTE));
323     InitLookasideList(GDIObjType_ICMLCS_TYPE, sizeof(COLORSPACE));
324     InitLookasideList(GDIObjType_LFONT_TYPE, sizeof(TEXTOBJ));
325     InitLookasideList(GDIObjType_BRUSH_TYPE, sizeof(BRUSH));
326 
327     return STATUS_SUCCESS;
328 }
329 
330 FORCEINLINE
331 VOID
IncrementCurrentProcessGdiHandleCount(void)332 IncrementCurrentProcessGdiHandleCount(void)
333 {
334     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
335     if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
336 }
337 
338 FORCEINLINE
339 VOID
DecrementCurrentProcessGdiHandleCount(void)340 DecrementCurrentProcessGdiHandleCount(void)
341 {
342     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
343     if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
344 }
345 
346 static inline
347 VOID
IncrementGdiHandleCount(ULONG ulProcessId)348 IncrementGdiHandleCount(ULONG ulProcessId)
349 {
350     PEPROCESS pep;
351     PPROCESSINFO ppi;
352     NTSTATUS Status;
353 
354     Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
355     NT_ASSERT(NT_SUCCESS(Status));
356     __analysis_assume(NT_SUCCESS(Status));
357 
358     ppi = PsGetProcessWin32Process(pep);
359     if (ppi) InterlockedIncrement((LONG*)&ppi->GDIHandleCount);
360     if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
361 }
362 
363 static inline
364 VOID
DecrementGdiHandleCount(ULONG ulProcessId)365 DecrementGdiHandleCount(ULONG ulProcessId)
366 {
367     PEPROCESS pep;
368     PPROCESSINFO ppi;
369     NTSTATUS Status;
370 
371     Status = PsLookupProcessByProcessId(ULongToHandle(ulProcessId), &pep);
372     NT_ASSERT(NT_SUCCESS(Status));
373     __analysis_assume(NT_SUCCESS(Status));
374 
375     ppi = PsGetProcessWin32Process(pep);
376     if (ppi) InterlockedDecrement((LONG*)&ppi->GDIHandleCount);
377     if (NT_SUCCESS(Status)) ObDereferenceObject(pep);
378 }
379 
380 static
381 PENTRY
ENTRY_pentPopFreeEntry(VOID)382 ENTRY_pentPopFreeEntry(VOID)
383 {
384     ULONG iFirst, iNext, iPrev;
385     PENTRY pentFree;
386 
387     DPRINT("Enter InterLockedPopFreeEntry\n");
388 
389     do
390     {
391         /* Get the index and sequence number of the first free entry */
392         iFirst = InterlockedReadUlong(&gulFirstFree);
393 
394         /* Check if we have a free entry */
395         if (!(iFirst & GDI_HANDLE_INDEX_MASK))
396         {
397             /* Increment FirstUnused and get the new index */
398             iFirst = InterlockedIncrement((LONG*)&gulFirstUnused) - 1;
399 
400             /* Check if we have unused entries left */
401             if (iFirst >= GDI_HANDLE_COUNT)
402             {
403                 DPRINT1("No more GDI handles left!\n");
404 #if DBG_ENABLE_GDIOBJ_BACKTRACES
405                 DbgDumpGdiHandleTableWithBT();
406 #endif
407                 InterlockedDecrement((LONG*)&gulFirstUnused);
408                 return 0;
409             }
410 
411             /* Return the old entry */
412             return &gpentHmgr[iFirst];
413         }
414 
415         /* Get a pointer to the first free entry */
416         pentFree = &gpentHmgr[iFirst & GDI_HANDLE_INDEX_MASK];
417 
418         /* Create a new value with an increased sequence number */
419         iNext = GDI_HANDLE_GET_INDEX(pentFree->einfo.hFree);
420         iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
421 
422         /* Try to exchange the FirstFree value */
423         iPrev = InterlockedCompareExchange((LONG*)&gulFirstFree,
424                                            iNext,
425                                            iFirst);
426     }
427     while (iPrev != iFirst);
428 
429     /* Sanity check: is entry really free? */
430     ASSERT(((ULONG_PTR)pentFree->einfo.pobj & ~GDI_HANDLE_INDEX_MASK) == 0);
431 
432     return pentFree;
433 }
434 
435 /* Pushes an entry of the handle table to the free list,
436    The entry must not have any references left */
437 static
438 VOID
ENTRY_vPushFreeEntry(PENTRY pentFree)439 ENTRY_vPushFreeEntry(PENTRY pentFree)
440 {
441     ULONG iToFree, iFirst, iPrev, idxToFree;
442 
443     DPRINT("Enter ENTRY_vPushFreeEntry\n");
444 
445     idxToFree = pentFree - gpentHmgr;
446     ASSERT((gpaulRefCount[idxToFree] & REF_MASK_INUSE) == 0);
447 
448     /* Initialize entry */
449     pentFree->Objt = GDIObjType_DEF_TYPE;
450     pentFree->ObjectOwner.ulObj = 0;
451     pentFree->pUser = NULL;
452 
453     /* Increase reuse counter in entry and reference counter */
454     InterlockedExchangeAdd((LONG*)&gpaulRefCount[idxToFree], REF_INC_REUSE);
455     pentFree->FullUnique += 0x0100;
456 
457     do
458     {
459         /* Get the current first free index and sequence number */
460         iFirst = InterlockedReadUlong(&gulFirstFree);
461 
462         /* Set the einfo.pobj member to the index of the first free entry */
463         pentFree->einfo.pobj = UlongToPtr(iFirst & GDI_HANDLE_INDEX_MASK);
464 
465         /* Combine new index and increased sequence number in iToFree */
466         iToFree = idxToFree | ((iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000);
467 
468         /* Try to atomically update the first free entry */
469         iPrev = InterlockedCompareExchange((LONG*)&gulFirstFree,
470                                            iToFree,
471                                            iFirst);
472     }
473     while (iPrev != iFirst);
474 }
475 
476 static
477 PENTRY
ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj,FLONG fl)478 ENTRY_ReferenceEntryByHandle(HGDIOBJ hobj, FLONG fl)
479 {
480     ULONG ulIndex, cNewRefs, cOldRefs;
481     PENTRY pentry;
482 
483     /* Get the handle index and check if its too big */
484     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
485 
486     /* Get pointer to the entry */
487     pentry = &gpentHmgr[ulIndex];
488 
489     /* Get the current reference count */
490     cOldRefs = gpaulRefCount[ulIndex];
491 
492     do
493     {
494         /* Check if the slot is deleted */
495         if ((cOldRefs & REF_MASK_VALID) == 0)
496         {
497             DPRINT("GDIOBJ: Slot is not valid: 0x%lx, hobh=%p\n", cOldRefs, hobj);
498             return NULL;
499         }
500 
501         /* Check if the unique value matches */
502         if (pentry->FullUnique != (USHORT)((ULONG_PTR)hobj >> 16))
503         {
504             DPRINT("GDIOBJ: Wrong unique value. Handle: 0x%4x, entry: 0x%4x\n",
505                    (USHORT)((ULONG_PTR)hobj >> 16), pentry->FullUnique);
506             return NULL;
507         }
508 
509         /* Check if the object owner is this process or public */
510         if (!(fl & GDIOBJFLAG_IGNOREPID) &&
511             pentry->ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
512             pentry->ObjectOwner.ulObj != PtrToUlong(PsGetCurrentProcessId()))
513         {
514             DPRINT("GDIOBJ: Cannot reference foreign handle %p, pentry=%p:%lx.\n",
515                     hobj, pentry, pentry->ObjectOwner.ulObj);
516             return NULL;
517         }
518 
519         /* Try to atomically increment the reference count */
520         cNewRefs = cOldRefs + 1;
521         cOldRefs = InterlockedCompareExchange((PLONG)&gpaulRefCount[ulIndex],
522                                               cNewRefs,
523                                               cOldRefs);
524     }
525     while (cNewRefs != cOldRefs + 1);
526 
527     /* Integrity checks */
528     ASSERT((pentry->FullUnique & 0x1f) == pentry->Objt);
529     ASSERT(pentry->einfo.pobj != NULL);
530 
531     /* Check if lower 32 bits match, the upper 32 bits are ignored */
532     ASSERT(pentry->einfo.pobj->hHmgr == UlongToPtr(PtrToUlong(hobj)));
533 
534     return pentry;
535 }
536 
537 static
538 HGDIOBJ
ENTRY_hInsertObject(PENTRY pentry,POBJ pobj,UCHAR objt,ULONG ulOwner)539 ENTRY_hInsertObject(PENTRY pentry, POBJ pobj, UCHAR objt, ULONG ulOwner)
540 {
541     ULONG ulIndex;
542 
543     /* Calculate the handle index */
544     ulIndex = pentry - gpentHmgr;
545 
546     /* Update the fields in the ENTRY */
547     pentry->einfo.pobj = pobj;
548     pentry->Objt = objt & 0x1f;
549     pentry->FullUnique = (pentry->FullUnique & 0xff00) | objt;
550     pentry->ObjectOwner.ulObj = ulOwner;
551 
552     /* Make the handle valid with 1 reference */
553     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_INUSE) == 0);
554     InterlockedOr((LONG*)&gpaulRefCount[ulIndex], REF_MASK_VALID | 1);
555 
556     /* Return the handle */
557     return (HGDIOBJ)(((ULONG_PTR)pentry->FullUnique << 16) | ulIndex);
558 }
559 
560 POBJ
561 NTAPI
GDIOBJ_AllocateObject(UCHAR objt,ULONG cjSize,FLONG fl)562 GDIOBJ_AllocateObject(UCHAR objt, ULONG cjSize, FLONG fl)
563 {
564     POBJ pobj;
565 
566     if (fl & BASEFLAG_LOOKASIDE)
567     {
568         /* Allocate the object from a lookaside list */
569         pobj = ExAllocateFromPagedLookasideList(&gpaLookasideList[objt & 0x1f]);
570     }
571     else
572     {
573         /* Allocate the object from paged pool */
574         pobj = ExAllocatePoolWithTag(PagedPool, cjSize, GDIOBJ_POOL_TAG(objt));
575     }
576 
577     if (!pobj) return NULL;
578 
579     /* Initialize the object */
580     RtlZeroMemory(pobj, cjSize);
581     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)objt << 16);
582     pobj->cExclusiveLock = 0;
583     pobj->ulShareCount = 1;
584     pobj->BaseFlags = fl & 0xffff;
585     DBG_INITLOG(&pobj->slhLog);
586     DBG_LOGEVENT(&pobj->slhLog, EVENT_ALLOCATE, 0);
587 #if DBG_ENABLE_GDIOBJ_BACKTRACES
588     DbgCaptureStackBackTace(pobj->apvBackTrace, 1, GDI_OBJECT_STACK_LEVELS);
589 #endif /* GDI_DEBUG */
590 
591     return pobj;
592 }
593 
594 VOID
595 NTAPI
GDIOBJ_vFreeObject(POBJ pobj)596 GDIOBJ_vFreeObject(POBJ pobj)
597 {
598     UCHAR objt;
599 
600     DBG_CLEANUP_EVENT_LIST(&pobj->slhLog);
601 
602     /* Get the object type */
603     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0x1f;
604 
605     /* Check if we have a delete procedure (for C++ based objects) */
606     if (apfnDelete[objt] != NULL)
607     {
608         /* Invoke the delete procedure */
609         apfnDelete[objt](pobj);
610     }
611     else
612     {
613         /* Call the cleanup procedure */
614         NT_ASSERT(apfnCleanup[objt]);
615         apfnCleanup[objt](pobj);
616 
617         /* Check if the object is allocated from a lookaside list */
618         if (pobj->BaseFlags & BASEFLAG_LOOKASIDE)
619         {
620             ExFreeToPagedLookasideList(&gpaLookasideList[objt], pobj);
621         }
622         else
623         {
624             ExFreePoolWithTag(pobj, GDIOBJ_POOL_TAG(objt));
625         }
626     }
627 }
628 
629 VOID
630 NTAPI
GDIOBJ_vDereferenceObject(POBJ pobj)631 GDIOBJ_vDereferenceObject(POBJ pobj)
632 {
633     ULONG cRefs, ulIndex;
634 
635     /* Calculate the index */
636     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
637 
638     /* Check if the object has a handle */
639     if (ulIndex)
640     {
641         /* Decrement reference count */
642         if ((gpaulRefCount[ulIndex] & REF_MASK_COUNT) == 0)
643         {
644             DBG_DUMP_EVENT_LIST(&pobj->slhLog);
645         }
646         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
647         cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
648         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
649 
650         /* Check if we reached 0 and handle bit is not set */
651         if ((cRefs & REF_MASK_INUSE) == 0)
652         {
653             /* Make sure it's ok to delete the object */
654             ASSERT(pobj->BaseFlags & BASEFLAG_READY_TO_DIE);
655 
656             /* Check if the handle was process owned */
657             if (gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_PUBLIC &&
658                 gpentHmgr[ulIndex].ObjectOwner.ulObj != GDI_OBJ_HMGR_NONE)
659             {
660                 /* Decrement the process handle count */
661                 ASSERT(gpentHmgr[ulIndex].ObjectOwner.ulObj ==
662                        HandleToUlong(PsGetCurrentProcessId()));
663                 DecrementCurrentProcessGdiHandleCount();
664             }
665 
666             /* Push entry to the free list */
667             ENTRY_vPushFreeEntry(&gpentHmgr[ulIndex]);
668 
669             /* Free the object */
670             GDIOBJ_vFreeObject(pobj);
671         }
672     }
673     else
674     {
675         /* Decrement the objects reference count */
676         ASSERT(pobj->ulShareCount > 0);
677         cRefs = InterlockedDecrement((LONG*)&pobj->ulShareCount);
678         DBG_LOGEVENT(&pobj->slhLog, EVENT_DEREFERENCE, cRefs);
679 
680         /* Check if we reached 0 */
681         if (cRefs == 0)
682         {
683             /* Free the object */
684             GDIOBJ_vFreeObject(pobj);
685         }
686     }
687 }
688 
689 POBJ
690 NTAPI
GDIOBJ_ReferenceObjectByHandle(HGDIOBJ hobj,UCHAR objt)691 GDIOBJ_ReferenceObjectByHandle(
692     HGDIOBJ hobj,
693     UCHAR objt)
694 {
695     PENTRY pentry;
696     POBJ pobj;
697 
698     /* Check if the handle type matches */
699     ASSERT_SHARED_OBJECT_TYPE(objt);
700     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
701     {
702         DPRINT("GDIOBJ: Wrong type. handle=%p, type=%x\n", hobj, objt);
703         return NULL;
704     }
705 
706     /* Reference the handle entry */
707     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
708     if (!pentry)
709     {
710         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
711         return NULL;
712     }
713 
714     /* Get the pointer to the BASEOBJECT */
715     pobj = pentry->einfo.pobj;
716 
717     /* Check if the object is exclusively locked */
718     if (pobj->cExclusiveLock != 0)
719     {
720         DPRINT1("GDIOBJ: Cannot reference object %p with exclusive lock.\n", hobj);
721         GDIOBJ_vDereferenceObject(pobj);
722         DBG_DUMP_EVENT_LIST(&pobj->slhLog);
723         return NULL;
724     }
725 
726     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, gpaulRefCount[pentry - gpentHmgr]);
727 
728     /* All is well, return the object */
729     return pobj;
730 }
731 
732 VOID
733 NTAPI
GDIOBJ_vReferenceObjectByPointer(POBJ pobj)734 GDIOBJ_vReferenceObjectByPointer(POBJ pobj)
735 {
736     ULONG cRefs;
737 
738     /* Check if the object has a handle */
739     if (GDI_HANDLE_GET_INDEX(pobj->hHmgr))
740     {
741         /* Increase the handle's reference count */
742         ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
743         ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
744         cRefs = InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
745     }
746     else
747     {
748         /* Increase the object's reference count */
749         cRefs = InterlockedIncrement((LONG*)&pobj->ulShareCount);
750     }
751 
752     DBG_LOGEVENT(&pobj->slhLog, EVENT_REFERENCE, cRefs);
753 }
754 
755 PGDIOBJ
756 NTAPI
GDIOBJ_TryLockObject(HGDIOBJ hobj,UCHAR objt)757 GDIOBJ_TryLockObject(
758     HGDIOBJ hobj,
759     UCHAR objt)
760 {
761     PENTRY pentry;
762     POBJ pobj;
763     DWORD dwThreadId;
764 
765     /* Check if the handle type matches */
766     ASSERT_TRYLOCK_OBJECT_TYPE(objt);
767     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
768     {
769         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
770         return NULL;
771     }
772 
773     /* Make sure lock order is correct */
774     ASSERT_LOCK_ORDER(objt);
775 
776     /* Reference the handle entry */
777     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
778     if (!pentry)
779     {
780         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
781         return NULL;
782     }
783 
784     /* Get the pointer to the BASEOBJECT */
785     pobj = pentry->einfo.pobj;
786 
787     /* Check if we already own the lock */
788     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
789     if (pobj->dwThreadId != dwThreadId)
790     {
791         /* Disable APCs and try acquiring the push lock */
792         KeEnterCriticalRegion();
793         if(!ExTryAcquirePushLockExclusive(&pobj->pushlock))
794         {
795             ULONG cRefs, ulIndex;
796             /* Already owned. Clean up and leave. */
797             KeLeaveCriticalRegion();
798 
799             /* Calculate the index */
800             ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
801 
802             /* Decrement reference count */
803             ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
804             cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
805             ASSERT(cRefs & REF_MASK_VALID);
806 
807             return NULL;
808         }
809 
810         /* Set us as lock owner */
811         ASSERT(pobj->dwThreadId == 0);
812         pobj->dwThreadId = dwThreadId;
813     }
814 
815     /* Increase lock count */
816     pobj->cExclusiveLock++;
817     INCREASE_THREAD_LOCK_COUNT(hobj);
818     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
819 
820     /* Return the object */
821     return pobj;
822 }
823 
824 PGDIOBJ
825 NTAPI
GDIOBJ_LockObject(HGDIOBJ hobj,UCHAR objt)826 GDIOBJ_LockObject(
827     HGDIOBJ hobj,
828     UCHAR objt)
829 {
830     PENTRY pentry;
831     POBJ pobj;
832     DWORD dwThreadId;
833 
834     /* Check if the handle type matches */
835     ASSERT_EXCLUSIVE_OBJECT_TYPE(objt);
836     if ((((ULONG_PTR)hobj >> 16) & 0x1f) != objt)
837     {
838         DPRINT("Wrong object type: hobj=0x%p, objt=0x%x\n", hobj, objt);
839         return NULL;
840     }
841 
842     /* Make sure lock order is correct */
843     ASSERT_LOCK_ORDER(objt);
844 
845     /* Reference the handle entry */
846     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
847     if (!pentry)
848     {
849         DPRINT("GDIOBJ: Requested handle 0x%p is not valid.\n", hobj);
850         return NULL;
851     }
852 
853     /* Get the pointer to the BASEOBJECT */
854     pobj = pentry->einfo.pobj;
855 
856     /* Check if we already own the lock */
857     dwThreadId = PtrToUlong(PsGetCurrentThreadId());
858     if (pobj->dwThreadId != dwThreadId)
859     {
860         /* Disable APCs and acquire the push lock */
861         KeEnterCriticalRegion();
862         ExAcquirePushLockExclusive(&pobj->pushlock);
863 
864         /* Set us as lock owner */
865         ASSERT(pobj->dwThreadId == 0);
866         pobj->dwThreadId = dwThreadId;
867     }
868 
869     /* Increase lock count */
870     pobj->cExclusiveLock++;
871     INCREASE_THREAD_LOCK_COUNT(hobj);
872     DBG_LOGEVENT(&pobj->slhLog, EVENT_LOCK, 0);
873 
874     /* Return the object */
875     return pobj;
876 }
877 
878 VOID
879 NTAPI
GDIOBJ_vUnlockObject(POBJ pobj)880 GDIOBJ_vUnlockObject(POBJ pobj)
881 {
882     ULONG cRefs, ulIndex;
883     ASSERT(pobj->cExclusiveLock > 0);
884 
885     /* Decrease lock count */
886     pobj->cExclusiveLock--;
887     DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
888     DBG_LOGEVENT(&pobj->slhLog, EVENT_UNLOCK, 0);
889 
890     /* Check if this was the last lock */
891     if (pobj->cExclusiveLock == 0)
892     {
893         /* Reset lock owner */
894         pobj->dwThreadId = 0;
895 
896         /* Release the pushlock and reenable APCs */
897         ExReleasePushLockExclusive(&pobj->pushlock);
898         KeLeaveCriticalRegion();
899     }
900 
901     /* Calculate the index */
902     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
903 
904     /* Decrement reference count */
905     ASSERT((gpaulRefCount[ulIndex] & REF_MASK_COUNT) > 0);
906     cRefs = InterlockedDecrement((LONG*)&gpaulRefCount[ulIndex]);
907     ASSERT(cRefs & REF_MASK_VALID);
908 }
909 
910 HGDIOBJ
911 NTAPI
GDIOBJ_hInsertObject(POBJ pobj,ULONG ulOwner)912 GDIOBJ_hInsertObject(
913     POBJ pobj,
914     ULONG ulOwner)
915 {
916     PENTRY pentry;
917     UCHAR objt;
918 
919     /* Must have no handle and only one reference */
920     ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr) == 0);
921     ASSERT(pobj->cExclusiveLock == 0);
922     ASSERT(pobj->ulShareCount == 1);
923 
924     /* Get a free handle entry */
925     pentry = ENTRY_pentPopFreeEntry();
926     if (!pentry)
927     {
928         DPRINT1("GDIOBJ: Could not get a free entry.\n");
929         return NULL;
930     }
931 
932     /* Make the object exclusively locked */
933     ExInitializePushLock(&pobj->pushlock);
934     KeEnterCriticalRegion();
935     ExAcquirePushLockExclusive(&pobj->pushlock);
936     pobj->cExclusiveLock = 1;
937     pobj->dwThreadId = PtrToUlong(PsGetCurrentThreadId());
938     INCREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
939 
940     /* Get object type from the hHmgr field */
941     objt = ((ULONG_PTR)pobj->hHmgr >> 16) & 0xff;
942     ASSERT(objt != GDIObjType_DEF_TYPE);
943 
944     /* Check if current process is requested owner */
945     if (ulOwner == GDI_OBJ_HMGR_POWNED)
946     {
947         /* Increment the process handle count */
948         IncrementCurrentProcessGdiHandleCount();
949 
950         /* Use Process id */
951         ulOwner = HandleToUlong(PsGetCurrentProcessId());
952     }
953 
954     /* Insert the object into the handle table */
955     pobj->hHmgr = ENTRY_hInsertObject(pentry, pobj, objt, ulOwner);
956 
957     /* Return the handle */
958     DPRINT("GDIOBJ: Created handle: %p\n", pobj->hHmgr);
959     DBG_LOGEVENT(&pobj->slhLog, EVENT_CREATE_HANDLE, 0);
960     return pobj->hHmgr;
961 }
962 
963 VOID
964 NTAPI
GDIOBJ_vSetObjectOwner(POBJ pobj,ULONG ulNewOwner)965 GDIOBJ_vSetObjectOwner(
966     POBJ pobj,
967     ULONG ulNewOwner)
968 {
969     PENTRY pentry;
970     ULONG ulOldOwner;
971 
972     /* This is a ugly HACK, needed to fix IntGdiSetDCOwnerEx */
973     if (GDI_HANDLE_IS_STOCKOBJ(pobj->hHmgr))
974     {
975         DPRINT("Trying to set ownership of stock object %p to %lx\n", pobj->hHmgr, ulNewOwner);
976         return;
977     }
978 
979     /* Get the handle entry */
980     NT_ASSERT(GDI_HANDLE_GET_INDEX(pobj->hHmgr));
981     pentry = &gpentHmgr[GDI_HANDLE_GET_INDEX(pobj->hHmgr)];
982 
983     /* Check if the new owner is the same as the old one */
984     ulOldOwner = pentry->ObjectOwner.ulObj;
985     if (ulOldOwner == ulNewOwner)
986     {
987         /* Nothing to do */
988         return;
989     }
990 
991     /* Is the current process requested? */
992     if (ulNewOwner == GDI_OBJ_HMGR_POWNED)
993     {
994         /* Use process id */
995         ulNewOwner = HandleToUlong(PsGetCurrentProcessId());
996     }
997 
998     // HACK
999     if (ulNewOwner == GDI_OBJ_HMGR_NONE)
1000         ulNewOwner = GDI_OBJ_HMGR_PUBLIC;
1001 
1002     /* Was the object process owned? */
1003     if ((ulOldOwner != GDI_OBJ_HMGR_PUBLIC) &&
1004         (ulOldOwner != GDI_OBJ_HMGR_NONE))
1005     {
1006         /* Decrement the previous owners handle count */
1007         DecrementGdiHandleCount(ulOldOwner);
1008     }
1009 
1010     /* Is the new owner a process? */
1011     if ((ulNewOwner != GDI_OBJ_HMGR_PUBLIC) &&
1012         (ulNewOwner != GDI_OBJ_HMGR_NONE))
1013     {
1014         /* Increment the new owners handle count */
1015         IncrementGdiHandleCount(ulNewOwner);
1016     }
1017     else
1018     {
1019         /* Make sure we don't leak user mode memory */
1020         NT_ASSERT(pentry->pUser == NULL);
1021     }
1022 
1023     /* Set new owner */
1024     pentry->ObjectOwner.ulObj = ulNewOwner;
1025     DBG_LOGEVENT(&pobj->slhLog, EVENT_SET_OWNER, 0);
1026 }
1027 
1028 /* Locks 2 or 3 objects at a time */
1029 BOOL
1030 NTAPI
GDIOBJ_bLockMultipleObjects(IN ULONG ulCount,IN HGDIOBJ * ahObj,OUT PGDIOBJ * apObj,IN UCHAR objt)1031 GDIOBJ_bLockMultipleObjects(
1032     IN ULONG ulCount,
1033     IN HGDIOBJ* ahObj,
1034     OUT PGDIOBJ* apObj,
1035     IN UCHAR objt)
1036 {
1037     UINT auiIndices[3] = {0, 1, 2};
1038     UINT i, j, tmp;
1039 
1040     ASSERT(ulCount <= 3);
1041 
1042     /* Sort the handles */
1043     for (i = 0; i < ulCount - 1; i++)
1044     {
1045         for (j = i + 1; j < ulCount; j++)
1046         {
1047             if ((ULONG_PTR)ahObj[auiIndices[i]] <
1048                 (ULONG_PTR)ahObj[auiIndices[j]])
1049             {
1050                 tmp = auiIndices[i];
1051                 auiIndices[i] = auiIndices[j];
1052                 auiIndices[j] = tmp;
1053             }
1054         }
1055     }
1056 
1057     /* Lock the objects in safe order */
1058     for (i = 0; i < ulCount; i++)
1059     {
1060         /* Skip NULL handles */
1061         if (ahObj[auiIndices[i]] == NULL)
1062         {
1063             apObj[auiIndices[i]] = NULL;
1064             continue;
1065         }
1066 
1067         /* Lock the object */
1068         apObj[auiIndices[i]] = GDIOBJ_LockObject(ahObj[auiIndices[i]], objt);
1069 
1070         /* Check for failure */
1071         if (apObj[auiIndices[i]] == NULL)
1072         {
1073             /* Cleanup */
1074             while (i--)
1075             {
1076                 if (apObj[auiIndices[i]])
1077                     GDIOBJ_vUnlockObject(apObj[auiIndices[i]]);
1078             }
1079             return FALSE;
1080         }
1081     }
1082 
1083     return TRUE;
1084 }
1085 
1086 PVOID
1087 NTAPI
GDIOBJ_pvGetObjectAttr(POBJ pobj)1088 GDIOBJ_pvGetObjectAttr(POBJ pobj)
1089 {
1090     ULONG ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1091     return gpentHmgr[ulIndex].pUser;
1092 }
1093 
1094 VOID
1095 NTAPI
GDIOBJ_vSetObjectAttr(POBJ pobj,PVOID pvObjAttr)1096 GDIOBJ_vSetObjectAttr(POBJ pobj, PVOID pvObjAttr)
1097 {
1098     ULONG ulIndex;
1099 
1100     ASSERT(pobj->hHmgr);
1101 
1102     /* Get the handle index */
1103     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1104 
1105     /* Set pointer to the usermode attribute */
1106     gpentHmgr[ulIndex].pUser = pvObjAttr;
1107 }
1108 
1109 VOID
1110 NTAPI
GDIOBJ_vDeleteObject(POBJ pobj)1111 GDIOBJ_vDeleteObject(POBJ pobj)
1112 {
1113     ULONG ulIndex;
1114 
1115     /* Set the object's delete flag */
1116     InterlockedOr16((SHORT*)&pobj->BaseFlags, BASEFLAG_READY_TO_DIE);
1117     DBG_LOGEVENT(&pobj->slhLog, EVENT_DELETE, 0);
1118 
1119     /* Get the handle index */
1120     ulIndex = GDI_HANDLE_GET_INDEX(pobj->hHmgr);
1121     if (ulIndex)
1122     {
1123         /* Reset the handle valid bit */
1124         InterlockedAnd((LONG*)&gpaulRefCount[ulIndex], ~REF_MASK_VALID);
1125 
1126         /* Check if the object is exclusively locked */
1127         if (pobj->cExclusiveLock != 0)
1128         {
1129             /* Reset lock owner and lock count */
1130             pobj->dwThreadId = 0;
1131             pobj->cExclusiveLock = 0;
1132 
1133             /* Release the pushlock and reenable APCs */
1134             ExReleasePushLockExclusive(&pobj->pushlock);
1135             KeLeaveCriticalRegion();
1136             DECREASE_THREAD_LOCK_COUNT(pobj->hHmgr);
1137         }
1138     }
1139 
1140     /* Dereference the object (will take care of deletion) */
1141     GDIOBJ_vDereferenceObject(pobj);
1142 }
1143 
1144 BOOL
1145 NTAPI
GreIsHandleValid(HGDIOBJ hobj)1146 GreIsHandleValid(HGDIOBJ hobj)
1147 {
1148     PENTRY pentry;
1149 
1150     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1151     if (!pentry) return FALSE;
1152     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1153     return TRUE;
1154 }
1155 
1156 BOOL
1157 NTAPI
GreDeleteObject(HGDIOBJ hobj)1158 GreDeleteObject(HGDIOBJ hobj)
1159 {
1160     PENTRY pentry;
1161 
1162     /* Check for stock objects */
1163     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1164     {
1165         DPRINT1("GreDeleteObject: Cannot delete stock object %p.\n", hobj);
1166         return FALSE;
1167     }
1168 
1169     /* Reference the handle entry */
1170     pentry = ENTRY_ReferenceEntryByHandle(hobj, 0);
1171     if (!pentry)
1172     {
1173         DPRINT1("GreDeleteObject: Trying to delete invalid object %p\n", hobj);
1174         return FALSE;
1175     }
1176 
1177     /* Check for public owner */
1178     if (pentry->ObjectOwner.ulObj == GDI_OBJ_HMGR_PUBLIC)
1179     {
1180         DPRINT1("GreDeleteObject: Trying to delete global object %p\n", hobj);
1181         GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1182         return FALSE;
1183     }
1184 
1185     /* Delete the object */
1186     GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1187     return TRUE;
1188 }
1189 
1190 ULONG
1191 NTAPI
GreGetObjectOwner(HGDIOBJ hobj)1192 GreGetObjectOwner(HGDIOBJ hobj)
1193 {
1194     ULONG ulIndex, ulOwner;
1195 
1196     /* Get the handle index */
1197     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
1198 
1199     /* Check if the handle is valid */
1200     if (ulIndex >= GDI_HANDLE_COUNT ||
1201         gpentHmgr[ulIndex].Objt == GDIObjType_DEF_TYPE ||
1202         ((ULONG_PTR)hobj >> 16) != gpentHmgr[ulIndex].FullUnique)
1203     {
1204         DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj);
1205         return GDI_OBJ_HMGR_RESTRICTED;
1206     }
1207 
1208     /* Get the object owner */
1209     ulOwner = gpentHmgr[ulIndex].ObjectOwner.ulObj;
1210 
1211     if (ulOwner == HandleToUlong(PsGetCurrentProcessId()))
1212         return GDI_OBJ_HMGR_POWNED;
1213 
1214     if (ulOwner == GDI_OBJ_HMGR_PUBLIC)
1215         return GDI_OBJ_HMGR_PUBLIC;
1216 
1217     return GDI_OBJ_HMGR_RESTRICTED;
1218 }
1219 
1220 BOOL
1221 NTAPI
GreSetObjectOwnerEx(HGDIOBJ hobj,ULONG ulOwner,ULONG Flags)1222 GreSetObjectOwnerEx(
1223     HGDIOBJ hobj,
1224     ULONG ulOwner,
1225     ULONG Flags)
1226 {
1227     PENTRY pentry;
1228 
1229     /* Check for stock objects */
1230     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1231     {
1232         DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj);
1233         return FALSE;
1234     }
1235 
1236     /* Reference the handle entry */
1237     pentry = ENTRY_ReferenceEntryByHandle(hobj, Flags);
1238     if (!pentry)
1239     {
1240         DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj);
1241         return FALSE;
1242     }
1243 
1244     /* Call internal function */
1245     GDIOBJ_vSetObjectOwner(pentry->einfo.pobj, ulOwner);
1246 
1247     /* Dereference the object */
1248     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1249 
1250     return TRUE;
1251 }
1252 
1253 BOOL
1254 NTAPI
GreSetObjectOwner(HGDIOBJ hobj,ULONG ulOwner)1255 GreSetObjectOwner(
1256     HGDIOBJ hobj,
1257     ULONG ulOwner)
1258 {
1259     return GreSetObjectOwnerEx(hobj, ulOwner, 0);
1260 }
1261 
1262 INT
1263 NTAPI
GreGetObject(IN HGDIOBJ hobj,IN INT cbCount,OUT PVOID pvBuffer)1264 GreGetObject(
1265     IN HGDIOBJ hobj,
1266     IN INT cbCount,
1267     OUT PVOID pvBuffer)
1268 {
1269     PVOID pvObj;
1270     UCHAR objt;
1271     INT iResult = 0;
1272 
1273     /* Verify object type */
1274     objt = ((ULONG_PTR)hobj >> 16) & 0x1f;
1275     if (objt != GDIObjType_BRUSH_TYPE &&
1276         objt != GDIObjType_SURF_TYPE &&
1277         objt != GDIObjType_LFONT_TYPE &&
1278         objt != GDIObjType_PAL_TYPE)
1279     {
1280         DPRINT1("GreGetObject: Invalid object type\n");
1281         return 0;
1282     }
1283 
1284     pvObj = GDIOBJ_ReferenceObjectByHandle(hobj, objt);
1285     if (!pvObj)
1286     {
1287         DPRINT("GreGetObject: Could not lock object\n");
1288         return 0;
1289     }
1290 
1291     switch (GDI_HANDLE_GET_TYPE(hobj))
1292     {
1293         case GDILoObjType_LO_PEN_TYPE:
1294         case GDILoObjType_LO_EXTPEN_TYPE:
1295             iResult = PEN_GetObject(pvObj, cbCount, pvBuffer);
1296             break;
1297 
1298         case GDILoObjType_LO_BRUSH_TYPE:
1299             iResult = BRUSH_GetObject(pvObj, cbCount, pvBuffer);
1300             break;
1301 
1302         case GDILoObjType_LO_BITMAP_TYPE:
1303             iResult = BITMAP_GetObject(pvObj, cbCount, pvBuffer);
1304             break;
1305 
1306         case GDILoObjType_LO_FONT_TYPE:
1307             iResult = FontGetObject(pvObj, cbCount, pvBuffer);
1308             break;
1309 
1310         case GDILoObjType_LO_PALETTE_TYPE:
1311             iResult = PALETTE_GetObject(pvObj, cbCount, pvBuffer);
1312             break;
1313 
1314         default:
1315             DPRINT1("GDI object type of 0x%p not implemented\n", hobj);
1316             break;
1317     }
1318 
1319     GDIOBJ_vDereferenceObject(pvObj);
1320     return iResult;
1321 }
1322 
1323 W32KAPI
1324 INT
1325 APIENTRY
NtGdiExtGetObjectW(IN HANDLE hobj,IN INT cjBufferSize,OUT LPVOID lpBuffer)1326 NtGdiExtGetObjectW(
1327     IN HANDLE hobj,
1328     IN INT cjBufferSize,
1329     OUT LPVOID lpBuffer)
1330 {
1331     UINT iResult, cjMaxSize;
1332     union
1333     {
1334         BITMAP bitmap;
1335         DIBSECTION dibsection;
1336         LOGPEN logpen;
1337         LOGBRUSH logbrush;
1338         LOGFONTW logfontw;
1339         EXTLOGFONTW extlogfontw;
1340         ENUMLOGFONTEXDVW enumlogfontexdvw;
1341     } object;
1342 
1343     /* Normalize to the largest supported object size */
1344     cjMaxSize = min((UINT)cjBufferSize, sizeof(object));
1345 
1346     /* Now do the actual call */
1347     iResult = GreGetObject(hobj, cjMaxSize, lpBuffer ? &object : NULL);
1348 
1349     /* Check if we have a buffer and data */
1350     if ((lpBuffer != NULL) && (iResult != 0))
1351     {
1352         /* Enter SEH for buffer transfer */
1353         _SEH2_TRY
1354         {
1355             /* Probe the buffer and copy it */
1356             cjMaxSize = min(cjMaxSize, iResult);
1357             ProbeForWrite(lpBuffer, cjMaxSize, sizeof(WORD));
1358             RtlCopyMemory(lpBuffer, &object, cjMaxSize);
1359         }
1360         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1361         {
1362             /* Clear the return value.
1363              * Do *NOT* set last error here! */
1364             iResult = 0;
1365         }
1366         _SEH2_END;
1367     }
1368 
1369     /* Return the count */
1370     return iResult;
1371 }
1372 
1373 W32KAPI
1374 HANDLE
1375 APIENTRY
NtGdiCreateClientObj(IN ULONG ulType)1376 NtGdiCreateClientObj(
1377     IN ULONG ulType)
1378 {
1379     POBJ pObject;
1380     HANDLE handle;
1381 
1382     /* Check if ulType is valid */
1383     if ((ulType != GDILoObjType_LO_METAFILE16_TYPE) &&
1384         (ulType != GDILoObjType_LO_METAFILE_TYPE) &&
1385         (ulType != GDILoObjType_LO_METADC16_TYPE))
1386     {
1387         DPRINT1("NtGdiCreateClientObj: Invalid object type 0x%lx.\n", ulType);
1388         return NULL;
1389     }
1390 
1391     /* Allocate a new object */
1392     pObject = GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE,
1393                                     sizeof(CLIENTOBJ),
1394                                     BASEFLAG_LOOKASIDE);
1395     if (!pObject)
1396     {
1397         DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1398         return NULL;
1399     }
1400 
1401     /* Set the real object type */
1402     pObject->hHmgr = UlongToHandle(ulType | GDILoObjType_LO_CLIENTOBJ_TYPE);
1403 
1404     /* Create a handle */
1405     handle = GDIOBJ_hInsertObject(pObject, GDI_OBJ_HMGR_POWNED);
1406     if (!handle)
1407     {
1408         DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1409         GDIOBJ_vFreeObject(pObject);
1410         return NULL;
1411     }
1412 
1413     /* Unlock it */
1414     GDIOBJ_vUnlockObject(pObject);
1415 
1416     return handle;
1417 }
1418 
1419 W32KAPI
1420 BOOL
1421 APIENTRY
NtGdiDeleteClientObj(IN HANDLE hobj)1422 NtGdiDeleteClientObj(
1423     IN HANDLE hobj)
1424 {
1425     /* We first need to get the real type from the handle */
1426     ULONG ulType = GDI_HANDLE_GET_TYPE(hobj);
1427 
1428     /* Check if it's really a CLIENTOBJ */
1429     if ((ulType & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1430     {
1431         /* FIXME: SetLastError? */
1432         return FALSE;
1433     }
1434 
1435     return GreDeleteObject(hobj);
1436 }
1437 
1438 
1439 
1440 PGDI_HANDLE_TABLE GdiHandleTable = NULL;
1441 
1442 PGDIOBJ NTAPI
GDIOBJ_ShareLockObj(HGDIOBJ hObj,DWORD ExpectedType)1443 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1444 {
1445     if (ExpectedType == GDI_OBJECT_TYPE_DONTCARE)
1446         ExpectedType = GDI_HANDLE_GET_TYPE(hObj);
1447     return GDIOBJ_ReferenceObjectByHandle(hObj, (ExpectedType >> 16) & 0x1f);
1448 }
1449 
1450 // This function is not safe to use with concurrent deleting attempts
1451 // That shouldn't be a problem, since we don't have any processes yet,
1452 // that could delete the handle
1453 BOOL
1454 NTAPI
GDIOBJ_ConvertToStockObj(HGDIOBJ * phObj)1455 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1456 {
1457     PENTRY pentry;
1458     POBJ pobj;
1459 
1460     /* Reference the handle entry */
1461     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1462     if (!pentry)
1463     {
1464         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1465         return FALSE;
1466     }
1467 
1468     /* Update the entry */
1469     pentry->FullUnique |= GDI_ENTRY_STOCK_MASK;
1470     pentry->ObjectOwner.ulObj = 0;
1471 
1472     /* Get the pointer to the BASEOBJECT */
1473     pobj = pentry->einfo.pobj;
1474 
1475     /* Calculate the new handle */
1476     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr | GDI_HANDLE_STOCK_MASK);
1477 
1478     /* Return the new handle */
1479     *phObj = pobj->hHmgr;
1480 
1481     /* Dereference the handle */
1482     GDIOBJ_vDereferenceObject(pobj);
1483 
1484     return TRUE;
1485 }
1486 
1487 BOOL
1488 NTAPI
GDIOBJ_ConvertFromStockObj(HGDIOBJ * phObj)1489 GDIOBJ_ConvertFromStockObj(HGDIOBJ *phObj)
1490 {
1491     PENTRY pentry;
1492     POBJ pobj;
1493 
1494     /* Reference the handle entry */
1495     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1496     if (!pentry)
1497     {
1498         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1499         return FALSE;
1500     }
1501 
1502     /* Update the entry */
1503     pentry->FullUnique &= ~GDI_ENTRY_STOCK_MASK;
1504     pentry->ObjectOwner.ulObj = PtrToUlong(PsGetCurrentProcessId());
1505 
1506     /* Get the pointer to the BASEOBJECT */
1507     pobj = pentry->einfo.pobj;
1508 
1509     /* Calculate the new handle */
1510     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr & ~GDI_HANDLE_STOCK_MASK);
1511 
1512     /* Return the new handle */
1513     *phObj = pobj->hHmgr;
1514 
1515     /* Dereference the handle */
1516     GDIOBJ_vDereferenceObject(pobj);
1517 
1518     return TRUE;
1519 }
1520 
1521 POBJ NTAPI
GDIOBJ_AllocObjWithHandle(ULONG ObjectType,ULONG cjSize)1522 GDIOBJ_AllocObjWithHandle(ULONG ObjectType, ULONG cjSize)
1523 {
1524     POBJ pobj;
1525     FLONG fl = 0;
1526     UCHAR objt = (ObjectType >> 16) & 0xFF;
1527 
1528     if ((objt == GDIObjType_DC_TYPE && cjSize == sizeof(DC)) ||
1529         (objt == GDIObjType_PAL_TYPE && cjSize == sizeof(PALETTE)) ||
1530         (objt == GDIObjType_RGN_TYPE && cjSize == sizeof(REGION)) ||
1531         (objt == GDIObjType_SURF_TYPE && cjSize == sizeof(SURFACE)) ||
1532         (objt == GDIObjType_PATH_TYPE && cjSize == sizeof(PATH)))
1533     {
1534         fl |= BASEFLAG_LOOKASIDE;
1535     }
1536 
1537     pobj = GDIOBJ_AllocateObject(objt, cjSize, fl);
1538     if (!pobj)
1539     {
1540         return NULL;
1541     }
1542 
1543     if (!GDIOBJ_hInsertObject(pobj, GDI_OBJ_HMGR_POWNED))
1544     {
1545         GDIOBJ_vFreeObject(pobj);
1546         return NULL;
1547     }
1548     return pobj;
1549 }
1550 
1551 PVOID NTAPI
GDI_MapHandleTable(PEPROCESS pProcess)1552 GDI_MapHandleTable(PEPROCESS pProcess)
1553 {
1554     PVOID pvMappedView = NULL;
1555     NTSTATUS Status;
1556     LARGE_INTEGER liOffset;
1557     SIZE_T cjViewSize = sizeof(GDI_HANDLE_TABLE);
1558 
1559     liOffset.QuadPart = 0;
1560 
1561     ASSERT(gpvGdiHdlTblSection != NULL);
1562     ASSERT(pProcess != NULL);
1563 
1564     Status = MmMapViewOfSection(gpvGdiHdlTblSection,
1565                                 pProcess,
1566                                 &pvMappedView,
1567                                 0,
1568                                 0,
1569                                 &liOffset,
1570                                 &cjViewSize,
1571                                 ViewUnmap,
1572                                 SEC_NO_CHANGE,
1573                                 PAGE_READONLY);
1574 
1575     if (!NT_SUCCESS(Status))
1576         return NULL;
1577 
1578     return pvMappedView;
1579 }
1580 
1581 BOOL NTAPI
GDI_CleanupForProcess(struct _EPROCESS * Process)1582 GDI_CleanupForProcess(struct _EPROCESS *Process)
1583 {
1584     PENTRY pentry;
1585     ULONG ulIndex;
1586     DWORD dwProcessId;
1587     PPROCESSINFO ppi;
1588 
1589     DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1590            Process, Process->UniqueProcessId);
1591 
1592     ASSERT(Process == PsGetCurrentProcess());
1593 
1594     /* Get the current process Id */
1595     dwProcessId = PtrToUlong(PsGetCurrentProcessId());
1596 
1597     /* Loop all handles in the handle table */
1598     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1599     {
1600         pentry = &gpentHmgr[ulIndex];
1601 
1602         /* Check if the object is owned by the process */
1603         if (pentry->ObjectOwner.ulObj == dwProcessId)
1604         {
1605             ASSERT(pentry->einfo.pobj->cExclusiveLock == 0);
1606 
1607             /* Reference the object and delete it */
1608             InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
1609             GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1610         }
1611     }
1612 
1613 #if DBG
1614     DbgGdiHTIntegrityCheck();
1615 #endif
1616 
1617     ppi = PsGetCurrentProcessWin32Process();
1618     DPRINT("Completed cleanup for process %p\n", Process->UniqueProcessId);
1619     if (ppi->GDIHandleCount != 0)
1620     {
1621         DPRINT1("Leaking %d handles!\n", ppi->GDIHandleCount);
1622         ASSERT(FALSE);
1623     }
1624 
1625     /* Loop all handles in the handle table */
1626     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1627     {
1628         pentry = &gpentHmgr[ulIndex];
1629 
1630         /* Check if the object is owned by the process */
1631         if (pentry->ObjectOwner.ulObj == dwProcessId)
1632         {
1633             DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1634                     ulIndex, pentry->Objt, gpaulRefCount[ulIndex]);
1635             DBG_DUMP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1636             //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1637             ASSERT(FALSE);
1638         }
1639     }
1640 
1641     return TRUE;
1642 }
1643 
1644 /// HACK!
1645 PGDI_POOL
GetBrushAttrPool(VOID)1646 GetBrushAttrPool(VOID)
1647 {
1648     PPROCESSINFO ppi;
1649 
1650     ppi = PsGetCurrentProcessWin32Process();
1651     NT_ASSERT(ppi != NULL);
1652 
1653     return ppi->pPoolBrushAttr;
1654 }
1655 
1656 /* EOF */
1657