- Write new simplified NtGdiDeleteObjectApp based on test case.
[reactos.git] / reactos / subsystems / win32 / win32k / objects / gdiobj.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/gdiobj.c
5 * PURPOSE: General GDI object manipulation routines
6 * PROGRAMMERS: ...
7 */
8
9 /** INCLUDES ******************************************************************/
10
11 #include <w32k.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define GDI_ENTRY_TO_INDEX(ht, e) \
16 (((ULONG_PTR)(e) - (ULONG_PTR)&((ht)->Entries[0])) / sizeof(GDI_TABLE_ENTRY))
17 #define GDI_HANDLE_GET_ENTRY(HandleTable, h) \
18 (&(HandleTable)->Entries[GDI_HANDLE_GET_INDEX((h))])
19
20 /* apparently the first 10 entries are never used in windows as they are empty */
21 #define RESERVE_ENTRIES_COUNT 10
22
23 #define BASE_OBJTYPE_COUNT 32
24
25 #define DelayExecution() \
26 DPRINT("%s:%i: Delay\n", __FILE__, __LINE__); \
27 KeDelayExecutionThread(KernelMode, FALSE, &ShortDelay)
28
29 /* static */ /* FIXME: -fno-unit-at-a-time breaks this */
30 BOOL INTERNAL_CALL GDI_CleanupDummy(PVOID ObjectBody);
31
32 /** GLOBALS *******************************************************************/
33
34 typedef struct
35 {
36 BOOL bUseLookaside;
37 ULONG_PTR ulBodySize;
38 ULONG Tag;
39 GDICLEANUPPROC CleanupProc;
40 } OBJ_TYPE_INFO, *POBJ_TYPE_INFO;
41
42 static const
43 OBJ_TYPE_INFO ObjTypeInfo[BASE_OBJTYPE_COUNT] =
44 {
45 {0, 0, 0, NULL}, /* 00 reserved entry */
46 {1, sizeof(DC), TAG_DC, DC_Cleanup}, /* 01 DC */
47 {1, 0, 0, NULL}, /* 02 UNUSED1 */
48 {1, 0, 0, NULL}, /* 03 UNUSED2 */
49 {1, sizeof(ROSRGNDATA), TAG_REGION, REGION_Cleanup}, /* 04 RGN */
50 {1, sizeof(SURFACE), TAG_SURFACE, SURFACE_Cleanup}, /* 05 SURFACE */
51 {1, sizeof(CLIENTOBJ), TAG_CLIENTOBJ, GDI_CleanupDummy}, /* 06 CLIENTOBJ: METADC,... */
52 {1, sizeof(PATH), TAG_PATH, GDI_CleanupDummy}, /* 07 PATH */
53 {1, sizeof(PALGDI), TAG_PALETTE, PALETTE_Cleanup}, /* 08 PAL */
54 {1, sizeof(COLORSPACE), TAG_ICMLCS, GDI_CleanupDummy}, /* 09 ICMLCS, */
55 {1, sizeof(TEXTOBJ), TAG_LFONT, GDI_CleanupDummy}, /* 0a LFONT */
56 {0, 0, TAG_RFONT, NULL}, /* 0b RFONT, unused */
57 {0, 0, TAG_PFE, NULL}, /* 0c PFE, unused */
58 {0, 0, TAG_PFT, NULL}, /* 0d PFT, unused */
59 {0, sizeof(GDICLRXFORM), TAG_ICMCXF, GDI_CleanupDummy}, /* 0e ICMCXF, */
60 {0, 0, TAG_SPRITE, NULL}, /* 0f SPRITE, unused */
61 {1, sizeof(BRUSH), TAG_BRUSH, BRUSH_Cleanup}, /* 10 BRUSH, PEN, EXTPEN */
62 {0, 0, TAG_UMPD, NULL}, /* 11 UMPD, unused */
63 {0, 0, 0, NULL}, /* 12 UNUSED4 */
64 {0, 0, TAG_SPACE, NULL}, /* 13 SPACE, unused */
65 {0, 0, 0, NULL}, /* 14 UNUSED5 */
66 {0, 0, TAG_META, NULL}, /* 15 META, unused */
67 {0, 0, TAG_EFSTATE, NULL}, /* 16 EFSTATE, unused */
68 {0, 0, TAG_BMFD, NULL}, /* 17 BMFD, unused */
69 {0, 0, TAG_VTFD, NULL}, /* 18 VTFD, unused */
70 {0, 0, TAG_TTFD, NULL}, /* 19 TTFD, unused */
71 {0, 0, TAG_RC, NULL}, /* 1a RC, unused */
72 {0, 0, TAG_TEMP, NULL}, /* 1b TEMP, unused */
73 {0, sizeof(EDRIVEROBJ), TAG_DRVOBJ, DRIVEROBJ_Cleanup},/* 1c DRVOBJ */
74 {0, 0, TAG_DCIOBJ, NULL}, /* 1d DCIOBJ, unused */
75 {0, 0, TAG_SPOOL, NULL}, /* 1e SPOOL, unused */
76 {0, 0, 0, NULL}, /* 1f reserved entry */
77 };
78
79 static LARGE_INTEGER ShortDelay;
80
81 /** DEBUGGING *****************************************************************/
82 //#define GDI_DEBUG
83 #include "gdidbg.c"
84
85 /** INTERNAL FUNCTIONS ********************************************************/
86
87 /*
88 * Dummy GDI Cleanup Callback
89 */
90 /* static */ /* FIXME: -fno-unit-at-a-time breaks this */
91 BOOL INTERNAL_CALL
92 GDI_CleanupDummy(PVOID ObjectBody)
93 {
94 return TRUE;
95 }
96
97 /*!
98 * Allocate GDI object table.
99 * \param Size - number of entries in the object table.
100 */
101 PGDI_HANDLE_TABLE INTERNAL_CALL
102 GDIOBJ_iAllocHandleTable(OUT PSECTION_OBJECT *SectionObject)
103 {
104 PGDI_HANDLE_TABLE HandleTable = NULL;
105 LARGE_INTEGER htSize;
106 UINT ObjType;
107 ULONG ViewSize = 0;
108 NTSTATUS Status;
109
110 ASSERT(SectionObject != NULL);
111
112 htSize.QuadPart = sizeof(GDI_HANDLE_TABLE);
113
114 Status = MmCreateSection((PVOID*)SectionObject,
115 SECTION_ALL_ACCESS,
116 NULL,
117 &htSize,
118 PAGE_READWRITE,
119 SEC_COMMIT,
120 NULL,
121 NULL);
122 if (!NT_SUCCESS(Status))
123 return NULL;
124
125 /* FIXME - use MmMapViewInSessionSpace once available! */
126 Status = MmMapViewInSystemSpace(*SectionObject,
127 (PVOID*)&HandleTable,
128 &ViewSize);
129 if (!NT_SUCCESS(Status))
130 {
131 ObDereferenceObject(*SectionObject);
132 *SectionObject = NULL;
133 return NULL;
134 }
135
136 RtlZeroMemory(HandleTable, sizeof(GDI_HANDLE_TABLE));
137
138 HandleTable->LookasideLists = ExAllocatePoolWithTag(NonPagedPool,
139 BASE_OBJTYPE_COUNT * sizeof(PAGED_LOOKASIDE_LIST),
140 TAG_GDIHNDTBLE);
141 if (HandleTable->LookasideLists == NULL)
142 {
143 MmUnmapViewInSystemSpace(HandleTable);
144 ObDereferenceObject(*SectionObject);
145 *SectionObject = NULL;
146 return NULL;
147 }
148
149 for (ObjType = 0; ObjType < BASE_OBJTYPE_COUNT; ObjType++)
150 {
151 if (ObjTypeInfo[ObjType].bUseLookaside)
152 {
153 ExInitializePagedLookasideList(HandleTable->LookasideLists + ObjType,
154 NULL,
155 NULL,
156 0,
157 ObjTypeInfo[ObjType].ulBodySize,
158 ObjTypeInfo[ObjType].Tag,
159 0);
160 }
161 }
162
163 ShortDelay.QuadPart = -5000LL; /* FIXME - 0.5 ms? */
164
165 HandleTable->FirstFree = 0;
166 HandleTable->FirstUnused = RESERVE_ENTRIES_COUNT;
167
168 return HandleTable;
169 }
170
171 static void FASTCALL
172 LockErrorDebugOutput(HGDIOBJ hObj, PGDI_TABLE_ENTRY Entry, LPSTR Function)
173 {
174 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0)
175 {
176 DPRINT1("%s: Attempted to lock object 0x%x that is deleted!\n", Function, hObj);
177 GDIDBG_TRACEDELETER(hObj);
178 }
179 else if (GDI_HANDLE_GET_REUSECNT(hObj) != GDI_ENTRY_GET_REUSECNT(Entry->Type))
180 {
181 DPRINT1("%s: Attempted to lock object 0x%x, wrong reuse counter (Handle: 0x%x, Entry: 0x%x)\n",
182 Function, hObj, GDI_HANDLE_GET_REUSECNT(hObj), GDI_ENTRY_GET_REUSECNT(Entry->Type));
183 }
184 else if (GDI_HANDLE_GET_TYPE(hObj) != ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK))
185 {
186 DPRINT1("%s: Attempted to lock object 0x%x, type mismatch (Handle: 0x%x, Entry: 0x%x)\n",
187 Function, hObj, GDI_HANDLE_GET_TYPE(hObj), (Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK);
188 }
189 else
190 {
191 DPRINT1("%s: Attempted to lock object 0x%x, something went wrong, typeinfo = 0x%x\n",
192 Function, hObj, Entry->Type);
193 }
194 GDIDBG_TRACECALLER();
195 }
196
197 ULONG
198 FASTCALL
199 InterlockedPopFreeEntry()
200 {
201 ULONG idxFirst, idxNext, idxPrev;
202 PGDI_TABLE_ENTRY pEntry;
203 DWORD PrevProcId;
204
205 DPRINT("Enter InterLockedPopFreeEntry\n");
206
207 while (TRUE)
208 {
209 idxFirst = GdiHandleTable->FirstFree;
210
211 if (!idxFirst)
212 {
213 /* Increment FirstUnused and get the new index */
214 idxFirst = InterlockedIncrement((LONG*)&GdiHandleTable->FirstUnused) - 1;
215
216 /* Check if we have entries left */
217 if (idxFirst >= GDI_HANDLE_COUNT)
218 {
219 DPRINT1("No more gdi handles left!\n");
220 return 0;
221 }
222
223 /* Return the old index */
224 return idxFirst;
225 }
226
227 /* Get a pointer to the first free entry */
228 pEntry = GdiHandleTable->Entries + idxFirst;
229
230 /* Try to lock the entry */
231 PrevProcId = InterlockedCompareExchange((LONG*)&pEntry->ProcessId, 1, 0);
232 if (PrevProcId != 0)
233 {
234 /* The entry was locked or not free, wait and start over */
235 DelayExecution();
236 continue;
237 }
238
239 /* Sanity check: is entry really free? */
240 ASSERT(((ULONG_PTR)pEntry->KernelData & ~GDI_HANDLE_INDEX_MASK) == 0);
241
242 /* Try to exchange the FirstFree value */
243 idxNext = (ULONG_PTR)pEntry->KernelData;
244 idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
245 idxNext,
246 idxFirst);
247
248 /* Unlock the free entry */
249 (void)InterlockedExchange((LONG*)&pEntry->ProcessId, 0);
250
251 /* If we succeeded, break out of the loop */
252 if (idxPrev == idxFirst)
253 {
254 break;
255 }
256 }
257
258 return idxFirst;
259 }
260
261 /* Pushes an entry of the handle table to the free list,
262 The entry must be unlocked and the base type field must be 0 */
263 VOID
264 FASTCALL
265 InterlockedPushFreeEntry(ULONG idxToFree)
266 {
267 ULONG idxFirstFree, idxPrev;
268 PGDI_TABLE_ENTRY pFreeEntry;
269
270 DPRINT("Enter InterlockedPushFreeEntry\n");
271
272 pFreeEntry = GdiHandleTable->Entries + idxToFree;
273 ASSERT((pFreeEntry->Type & GDI_ENTRY_BASETYPE_MASK) == 0);
274 ASSERT(pFreeEntry->ProcessId == 0);
275 pFreeEntry->UserData = NULL;
276
277 do
278 {
279 idxFirstFree = GdiHandleTable->FirstFree;
280 pFreeEntry->KernelData = (PVOID)(ULONG_PTR)idxFirstFree;
281
282 idxPrev = InterlockedCompareExchange((LONG*)&GdiHandleTable->FirstFree,
283 idxToFree,
284 idxFirstFree);
285 }
286 while (idxPrev != idxFirstFree);
287 }
288
289
290 BOOL
291 INTERNAL_CALL
292 GDIOBJ_ValidateHandle(HGDIOBJ hObj, ULONG ObjectType)
293 {
294 PGDI_TABLE_ENTRY Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
295 if ((((ULONG_PTR)hObj & GDI_HANDLE_TYPE_MASK) == ObjectType) &&
296 (Entry->Type << GDI_ENTRY_UPPER_SHIFT) == GDI_HANDLE_GET_UPPER(hObj))
297 {
298 HANDLE pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
299 if (pid == NULL || pid == PsGetCurrentProcessId())
300 {
301 return TRUE;
302 }
303 }
304 return FALSE;
305 }
306
307 POBJ INTERNAL_CALL
308 GDIOBJ_AllocObj(UCHAR BaseType)
309 {
310 POBJ pObject;
311
312 ASSERT((BaseType & ~GDIObjTypeTotal) == 0);
313 // BaseType &= GDI_HANDLE_BASETYPE_MASK;
314
315 if (ObjTypeInfo[BaseType].bUseLookaside)
316 {
317 PPAGED_LOOKASIDE_LIST LookasideList;
318
319 LookasideList = GdiHandleTable->LookasideLists + BaseType;
320 pObject = ExAllocateFromPagedLookasideList(LookasideList);
321 }
322 else
323 {
324 pObject = ExAllocatePoolWithTag(PagedPool,
325 ObjTypeInfo[BaseType].ulBodySize,
326 ObjTypeInfo[BaseType].Tag);
327 }
328
329 if (pObject)
330 {
331 RtlZeroMemory(pObject, ObjTypeInfo[BaseType].ulBodySize);
332 }
333
334 return pObject;
335 }
336
337
338 /*!
339 * Allocate memory for GDI object and return handle to it.
340 *
341 * \param ObjectType - type of object \ref GDI object types
342 *
343 * \return Pointer to the allocated object, which is locked.
344 */
345 POBJ INTERNAL_CALL
346 GDIOBJ_AllocObjWithHandle(ULONG ObjectType)
347 {
348 PW32PROCESS W32Process;
349 POBJ newObject = NULL;
350 HANDLE CurrentProcessId, LockedProcessId;
351 UCHAR TypeIndex;
352
353 GDIDBG_INITLOOPTRACE();
354
355 W32Process = PsGetCurrentProcessWin32Process();
356 /* HACK HACK HACK: simplest-possible quota implementation - don't allow a process
357 to take too many GDI objects, itself. */
358 if (W32Process && W32Process->GDIObjects >= 0x2710)
359 {
360 DPRINT1("Too many objects for process!!!\n");
361 GDIDBG_DUMPHANDLETABLE();
362 return NULL;
363 }
364
365 ASSERT(ObjectType != GDI_OBJECT_TYPE_DONTCARE);
366
367 TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(ObjectType);
368
369 newObject = GDIOBJ_AllocObj(TypeIndex);
370 if (!newObject)
371 {
372 DPRINT1("Not enough memory to allocate gdi object!\n");
373 return NULL;
374 }
375
376 UINT Index;
377 PGDI_TABLE_ENTRY Entry;
378 LONG TypeInfo;
379
380 CurrentProcessId = PsGetCurrentProcessId();
381 LockedProcessId = (HANDLE)((ULONG_PTR)CurrentProcessId | 0x1);
382
383 // RtlZeroMemory(newObject, ObjTypeInfo[TypeIndex].ulBodySize);
384
385 /* On Windows the higher 16 bit of the type field don't contain the
386 full type from the handle, but the base type.
387 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
388 TypeInfo = (ObjectType & GDI_HANDLE_BASETYPE_MASK) | (ObjectType >> GDI_ENTRY_UPPER_SHIFT);
389
390 Index = InterlockedPopFreeEntry();
391 if (Index != 0)
392 {
393 HANDLE PrevProcId;
394
395 Entry = &GdiHandleTable->Entries[Index];
396
397 LockHandle:
398 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, 0);
399 if (PrevProcId == NULL)
400 {
401 PW32THREAD Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
402 HGDIOBJ Handle;
403
404 Entry->KernelData = newObject;
405
406 /* copy the reuse-counter */
407 TypeInfo |= Entry->Type & GDI_ENTRY_REUSE_MASK;
408
409 /* we found a free entry, no need to exchange this field atomically
410 since we're holding the lock */
411 Entry->Type = TypeInfo;
412
413 /* Create a handle */
414 Handle = (HGDIOBJ)((Index & 0xFFFF) | (TypeInfo << GDI_ENTRY_UPPER_SHIFT));
415
416 /* Initialize BaseObject fields */
417 newObject->hHmgr = Handle;
418 newObject->ulShareCount = 0;
419 newObject->cExclusiveLock = 1;
420 newObject->Tid = Thread;
421
422 /* unlock the entry */
423 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, CurrentProcessId);
424
425 GDIDBG_CAPTUREALLOCATOR(Index);
426
427 if (W32Process != NULL)
428 {
429 InterlockedIncrement(&W32Process->GDIObjects);
430 }
431
432 DPRINT("GDIOBJ_AllocObj: 0x%x ob: 0x%x\n", Handle, newObject);
433 return newObject;
434 }
435 else
436 {
437 GDIDBG_TRACELOOP(Index, PrevProcId, CurrentProcessId);
438 /* damn, someone is trying to lock the object even though it doesn't
439 even exist anymore, wait a little and try again!
440 FIXME - we shouldn't loop forever! Give up after some time! */
441 DelayExecution();
442 /* try again */
443 goto LockHandle;
444 }
445 }
446
447 GDIOBJ_FreeObj(newObject, TypeIndex);
448
449 DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
450 GDIDBG_DUMPHANDLETABLE();
451
452 return NULL;
453 }
454
455
456 VOID INTERNAL_CALL
457 GDIOBJ_FreeObj(POBJ pObject, UCHAR BaseType)
458 {
459 /* Object must not have a handle! */
460 ASSERT(pObject->hHmgr == NULL);
461
462 if (ObjTypeInfo[BaseType].bUseLookaside)
463 {
464 PPAGED_LOOKASIDE_LIST LookasideList;
465
466 LookasideList = GdiHandleTable->LookasideLists + BaseType;
467 ExFreeToPagedLookasideList(LookasideList, pObject);
468 }
469 else
470 {
471 ExFreePool(pObject);
472 }
473 }
474
475 /*!
476 * Free memory allocated for the GDI object. For each object type this function calls the
477 * appropriate cleanup routine.
478 *
479 * \param hObj - handle of the object to be deleted.
480 *
481 * \return Returns TRUE if succesful.
482 * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
483 * to the calling process.
484 *
485 * \bug This function should return VOID and kill the object no matter what...
486 */
487 BOOL INTERNAL_CALL
488 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType)
489 {
490 PGDI_TABLE_ENTRY Entry;
491 HANDLE ProcessId, LockedProcessId, PrevProcId;
492 ULONG HandleType, HandleUpper, TypeIndex;
493 BOOL Silent;
494
495 GDIDBG_INITLOOPTRACE();
496
497 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
498
499 if (GDI_HANDLE_IS_STOCKOBJ(hObj))
500 {
501 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
502 GDIDBG_TRACECALLER();
503 return FALSE;
504 }
505
506 ProcessId = PsGetCurrentProcessId();
507 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
508
509 Silent = (ExpectedType & GDI_OBJECT_TYPE_SILENT);
510 ExpectedType &= ~GDI_OBJECT_TYPE_SILENT;
511
512 HandleType = GDI_HANDLE_GET_TYPE(hObj);
513 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
514
515 /* Check if we have the requested type */
516 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
517 HandleType != ExpectedType) ||
518 HandleType == 0 )
519 {
520 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
521 hObj, HandleType, ExpectedType);
522 GDIDBG_TRACECALLER();
523 return FALSE;
524 }
525
526 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
527
528 LockHandle:
529 /* lock the object, we must not delete global objects, so don't exchange the locking
530 process ID to zero when attempting to lock a global object... */
531 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
532 if (PrevProcId == ProcessId)
533 {
534 if ( (Entry->KernelData != NULL) &&
535 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) &&
536 ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
537 {
538 POBJ Object;
539
540 Object = Entry->KernelData;
541
542 if ((Object->cExclusiveLock == 0 ||
543 Object->Tid == (PW32THREAD)PsGetCurrentThreadWin32Thread()) &&
544 Object->ulShareCount == 0)
545 {
546 BOOL Ret;
547 PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
548
549 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
550 Entry->Type = (Entry->Type + GDI_ENTRY_REUSE_INC) & ~GDI_ENTRY_BASETYPE_MASK;
551
552 /* unlock the handle slot */
553 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
554
555 /* push this entry to the free list */
556 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
557
558 Object->hHmgr = NULL;
559
560 if (W32Process != NULL)
561 {
562 InterlockedDecrement(&W32Process->GDIObjects);
563 }
564
565 /* call the cleanup routine. */
566 TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(HandleType);
567 Ret = ObjTypeInfo[TypeIndex].CleanupProc(Object);
568
569 /* Now it's time to free the memory */
570 GDIOBJ_FreeObj(Object, TypeIndex);
571
572 GDIDBG_CAPTUREDELETER(hObj);
573 return Ret;
574 }
575 else if (Object->ulShareCount != 0)
576 {
577 DPRINT("Object %p, ulShareCount = %d\n", Object->hHmgr, Object->ulShareCount);
578 GDIDBG_TRACECALLER();
579 GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
580 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
581 /* Don't wait on shared locks */
582 return FALSE;
583 }
584 else
585 {
586 /*
587 * The object is currently locked by another thread, so freeing is forbidden!
588 */
589 DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock);
590 GDIDBG_TRACECALLER();
591 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj));
592 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
593 /* do not assert here for it will call again from dxg.sys it being call twice */
594
595 DelayExecution();
596 goto LockHandle;
597 }
598 }
599 else
600 {
601 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj");
602 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
603 }
604 }
605 else if (PrevProcId == LockedProcessId)
606 {
607 GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
608
609 /* the object is currently locked, wait some time and try again.
610 FIXME - we shouldn't loop forever! Give up after some time! */
611 DelayExecution();
612 /* try again */
613 goto LockHandle;
614 }
615 else
616 {
617 if (!Silent)
618 {
619 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0)
620 {
621 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj);
622 }
623 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
624 {
625 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj);
626 }
627 else
628 {
629 DPRINT1("Attempted to free foreign handle: 0x%x Owner: 0x%x from Caller: 0x%x\n", hObj, (ULONG_PTR)PrevProcId & ~0x1, (ULONG_PTR)ProcessId & ~0x1);
630 }
631 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry->Type, Entry->KernelData, Entry->ProcessId);
632 GDIDBG_TRACECALLER();
633 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
634 }
635 }
636
637 return FALSE;
638 }
639
640 BOOL
641 FASTCALL
642 IsObjectDead(HGDIOBJ hObject)
643 {
644 INT Index = GDI_HANDLE_GET_INDEX(hObject);
645 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
646 // We check to see if the objects are knocking on deaths door.
647 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
648 return FALSE;
649 else
650 {
651 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject);
652 return TRUE; // return true and move on.
653 }
654 }
655
656
657 /*!
658 * Delete GDI object
659 * \param hObject object handle
660 * \return if the function fails the returned value is FALSE.
661 */
662 BOOL
663 FASTCALL
664 NtGdiDeleteObject(HGDIOBJ hObject)
665 {
666 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
667 if (!IsObjectDead(hObject))
668 {
669 return NULL != hObject
670 ? GDIOBJ_FreeObjByHandle(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
671 }
672 else
673 {
674 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject);
675 return TRUE; // return true and move on.
676 }
677 }
678
679 VOID
680 FASTCALL
681 IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType)
682 {
683 PGDI_TABLE_ENTRY Entry, End;
684 ULONG Index = RESERVE_ENTRIES_COUNT;
685 HANDLE ProcId;
686 PW32PROCESS W32Process;
687
688 W32Process = (PW32PROCESS)Process->Win32Process;
689 ASSERT(W32Process);
690
691 if (W32Process->GDIObjects > 0)
692 {
693 ProcId = Process->UniqueProcessId;
694
695 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
696 we should delete it directly here! */
697
698 End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT];
699 for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT];
700 Entry != End;
701 Entry++, Index++)
702 {
703 /* ignore the lock bit */
704 if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId)
705 {
706 if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) == ObjectType ||
707 ObjectType == GDI_OBJECT_TYPE_DONTCARE)
708 {
709 HGDIOBJ ObjectHandle;
710
711 /* Create the object handle for the entry, the lower(!) 16 bit of the
712 Type field includes the type of the object including the stock
713 object flag - but since stock objects don't have a process id we can
714 simply ignore this fact here. */
715 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
716
717 if (!GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE))
718 {
719 DPRINT1("Failed to delete object %p!\n", ObjectHandle);
720 }
721
722 if (W32Process->GDIObjects == 0)
723 {
724 /* there are no more gdi handles for this process, bail */
725 break;
726 }
727 }
728 }
729 }
730 }
731 }
732
733
734 /*!
735 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
736 * \param Process - PID of the process that will be destroyed.
737 */
738 BOOL INTERNAL_CALL
739 GDI_CleanupForProcess(struct _EPROCESS *Process)
740 {
741 PEPROCESS CurrentProcess;
742 PW32PROCESS W32Process;
743
744 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
745 CurrentProcess = PsGetCurrentProcess();
746 if (CurrentProcess != Process)
747 {
748 KeAttachProcess(&Process->Pcb);
749 }
750
751 W32Process = (PW32PROCESS)CurrentProcess->Win32Process;
752
753 /* Delete objects. Begin with types that are not referenced by other types */
754 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE);
755 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE);
756 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BITMAP_TYPE);
757
758 /* Finally finish with what's left */
759 IntDeleteHandlesForProcess(Process, GDI_OBJECT_TYPE_DONTCARE);
760
761 if (CurrentProcess != Process)
762 {
763 KeDetachProcess();
764 }
765
766 #ifdef GDI_DEBUG
767 GdiDbgHTIntegrityCheck();
768 #endif
769
770 DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
771 if (W32Process->GDIObjects > 0)
772 {
773 DPRINT1("Leaking %d handles!\n", W32Process->GDIObjects);
774 }
775
776 return TRUE;
777 }
778
779 /*!
780 * Return pointer to the object by handle.
781 *
782 * \param hObj Object handle
783 * \return Pointer to the object.
784 *
785 * \note Process can only get pointer to the objects it created or global objects.
786 *
787 * \todo Get rid of the ExpectedType parameter!
788 */
789 PGDIOBJ INTERNAL_CALL
790 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
791 {
792 ULONG HandleIndex;
793 PGDI_TABLE_ENTRY Entry;
794 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
795 POBJ Object = NULL;
796 ULONG HandleType, HandleUpper;
797
798 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
799 HandleType = GDI_HANDLE_GET_TYPE(hObj);
800 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
801
802 /* Check that the handle index is valid. */
803 if (HandleIndex >= GDI_HANDLE_COUNT)
804 return NULL;
805
806 Entry = &GdiHandleTable->Entries[HandleIndex];
807
808 /* Check if we have the requested type */
809 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
810 HandleType != ExpectedType) ||
811 HandleType == 0 )
812 {
813 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
814 hObj, HandleType, ExpectedType);
815 GDIDBG_TRACECALLER();
816 GDIDBG_TRACEALLOCATOR(hObj);
817 GDIDBG_TRACEDELETER(hObj);
818 return NULL;
819 }
820
821 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
822 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
823
824 /* Check for invalid owner. */
825 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
826 {
827 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
828 GDIDBG_TRACECALLER();
829 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
830 return NULL;
831 }
832
833 /*
834 * Prevent the thread from being terminated during the locking process.
835 * It would result in undesired effects and inconsistency of the global
836 * handle table.
837 */
838
839 KeEnterCriticalRegion();
840
841 /*
842 * Loop until we either successfully lock the handle entry & object or
843 * fail some of the check.
844 */
845
846 for (;;)
847 {
848 /* Lock the handle table entry. */
849 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
850 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
851 LockedProcessId,
852 HandleProcessId);
853
854 if (PrevProcId == HandleProcessId)
855 {
856 /*
857 * We're locking an object that belongs to our process or it's a
858 * global object if HandleProcessId is 0 here.
859 */
860
861 if ( (Entry->KernelData != NULL) &&
862 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) )
863 {
864 PW32THREAD Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
865 Object = Entry->KernelData;
866
867 if (Object->cExclusiveLock == 0)
868 {
869 Object->Tid = Thread;
870 Object->cExclusiveLock = 1;
871 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
872 }
873 else
874 {
875 if (Object->Tid != Thread)
876 {
877 /* Unlock the handle table entry. */
878 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
879
880 DelayExecution();
881 continue;
882 }
883 InterlockedIncrement((PLONG)&Object->cExclusiveLock);
884 }
885 }
886 else
887 {
888 /*
889 * Debugging code. Report attempts to lock deleted handles and
890 * locking type mismatches.
891 */
892 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj");
893 }
894
895 /* Unlock the handle table entry. */
896 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
897
898 break;
899 }
900 else
901 {
902 /*
903 * The handle is currently locked, wait some time and try again.
904 */
905
906 DelayExecution();
907 continue;
908 }
909 }
910
911 KeLeaveCriticalRegion();
912
913 return Object;
914 }
915
916
917 /*!
918 * Return pointer to the object by handle (and allow sharing of the handle
919 * across threads).
920 *
921 * \param hObj Object handle
922 * \return Pointer to the object.
923 *
924 * \note Process can only get pointer to the objects it created or global objects.
925 *
926 * \todo Get rid of the ExpectedType parameter!
927 */
928 PGDIOBJ INTERNAL_CALL
929 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
930 {
931 ULONG HandleIndex;
932 PGDI_TABLE_ENTRY Entry;
933 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
934 POBJ Object = NULL;
935 ULONG_PTR HandleType, HandleUpper;
936
937 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
938 HandleType = GDI_HANDLE_GET_TYPE(hObj);
939 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
940
941 /* Check that the handle index is valid. */
942 if (HandleIndex >= GDI_HANDLE_COUNT)
943 return NULL;
944
945 /* Check if we have the requested type */
946 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
947 HandleType != ExpectedType) ||
948 HandleType == 0 )
949 {
950 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
951 hObj, HandleType, ExpectedType);
952 return NULL;
953 }
954
955 Entry = &GdiHandleTable->Entries[HandleIndex];
956
957 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
958 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
959
960 /* Check for invalid owner. */
961 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
962 {
963 return NULL;
964 }
965
966 /*
967 * Prevent the thread from being terminated during the locking process.
968 * It would result in undesired effects and inconsistency of the global
969 * handle table.
970 */
971
972 KeEnterCriticalRegion();
973
974 /*
975 * Loop until we either successfully lock the handle entry & object or
976 * fail some of the check.
977 */
978
979 for (;;)
980 {
981 /* Lock the handle table entry. */
982 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
983 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
984 LockedProcessId,
985 HandleProcessId);
986
987 if (PrevProcId == HandleProcessId)
988 {
989 /*
990 * We're locking an object that belongs to our process or it's a
991 * global object if HandleProcessId is 0 here.
992 */
993
994 if ( (Entry->KernelData != NULL) &&
995 (HandleUpper == (Entry->Type << GDI_ENTRY_UPPER_SHIFT)) )
996 {
997 Object = (POBJ)Entry->KernelData;
998
999 GDIDBG_CAPTURESHARELOCKER(HandleIndex);
1000 #ifdef GDI_DEBUG3
1001 if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
1002 {
1003 memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
1004 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleShareLocker[HandleIndex], NULL);
1005 }
1006 #else
1007 InterlockedIncrement((PLONG)&Object->ulShareCount);
1008 #endif
1009 }
1010 else
1011 {
1012 /*
1013 * Debugging code. Report attempts to lock deleted handles and
1014 * locking type mismatches.
1015 */
1016 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj");
1017 }
1018
1019 /* Unlock the handle table entry. */
1020 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1021
1022 break;
1023 }
1024 else
1025 {
1026 /*
1027 * The handle is currently locked, wait some time and try again.
1028 */
1029
1030 DelayExecution();
1031 continue;
1032 }
1033 }
1034
1035 KeLeaveCriticalRegion();
1036
1037 return Object;
1038 }
1039
1040 BOOL INTERNAL_CALL
1041 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1042 {
1043 PGDI_TABLE_ENTRY Entry;
1044 HANDLE ProcessId;
1045 BOOL Ret;
1046
1047 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1048
1049 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1050 {
1051 ProcessId = PsGetCurrentProcessId();
1052
1053 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1054 Ret = Entry->KernelData != NULL &&
1055 (Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 &&
1056 (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
1057
1058 return Ret;
1059 }
1060
1061 return FALSE;
1062 }
1063
1064 BOOL INTERNAL_CALL
1065 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1066 {
1067 /*
1068 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1069 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1070 */
1071 PGDI_TABLE_ENTRY Entry;
1072 HANDLE ProcessId, LockedProcessId, PrevProcId;
1073 PW32THREAD Thread;
1074 HGDIOBJ hObj;
1075
1076 GDIDBG_INITLOOPTRACE();
1077
1078 ASSERT(phObj);
1079 hObj = *phObj;
1080
1081 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj);
1082
1083 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1084
1085 if (!GDI_HANDLE_IS_STOCKOBJ(hObj))
1086 {
1087 ProcessId = PsGetCurrentProcessId();
1088 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1089
1090 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
1091
1092 LockHandle:
1093 /* lock the object, we must not convert stock objects, so don't check!!! */
1094 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
1095 if (PrevProcId == ProcessId)
1096 {
1097 LONG NewType, PrevType, OldType;
1098
1099 /* we're locking an object that belongs to our process. First calculate
1100 the new object type including the stock object flag and then try to
1101 exchange it.*/
1102 /* On Windows the higher 16 bit of the type field don't contain the
1103 full type from the handle, but the base type.
1104 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1105 OldType = ((ULONG)hObj & GDI_HANDLE_BASETYPE_MASK) | ((ULONG)hObj >> GDI_ENTRY_UPPER_SHIFT);
1106 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1107 we copy them as we can't get them from the handle */
1108 OldType |= Entry->Type & GDI_ENTRY_FLAGS_MASK;
1109
1110 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1111 NewType = OldType | GDI_ENTRY_STOCK_MASK;
1112
1113 /* Try to exchange the type field - but only if the old (previous type) matches! */
1114 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1115 if (PrevType == OldType && Entry->KernelData != NULL)
1116 {
1117 PW32THREAD PrevThread;
1118 POBJ Object;
1119
1120 /* We successfully set the stock object flag.
1121 KernelData should never be NULL here!!! */
1122 ASSERT(Entry->KernelData);
1123
1124 Object = Entry->KernelData;
1125
1126 PrevThread = Object->Tid;
1127 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1128 {
1129 /* dereference the process' object counter */
1130 if (PrevProcId != GDI_GLOBAL_PROCESS)
1131 {
1132 PEPROCESS OldProcess;
1133 PW32PROCESS W32Process;
1134 NTSTATUS Status;
1135
1136 /* FIXME */
1137 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1138 if (NT_SUCCESS(Status))
1139 {
1140 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1141 if (W32Process != NULL)
1142 {
1143 InterlockedDecrement(&W32Process->GDIObjects);
1144 }
1145 ObDereferenceObject(OldProcess);
1146 }
1147 }
1148
1149 hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK);
1150 *phObj = hObj;
1151 Object->hHmgr = hObj;
1152
1153 /* remove the process id lock and make it global */
1154 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1155
1156 /* we're done, successfully converted the object */
1157 return TRUE;
1158 }
1159 else
1160 {
1161 GDIDBG_TRACELOOP(hObj, PrevThread, Thread);
1162
1163 /* WTF?! The object is already locked by a different thread!
1164 Release the lock, wait a bit and try again!
1165 FIXME - we should give up after some time unless we want to wait forever! */
1166 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1167
1168 DelayExecution();
1169 goto LockHandle;
1170 }
1171 }
1172 else
1173 {
1174 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1175 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType, Entry->Type, NewType, Entry->KernelData);
1176 }
1177 }
1178 else if (PrevProcId == LockedProcessId)
1179 {
1180 GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
1181
1182 /* the object is currently locked, wait some time and try again.
1183 FIXME - we shouldn't loop forever! Give up after some time! */
1184 DelayExecution();
1185 /* try again */
1186 goto LockHandle;
1187 }
1188 else
1189 {
1190 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1191 }
1192 }
1193
1194 return FALSE;
1195 }
1196
1197 BOOL INTERNAL_CALL
1198 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1199 {
1200 PGDI_TABLE_ENTRY Entry;
1201 HANDLE ProcessId, LockedProcessId, PrevProcId;
1202 PW32THREAD Thread;
1203 BOOL Ret = TRUE;
1204
1205 GDIDBG_INITLOOPTRACE();
1206
1207 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1208
1209 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1210
1211 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1212 {
1213 ProcessId = PsGetCurrentProcessId();
1214 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1215
1216 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1217
1218 LockHandle:
1219 /* lock the object, we must not convert stock objects, so don't check!!! */
1220 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId);
1221 if (PrevProcId == ProcessId)
1222 {
1223 PW32THREAD PrevThread;
1224
1225 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1226 {
1227 POBJ Object = Entry->KernelData;
1228
1229 PrevThread = Object->Tid;
1230 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1231 {
1232 PEPROCESS OldProcess;
1233 PW32PROCESS W32Process;
1234 NTSTATUS Status;
1235
1236 /* dereference the process' object counter */
1237 /* FIXME */
1238 if ((ULONG_PTR)PrevProcId & ~0x1)
1239 {
1240 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1241 if (NT_SUCCESS(Status))
1242 {
1243 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1244 if (W32Process != NULL)
1245 {
1246 InterlockedDecrement(&W32Process->GDIObjects);
1247 }
1248 ObDereferenceObject(OldProcess);
1249 }
1250 }
1251
1252 if (NewOwner != NULL)
1253 {
1254 ProcessId = PsGetProcessId(NewOwner);
1255
1256 /* Increase the new process' object counter */
1257 W32Process = (PW32PROCESS)NewOwner->Win32Process;
1258 if (W32Process != NULL)
1259 {
1260 InterlockedIncrement(&W32Process->GDIObjects);
1261 }
1262 }
1263 else
1264 ProcessId = 0;
1265
1266 /* remove the process id lock and change it to the new process id */
1267 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
1268
1269 /* we're done! */
1270 return Ret;
1271 }
1272 else
1273 {
1274 GDIDBG_TRACELOOP(ObjectHandle, PrevThread, Thread);
1275
1276 /* WTF?! The object is already locked by a different thread!
1277 Release the lock, wait a bit and try again! DO reset the pid lock
1278 so we make sure we don't access invalid memory in case the object is
1279 being deleted in the meantime (because we don't have aquired a reference
1280 at this point).
1281 FIXME - we should give up after some time unless we want to wait forever! */
1282 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1283
1284 DelayExecution();
1285 goto LockHandle;
1286 }
1287 }
1288 else
1289 {
1290 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1291 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry->Type, Entry->KernelData);
1292 Ret = FALSE;
1293 }
1294 }
1295 else if (PrevProcId == LockedProcessId)
1296 {
1297 GDIDBG_TRACELOOP(ObjectHandle, PrevProcId, ProcessId);
1298
1299 /* the object is currently locked, wait some time and try again.
1300 FIXME - we shouldn't loop forever! Give up after some time! */
1301 DelayExecution();
1302 /* try again */
1303 goto LockHandle;
1304 }
1305 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
1306 {
1307 /* allow changing ownership of global objects */
1308 ProcessId = NULL;
1309 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1310 goto LockHandle;
1311 }
1312 else if ((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId())
1313 {
1314 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId());
1315 Ret = FALSE;
1316 }
1317 else
1318 {
1319 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1320 Ret = FALSE;
1321 }
1322 }
1323 return Ret;
1324 }
1325
1326 BOOL INTERNAL_CALL
1327 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1328 {
1329 PGDI_TABLE_ENTRY FromEntry;
1330 PW32THREAD Thread;
1331 HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
1332 BOOL Ret = TRUE;
1333
1334 GDIDBG_INITLOOPTRACE();
1335
1336 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1337
1338 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1339
1340 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1341 {
1342 FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom);
1343
1344 FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
1345 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1346
1347 LockHandleFrom:
1348 /* lock the object, we must not convert stock objects, so don't check!!! */
1349 FromPrevProcId = InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1350 if (FromPrevProcId == FromProcessId)
1351 {
1352 PW32THREAD PrevThread;
1353 POBJ Object;
1354
1355 if ((FromEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1356 {
1357 Object = FromEntry->KernelData;
1358
1359 /* save the pointer to the calling thread so we know it was this thread
1360 that locked the object */
1361 PrevThread = Object->Tid;
1362 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1363 {
1364 /* now let's change the ownership of the target object */
1365
1366 if (((ULONG_PTR)FromPrevProcId & ~0x1) != 0)
1367 {
1368 PEPROCESS ProcessTo;
1369 /* FIXME */
1370 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo)))
1371 {
1372 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1373 ObDereferenceObject(ProcessTo);
1374 }
1375 }
1376 else
1377 {
1378 /* mark the object as global */
1379 GDIOBJ_SetOwnership(CopyTo, NULL);
1380 }
1381
1382 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1383 }
1384 else
1385 {
1386 GDIDBG_TRACELOOP(CopyFrom, PrevThread, Thread);
1387
1388 /* WTF?! The object is already locked by a different thread!
1389 Release the lock, wait a bit and try again! DO reset the pid lock
1390 so we make sure we don't access invalid memory in case the object is
1391 being deleted in the meantime (because we don't have aquired a reference
1392 at this point).
1393 FIXME - we should give up after some time unless we want to wait forever! */
1394 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1395
1396 DelayExecution();
1397 goto LockHandleFrom;
1398 }
1399 }
1400 else
1401 {
1402 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1403 Ret = FALSE;
1404 }
1405 }
1406 else if (FromPrevProcId == FromLockedProcessId)
1407 {
1408 GDIDBG_TRACELOOP(CopyFrom, FromPrevProcId, FromProcessId);
1409
1410 /* the object is currently locked, wait some time and try again.
1411 FIXME - we shouldn't loop forever! Give up after some time! */
1412 DelayExecution();
1413 /* try again */
1414 goto LockHandleFrom;
1415 }
1416 else if ((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId())
1417 {
1418 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1419 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId());
1420 FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1);
1421 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1422 goto LockHandleFrom;
1423 }
1424 else
1425 {
1426 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1427 Ret = FALSE;
1428 }
1429 }
1430 return Ret;
1431 }
1432
1433 PVOID INTERNAL_CALL
1434 GDI_MapHandleTable(PSECTION_OBJECT SectionObject, PEPROCESS Process)
1435 {
1436 PVOID MappedView = NULL;
1437 NTSTATUS Status;
1438 LARGE_INTEGER Offset;
1439 ULONG ViewSize = sizeof(GDI_HANDLE_TABLE);
1440
1441 Offset.QuadPart = 0;
1442
1443 ASSERT(SectionObject != NULL);
1444 ASSERT(Process != NULL);
1445
1446 Status = MmMapViewOfSection(SectionObject,
1447 Process,
1448 &MappedView,
1449 0,
1450 0,
1451 &Offset,
1452 &ViewSize,
1453 ViewUnmap,
1454 SEC_NO_CHANGE,
1455 PAGE_READONLY);
1456
1457 if (!NT_SUCCESS(Status))
1458 return NULL;
1459
1460 return MappedView;
1461 }
1462
1463 /** PUBLIC FUNCTIONS **********************************************************/
1464
1465 /*
1466 Since Brush/Pen and Region objects are sharable,,, we can just use
1467 UserHeapAlloc to allocate the small attribute objects.
1468
1469 Example Allocating:
1470
1471 // Save Kernel Space Pointer
1472 (PBRUSH)->pBrushAttr = IntGdiAllocObjAttr(GDIObjType_BRUSH_TYPE);
1473
1474 // Kernel Space to User Space Pointer
1475 (PGDI_TABLE_ENTRY)->UserData = pBrushAttr;
1476 // Gdi will adjust for heap delta.
1477
1478 Example Freeing:
1479
1480 (PGDI_TABLE_ENTRY)->UserData = NULL; // Zero the user ptr.
1481 UserHeapFree((PBRUSH)->pBrushAttr); // Free from kernel ptr.
1482 (PBRUSH)->pBrushAttr = NULL;
1483
1484 Notes:
1485 Testing with DC_ATTR works but has drawing difficulties.
1486 Base on observation, (Over looking the obvious) we need to supply heap delta
1487 to user space gdi. Now, with testing, looks all normal.
1488
1489 */
1490 PVOID
1491 FASTCALL
1492 IntGdiAllocObjAttr(GDIOBJTYPE Type)
1493 {
1494 PVOID pMemAttr = NULL;
1495
1496 switch( Type )
1497 {
1498 case GDIObjType_DC_TYPE:
1499 pMemAttr = UserHeapAlloc(sizeof(DC_ATTR));
1500 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(DC_ATTR));
1501 break;
1502 case GDIObjType_RGN_TYPE:
1503 pMemAttr = UserHeapAlloc(sizeof(RGN_ATTR));
1504 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(RGN_ATTR));
1505 break;
1506 case GDIObjType_BRUSH_TYPE:
1507 pMemAttr = UserHeapAlloc(sizeof(BRUSH_ATTR));
1508 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(BRUSH_ATTR));
1509 break;
1510 default:
1511 break;
1512 }
1513 return pMemAttr;
1514 }
1515
1516
1517 BOOL
1518 FASTCALL
1519 IntGdiSetBrushOwner(PBRUSH pbr, DWORD OwnerMask)
1520 {
1521 HBRUSH hBR;
1522 PEPROCESS Owner = NULL;
1523 PGDI_TABLE_ENTRY pEntry = NULL;
1524
1525 if (!pbr) return FALSE;
1526
1527 hBR = pbr->BaseObject.hHmgr;
1528
1529 if (!hBR || (GDI_HANDLE_GET_TYPE(hBR) != GDI_OBJECT_TYPE_BRUSH))
1530 return FALSE;
1531 else
1532 {
1533 INT Index = GDI_HANDLE_GET_INDEX((HGDIOBJ)hBR);
1534 pEntry = &GdiHandleTable->Entries[Index];
1535 }
1536
1537 if (pbr->flAttrs & GDIBRUSH_IS_GLOBAL)
1538 {
1539 GDIOBJ_ShareUnlockObjByPtr((POBJ)pbr);
1540 return TRUE;
1541 }
1542
1543 if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE)
1544 {
1545 // Set this Brush to inaccessible mode and to an Owner of NONE.
1546 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1547
1548 if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, Owner))
1549 return FALSE;
1550
1551 // Deny user access to User Data.
1552 pEntry->UserData = NULL; // This hBR is inaccessible!
1553 }
1554
1555 if (OwnerMask == GDI_OBJ_HMGR_POWNED)
1556 {
1557 if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, PsGetCurrentProcess() ))
1558 return FALSE;
1559
1560 // Allow user access to User Data.
1561 pEntry->UserData = pbr->pBrushAttr;
1562 }
1563 return TRUE;
1564 }
1565
1566
1567 BOOL
1568 FASTCALL
1569 IntGdiSetDCOwnerEx( HDC hDC, DWORD OwnerMask, BOOL NoSetBrush)
1570 {
1571 PDC pDC;
1572 BOOL Ret = FALSE;
1573
1574 if (!hDC || (GDI_HANDLE_GET_TYPE(hDC) != GDI_OBJECT_TYPE_DC)) return FALSE;
1575
1576 if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE)
1577 {
1578 pDC = DC_LockDc ( hDC );
1579 MmCopyFromCaller(&pDC->dcattr, pDC->pdcattr, sizeof(DC_ATTR));
1580 DC_UnlockDc( pDC );
1581
1582 DC_FreeDcAttr( hDC ); // Free the dcattr!
1583
1584 if (!DC_SetOwnership( hDC, NULL )) // This hDC is inaccessible!
1585 return Ret;
1586 }
1587
1588 if (OwnerMask == GDI_OBJ_HMGR_POWNED)
1589 {
1590 pDC = DC_LockDc ( hDC );
1591 ASSERT(pDC->pdcattr == &pDC->dcattr);
1592 DC_UnlockDc( pDC );
1593
1594 if (!DC_SetOwnership( hDC, PsGetCurrentProcess() )) return Ret;
1595
1596 DC_AllocateDcAttr( hDC ); // Allocate new dcattr
1597
1598 DCU_SynchDcAttrtoUser( hDC ); // Copy data from dc to dcattr
1599 }
1600
1601 if ((OwnerMask != GDI_OBJ_HMGR_NONE) && !NoSetBrush)
1602 {
1603 pDC = DC_LockDc ( hDC );
1604 if (IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrFill, OwnerMask))
1605 IntGdiSetBrushOwner((PBRUSH)pDC->dclevel.pbrLine, OwnerMask);
1606 DC_UnlockDc( pDC );
1607 }
1608 return TRUE;
1609 }
1610
1611 INT
1612 FASTCALL
1613 GreGetObjectOwner(HGDIOBJ Handle, GDIOBJTYPE ObjType)
1614 {
1615 INT Ret = GDI_OBJ_HMGR_RESTRICTED;
1616
1617 if ( GDI_HANDLE_GET_INDEX(Handle) < GDI_HANDLE_COUNT )
1618 {
1619 PGDI_TABLE_ENTRY pEntry = &GdiHandleTable->Entries[GDI_HANDLE_GET_INDEX(Handle)];
1620
1621 if (pEntry->ObjectType == ObjType)
1622 {
1623 if (pEntry->FullUnique == (GDI_HANDLE_GET_UPPER(Handle) >> GDI_ENTRY_UPPER_SHIFT))
1624 Ret = pEntry->ProcessId & ~1;
1625 }
1626 }
1627 return Ret;
1628 }
1629
1630
1631 W32KAPI
1632 HANDLE
1633 APIENTRY
1634 NtGdiCreateClientObj(
1635 IN ULONG ulType
1636 )
1637 {
1638 POBJ pObject;
1639 HANDLE handle;
1640
1641 /* Mask out everything that would change the type in a wrong manner */
1642 ulType &= (GDI_HANDLE_TYPE_MASK & ~GDI_HANDLE_BASETYPE_MASK);
1643
1644 /* Allocate a new object */
1645 pObject = GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ | ulType);
1646 if (!pObject)
1647 {
1648 return NULL;
1649 }
1650
1651 /* get the handle */
1652 handle = pObject->hHmgr;
1653
1654 /* Unlock it */
1655 GDIOBJ_UnlockObjByPtr(pObject);
1656
1657 return handle;
1658 }
1659
1660 W32KAPI
1661 BOOL
1662 APIENTRY
1663 NtGdiDeleteClientObj(
1664 IN HANDLE h
1665 )
1666 {
1667 /* We first need to get the real type from the handle */
1668 ULONG type = GDI_HANDLE_GET_TYPE(h);
1669
1670 /* Check if it's really a CLIENTOBJ */
1671 if ((type & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1672 {
1673 /* FIXME: SetLastError? */
1674 return FALSE;
1675 }
1676 return GDIOBJ_FreeObjByHandle(h, type);
1677 }
1678
1679 INT
1680 FASTCALL
1681 IntGdiGetObject(IN HANDLE Handle,
1682 IN INT cbCount,
1683 IN LPVOID lpBuffer)
1684 {
1685 PVOID pGdiObject;
1686 INT Result = 0;
1687 DWORD dwObjectType;
1688
1689 pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE);
1690 if (!pGdiObject)
1691 {
1692 SetLastWin32Error(ERROR_INVALID_HANDLE);
1693 return 0;
1694 }
1695
1696 dwObjectType = GDIOBJ_GetObjectType(Handle);
1697 switch (dwObjectType)
1698 {
1699 case GDI_OBJECT_TYPE_PEN:
1700 case GDI_OBJECT_TYPE_EXTPEN:
1701 Result = PEN_GetObject((PBRUSH) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect
1702 break;
1703
1704 case GDI_OBJECT_TYPE_BRUSH:
1705 Result = BRUSH_GetObject((PBRUSH ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer);
1706 break;
1707
1708 case GDI_OBJECT_TYPE_BITMAP:
1709 Result = BITMAP_GetObject((SURFACE *) pGdiObject, cbCount, lpBuffer);
1710 break;
1711 case GDI_OBJECT_TYPE_FONT:
1712 Result = FontGetObject((PTEXTOBJ) pGdiObject, cbCount, lpBuffer);
1713 #if 0
1714 // Fix the LOGFONT structure for the stock fonts
1715 if (FIRST_STOCK_HANDLE <= Handle && Handle <= LAST_STOCK_HANDLE)
1716 {
1717 FixStockFontSizeW(Handle, cbCount, lpBuffer);
1718 }
1719 #endif
1720 break;
1721
1722 case GDI_OBJECT_TYPE_PALETTE:
1723 Result = PALETTE_GetObject((PPALGDI) pGdiObject, cbCount, lpBuffer);
1724 break;
1725
1726 default:
1727 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType);
1728 break;
1729 }
1730
1731 GDIOBJ_UnlockObjByPtr(pGdiObject);
1732
1733 return Result;
1734 }
1735
1736
1737
1738 W32KAPI
1739 INT
1740 APIENTRY
1741 NtGdiExtGetObjectW(IN HANDLE hGdiObj,
1742 IN INT cbCount,
1743 OUT LPVOID lpBuffer)
1744 {
1745 INT iRetCount = 0;
1746 INT cbCopyCount;
1747 union
1748 {
1749 BITMAP bitmap;
1750 DIBSECTION dibsection;
1751 LOGPEN logpen;
1752 LOGBRUSH logbrush;
1753 LOGFONTW logfontw;
1754 EXTLOGFONTW extlogfontw;
1755 ENUMLOGFONTEXDVW enumlogfontexdvw;
1756 } Object;
1757
1758 // Normalize to the largest supported object size
1759 cbCount = min((UINT)cbCount, sizeof(Object));
1760
1761 // Now do the actual call
1762 iRetCount = IntGdiGetObject(hGdiObj, cbCount, lpBuffer ? &Object : NULL);
1763 cbCopyCount = min((UINT)cbCount, (UINT)iRetCount);
1764
1765 // Make sure we have a buffer and a copy size
1766 if ((cbCopyCount) && (lpBuffer))
1767 {
1768 // Enter SEH for buffer transfer
1769 _SEH2_TRY
1770 {
1771 // Probe the buffer and copy it
1772 ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD));
1773 RtlCopyMemory(lpBuffer, &Object, cbCopyCount);
1774 }
1775 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1776 {
1777 // Clear the return value.
1778 // Do *NOT* set last error here!
1779 iRetCount = 0;
1780 }
1781 _SEH2_END;
1782 }
1783 // Return the count
1784 return iRetCount;
1785 }
1786
1787 /* EOF */