xref: /reactos/win32ss/gdi/ntgdi/gdiobj.c (revision b1257cef)
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     if (hobj == NULL)
1197     {
1198         DPRINT("GreGetObjectOwner: invalid NULL handle\n");
1199         return GDI_OBJ_HMGR_RESTRICTED;
1200     }
1201 
1202     /* Get the handle index */
1203     ulIndex = GDI_HANDLE_GET_INDEX(hobj);
1204 
1205     /* Check if the handle is valid */
1206     if (ulIndex >= GDI_HANDLE_COUNT ||
1207         gpentHmgr[ulIndex].Objt == GDIObjType_DEF_TYPE ||
1208         ((ULONG_PTR)hobj >> 16) != gpentHmgr[ulIndex].FullUnique)
1209     {
1210         DPRINT1("GreGetObjectOwner: invalid handle 0x%p.\n", hobj);
1211         return GDI_OBJ_HMGR_RESTRICTED;
1212     }
1213 
1214     /* Get the object owner */
1215     ulOwner = gpentHmgr[ulIndex].ObjectOwner.ulObj;
1216 
1217     if (ulOwner == HandleToUlong(PsGetCurrentProcessId()))
1218         return GDI_OBJ_HMGR_POWNED;
1219 
1220     if (ulOwner == GDI_OBJ_HMGR_PUBLIC)
1221         return GDI_OBJ_HMGR_PUBLIC;
1222 
1223     return GDI_OBJ_HMGR_RESTRICTED;
1224 }
1225 
1226 BOOL
1227 NTAPI
GreSetObjectOwnerEx(HGDIOBJ hobj,ULONG ulOwner,ULONG Flags)1228 GreSetObjectOwnerEx(
1229     HGDIOBJ hobj,
1230     ULONG ulOwner,
1231     ULONG Flags)
1232 {
1233     PENTRY pentry;
1234 
1235     /* Check for stock objects */
1236     if (GDI_HANDLE_IS_STOCKOBJ(hobj))
1237     {
1238         DPRINT("GreSetObjectOwner: Got stock object %p\n", hobj);
1239         return FALSE;
1240     }
1241 
1242     /* Reference the handle entry */
1243     pentry = ENTRY_ReferenceEntryByHandle(hobj, Flags);
1244     if (!pentry)
1245     {
1246         DPRINT("GreSetObjectOwner: Invalid handle 0x%p.\n", hobj);
1247         return FALSE;
1248     }
1249 
1250     /* Call internal function */
1251     GDIOBJ_vSetObjectOwner(pentry->einfo.pobj, ulOwner);
1252 
1253     /* Dereference the object */
1254     GDIOBJ_vDereferenceObject(pentry->einfo.pobj);
1255 
1256     return TRUE;
1257 }
1258 
1259 BOOL
1260 NTAPI
GreSetObjectOwner(HGDIOBJ hobj,ULONG ulOwner)1261 GreSetObjectOwner(
1262     HGDIOBJ hobj,
1263     ULONG ulOwner)
1264 {
1265     return GreSetObjectOwnerEx(hobj, ulOwner, 0);
1266 }
1267 
1268 INT
1269 NTAPI
GreGetObject(IN HGDIOBJ hobj,IN INT cbCount,OUT PVOID pvBuffer)1270 GreGetObject(
1271     IN HGDIOBJ hobj,
1272     IN INT cbCount,
1273     OUT PVOID pvBuffer)
1274 {
1275     PVOID pvObj;
1276     UCHAR objt;
1277     INT iResult = 0;
1278 
1279     /* Verify object type */
1280     objt = ((ULONG_PTR)hobj >> 16) & 0x1f;
1281     if (objt != GDIObjType_BRUSH_TYPE &&
1282         objt != GDIObjType_SURF_TYPE &&
1283         objt != GDIObjType_LFONT_TYPE &&
1284         objt != GDIObjType_PAL_TYPE)
1285     {
1286         DPRINT1("GreGetObject: Invalid object type\n");
1287         return 0;
1288     }
1289 
1290     pvObj = GDIOBJ_ReferenceObjectByHandle(hobj, objt);
1291     if (!pvObj)
1292     {
1293         DPRINT("GreGetObject: Could not lock object\n");
1294         return 0;
1295     }
1296 
1297     switch (GDI_HANDLE_GET_TYPE(hobj))
1298     {
1299         case GDILoObjType_LO_PEN_TYPE:
1300         case GDILoObjType_LO_EXTPEN_TYPE:
1301             iResult = PEN_GetObject(pvObj, cbCount, pvBuffer);
1302             break;
1303 
1304         case GDILoObjType_LO_BRUSH_TYPE:
1305             iResult = BRUSH_GetObject(pvObj, cbCount, pvBuffer);
1306             break;
1307 
1308         case GDILoObjType_LO_BITMAP_TYPE:
1309             iResult = BITMAP_GetObject(pvObj, cbCount, pvBuffer);
1310             break;
1311 
1312         case GDILoObjType_LO_FONT_TYPE:
1313             iResult = FontGetObject(pvObj, cbCount, pvBuffer);
1314             break;
1315 
1316         case GDILoObjType_LO_PALETTE_TYPE:
1317             iResult = PALETTE_GetObject(pvObj, cbCount, pvBuffer);
1318             break;
1319 
1320         default:
1321             DPRINT1("GDI object type of 0x%p not implemented\n", hobj);
1322             break;
1323     }
1324 
1325     GDIOBJ_vDereferenceObject(pvObj);
1326     return iResult;
1327 }
1328 
1329 W32KAPI
1330 INT
1331 APIENTRY
NtGdiExtGetObjectW(IN HANDLE hobj,IN INT cjBufferSize,OUT LPVOID lpBuffer)1332 NtGdiExtGetObjectW(
1333     IN HANDLE hobj,
1334     IN INT cjBufferSize,
1335     OUT LPVOID lpBuffer)
1336 {
1337     UINT iResult, cjMaxSize;
1338     union
1339     {
1340         BITMAP bitmap;
1341         DIBSECTION dibsection;
1342         LOGPEN logpen;
1343         LOGBRUSH logbrush;
1344         LOGFONTW logfontw;
1345         EXTLOGFONTW extlogfontw;
1346         ENUMLOGFONTEXDVW enumlogfontexdvw;
1347     } object;
1348 
1349     /* Normalize to the largest supported object size */
1350     cjMaxSize = min((UINT)cjBufferSize, sizeof(object));
1351 
1352     /* Now do the actual call */
1353     iResult = GreGetObject(hobj, cjMaxSize, lpBuffer ? &object : NULL);
1354 
1355     /* Check if we have a buffer and data */
1356     if ((lpBuffer != NULL) && (iResult != 0))
1357     {
1358         /* Enter SEH for buffer transfer */
1359         _SEH2_TRY
1360         {
1361             /* Probe the buffer and copy it */
1362             cjMaxSize = min(cjMaxSize, iResult);
1363             ProbeForWrite(lpBuffer, cjMaxSize, sizeof(WORD));
1364             RtlCopyMemory(lpBuffer, &object, cjMaxSize);
1365         }
1366         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1367         {
1368             /* Clear the return value.
1369              * Do *NOT* set last error here! */
1370             iResult = 0;
1371         }
1372         _SEH2_END;
1373     }
1374 
1375     /* Return the count */
1376     return iResult;
1377 }
1378 
1379 W32KAPI
1380 HANDLE
1381 APIENTRY
NtGdiCreateClientObj(IN ULONG ulType)1382 NtGdiCreateClientObj(
1383     IN ULONG ulType)
1384 {
1385     POBJ pObject;
1386     HANDLE handle;
1387 
1388     /* Check if ulType is valid */
1389     if ((ulType != GDILoObjType_LO_METAFILE16_TYPE) &&
1390         (ulType != GDILoObjType_LO_METAFILE_TYPE) &&
1391         (ulType != GDILoObjType_LO_METADC16_TYPE))
1392     {
1393         DPRINT1("NtGdiCreateClientObj: Invalid object type 0x%lx.\n", ulType);
1394         return NULL;
1395     }
1396 
1397     /* Allocate a new object */
1398     pObject = GDIOBJ_AllocateObject(GDIObjType_CLIENTOBJ_TYPE,
1399                                     sizeof(CLIENTOBJ),
1400                                     BASEFLAG_LOOKASIDE);
1401     if (!pObject)
1402     {
1403         DPRINT1("NtGdiCreateClientObj: Could not allocate a clientobj.\n");
1404         return NULL;
1405     }
1406 
1407     /* Set the real object type */
1408     pObject->hHmgr = UlongToHandle(ulType | GDILoObjType_LO_CLIENTOBJ_TYPE);
1409 
1410     /* Create a handle */
1411     handle = GDIOBJ_hInsertObject(pObject, GDI_OBJ_HMGR_POWNED);
1412     if (!handle)
1413     {
1414         DPRINT1("NtGdiCreateClientObj: Could not create a handle.\n");
1415         GDIOBJ_vFreeObject(pObject);
1416         return NULL;
1417     }
1418 
1419     /* Unlock it */
1420     GDIOBJ_vUnlockObject(pObject);
1421 
1422     return handle;
1423 }
1424 
1425 W32KAPI
1426 BOOL
1427 APIENTRY
NtGdiDeleteClientObj(IN HANDLE hobj)1428 NtGdiDeleteClientObj(
1429     IN HANDLE hobj)
1430 {
1431     /* We first need to get the real type from the handle */
1432     ULONG ulType = GDI_HANDLE_GET_TYPE(hobj);
1433 
1434     /* Check if it's really a CLIENTOBJ */
1435     if ((ulType & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1436     {
1437         /* FIXME: SetLastError? */
1438         return FALSE;
1439     }
1440 
1441     return GreDeleteObject(hobj);
1442 }
1443 
1444 
1445 
1446 PGDI_HANDLE_TABLE GdiHandleTable = NULL;
1447 
1448 PGDIOBJ NTAPI
GDIOBJ_ShareLockObj(HGDIOBJ hObj,DWORD ExpectedType)1449 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
1450 {
1451     if (ExpectedType == GDI_OBJECT_TYPE_DONTCARE)
1452         ExpectedType = GDI_HANDLE_GET_TYPE(hObj);
1453     return GDIOBJ_ReferenceObjectByHandle(hObj, (ExpectedType >> 16) & 0x1f);
1454 }
1455 
1456 // This function is not safe to use with concurrent deleting attempts
1457 // That shouldn't be a problem, since we don't have any processes yet,
1458 // that could delete the handle
1459 BOOL
1460 NTAPI
GDIOBJ_ConvertToStockObj(HGDIOBJ * phObj)1461 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1462 {
1463     PENTRY pentry;
1464     POBJ pobj;
1465 
1466     /* Reference the handle entry */
1467     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1468     if (!pentry)
1469     {
1470         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1471         return FALSE;
1472     }
1473 
1474     /* Update the entry */
1475     pentry->FullUnique |= GDI_ENTRY_STOCK_MASK;
1476     pentry->ObjectOwner.ulObj = 0;
1477 
1478     /* Get the pointer to the BASEOBJECT */
1479     pobj = pentry->einfo.pobj;
1480 
1481     /* Calculate the new handle */
1482     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr | GDI_HANDLE_STOCK_MASK);
1483 
1484     /* Return the new handle */
1485     *phObj = pobj->hHmgr;
1486 
1487     /* Dereference the handle */
1488     GDIOBJ_vDereferenceObject(pobj);
1489 
1490     return TRUE;
1491 }
1492 
1493 BOOL
1494 NTAPI
GDIOBJ_ConvertFromStockObj(HGDIOBJ * phObj)1495 GDIOBJ_ConvertFromStockObj(HGDIOBJ *phObj)
1496 {
1497     PENTRY pentry;
1498     POBJ pobj;
1499 
1500     /* Reference the handle entry */
1501     pentry = ENTRY_ReferenceEntryByHandle(*phObj, 0);
1502     if (!pentry)
1503     {
1504         DPRINT1("GDIOBJ: Requested handle 0x%p is not valid.\n", *phObj);
1505         return FALSE;
1506     }
1507 
1508     /* Update the entry */
1509     pentry->FullUnique &= ~GDI_ENTRY_STOCK_MASK;
1510     pentry->ObjectOwner.ulObj = PtrToUlong(PsGetCurrentProcessId());
1511 
1512     /* Get the pointer to the BASEOBJECT */
1513     pobj = pentry->einfo.pobj;
1514 
1515     /* Calculate the new handle */
1516     pobj->hHmgr = (HGDIOBJ)((ULONG_PTR)pobj->hHmgr & ~GDI_HANDLE_STOCK_MASK);
1517 
1518     /* Return the new handle */
1519     *phObj = pobj->hHmgr;
1520 
1521     /* Dereference the handle */
1522     GDIOBJ_vDereferenceObject(pobj);
1523 
1524     return TRUE;
1525 }
1526 
1527 POBJ NTAPI
GDIOBJ_AllocObjWithHandle(ULONG ObjectType,ULONG cjSize)1528 GDIOBJ_AllocObjWithHandle(ULONG ObjectType, ULONG cjSize)
1529 {
1530     POBJ pobj;
1531     FLONG fl = 0;
1532     UCHAR objt = (ObjectType >> 16) & 0xFF;
1533 
1534     if ((objt == GDIObjType_DC_TYPE && cjSize == sizeof(DC)) ||
1535         (objt == GDIObjType_PAL_TYPE && cjSize == sizeof(PALETTE)) ||
1536         (objt == GDIObjType_RGN_TYPE && cjSize == sizeof(REGION)) ||
1537         (objt == GDIObjType_SURF_TYPE && cjSize == sizeof(SURFACE)) ||
1538         (objt == GDIObjType_PATH_TYPE && cjSize == sizeof(PATH)))
1539     {
1540         fl |= BASEFLAG_LOOKASIDE;
1541     }
1542 
1543     pobj = GDIOBJ_AllocateObject(objt, cjSize, fl);
1544     if (!pobj)
1545     {
1546         return NULL;
1547     }
1548 
1549     if (!GDIOBJ_hInsertObject(pobj, GDI_OBJ_HMGR_POWNED))
1550     {
1551         GDIOBJ_vFreeObject(pobj);
1552         return NULL;
1553     }
1554     return pobj;
1555 }
1556 
1557 PVOID NTAPI
GDI_MapHandleTable(PEPROCESS pProcess)1558 GDI_MapHandleTable(PEPROCESS pProcess)
1559 {
1560     PVOID pvMappedView = NULL;
1561     NTSTATUS Status;
1562     LARGE_INTEGER liOffset;
1563     SIZE_T cjViewSize = sizeof(GDI_HANDLE_TABLE);
1564 
1565     liOffset.QuadPart = 0;
1566 
1567     ASSERT(gpvGdiHdlTblSection != NULL);
1568     ASSERT(pProcess != NULL);
1569 
1570     Status = MmMapViewOfSection(gpvGdiHdlTblSection,
1571                                 pProcess,
1572                                 &pvMappedView,
1573                                 0,
1574                                 0,
1575                                 &liOffset,
1576                                 &cjViewSize,
1577                                 ViewUnmap,
1578                                 SEC_NO_CHANGE,
1579                                 PAGE_READONLY);
1580 
1581     if (!NT_SUCCESS(Status))
1582         return NULL;
1583 
1584     return pvMappedView;
1585 }
1586 
1587 BOOL NTAPI
GDI_CleanupForProcess(struct _EPROCESS * Process)1588 GDI_CleanupForProcess(struct _EPROCESS *Process)
1589 {
1590     PENTRY pentry;
1591     ULONG ulIndex;
1592     DWORD dwProcessId;
1593     PPROCESSINFO ppi;
1594 
1595     DPRINT("CleanupForProcess prochandle %p Pid %p\n",
1596            Process, Process->UniqueProcessId);
1597 
1598     ASSERT(Process == PsGetCurrentProcess());
1599 
1600     /* Get the current process Id */
1601     dwProcessId = PtrToUlong(PsGetCurrentProcessId());
1602 
1603     /* Loop all handles in the handle table */
1604     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1605     {
1606         pentry = &gpentHmgr[ulIndex];
1607 
1608         /* Check if the object is owned by the process */
1609         if (pentry->ObjectOwner.ulObj == dwProcessId)
1610         {
1611             ASSERT(pentry->einfo.pobj->cExclusiveLock == 0);
1612 
1613             /* Reference the object and delete it */
1614             InterlockedIncrement((LONG*)&gpaulRefCount[ulIndex]);
1615             GDIOBJ_vDeleteObject(pentry->einfo.pobj);
1616         }
1617     }
1618 
1619 #if DBG
1620     DbgGdiHTIntegrityCheck();
1621 #endif
1622 
1623     ppi = PsGetCurrentProcessWin32Process();
1624     DPRINT("Completed cleanup for process %p\n", Process->UniqueProcessId);
1625     if (ppi->GDIHandleCount != 0)
1626     {
1627         DPRINT1("Leaking %d handles!\n", ppi->GDIHandleCount);
1628         ASSERT(FALSE);
1629     }
1630 
1631     /* Loop all handles in the handle table */
1632     for (ulIndex = RESERVE_ENTRIES_COUNT; ulIndex < gulFirstUnused; ulIndex++)
1633     {
1634         pentry = &gpentHmgr[ulIndex];
1635 
1636         /* Check if the object is owned by the process */
1637         if (pentry->ObjectOwner.ulObj == dwProcessId)
1638         {
1639             DPRINT1("Leaking object. Index=%lx, type=0x%x, refcount=%lx\n",
1640                     ulIndex, pentry->Objt, gpaulRefCount[ulIndex]);
1641             DBG_DUMP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1642             //DBG_CLEANUP_EVENT_LIST(&pentry->einfo.pobj->slhLog);
1643             ASSERT(FALSE);
1644         }
1645     }
1646 
1647     return TRUE;
1648 }
1649 
1650 /// HACK!
1651 PGDI_POOL
GetBrushAttrPool(VOID)1652 GetBrushAttrPool(VOID)
1653 {
1654     PPROCESSINFO ppi;
1655 
1656     ppi = PsGetCurrentProcessWin32Process();
1657     NT_ASSERT(ppi != NULL);
1658 
1659     return ppi->pPoolBrushAttr;
1660 }
1661 
1662 /* EOF */
1663