gdiobj.c: go back from intrinsics to to portable interlocked functions. InterlockedPo...
[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(GDIBRUSHOBJ), 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, 0, TAG_DRVOBJ, NULL}, /* 1c DRVOBJ, unused */
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 BOOL INTERNAL_CALL
486 GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType)
487 {
488 PGDI_TABLE_ENTRY Entry;
489 HANDLE ProcessId, LockedProcessId, PrevProcId;
490 ULONG HandleType, HandleUpper, TypeIndex;
491 BOOL Silent;
492
493 GDIDBG_INITLOOPTRACE();
494
495 DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
496
497 if (GDI_HANDLE_IS_STOCKOBJ(hObj))
498 {
499 DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
500 GDIDBG_TRACECALLER();
501 return FALSE;
502 }
503
504 ProcessId = PsGetCurrentProcessId();
505 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
506
507 Silent = (ExpectedType & GDI_OBJECT_TYPE_SILENT);
508 ExpectedType &= ~GDI_OBJECT_TYPE_SILENT;
509
510 HandleType = GDI_HANDLE_GET_TYPE(hObj);
511 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
512
513 /* Check if we have the requested type */
514 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
515 HandleType != ExpectedType) ||
516 HandleType == 0 )
517 {
518 DPRINT1("Attempted to free object 0x%x of wrong type (Handle: 0x%x, expected: 0x%x)\n",
519 hObj, HandleType, ExpectedType);
520 GDIDBG_TRACECALLER();
521 return FALSE;
522 }
523
524 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
525
526 LockHandle:
527 /* lock the object, we must not delete global objects, so don't exchange the locking
528 process ID to zero when attempting to lock a global object... */
529 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
530 if (PrevProcId == ProcessId)
531 {
532 if ( (Entry->KernelData != NULL) &&
533 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) &&
534 ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == (HandleUpper & GDI_ENTRY_BASETYPE_MASK)) )
535 {
536 POBJ Object;
537
538 Object = Entry->KernelData;
539
540 if (Object->cExclusiveLock == 0)
541 {
542 BOOL Ret;
543 PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
544
545 /* Clear the basetype field so when unlocking the handle it gets finally deleted and increment reuse counter */
546 Entry->Type = (Entry->Type + GDI_ENTRY_REUSE_INC) & ~GDI_ENTRY_BASETYPE_MASK;
547
548 /* unlock the handle slot */
549 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, NULL);
550
551 /* push this entry to the free list */
552 InterlockedPushFreeEntry(GDI_ENTRY_TO_INDEX(GdiHandleTable, Entry));
553
554 Object->hHmgr = NULL;
555
556 if (W32Process != NULL)
557 {
558 InterlockedDecrement(&W32Process->GDIObjects);
559 }
560
561 /* call the cleanup routine. */
562 TypeIndex = GDI_OBJECT_GET_TYPE_INDEX(HandleType);
563 Ret = ObjTypeInfo[TypeIndex].CleanupProc(Object);
564
565 /* Now it's time to free the memory */
566 GDIOBJ_FreeObj(Object, TypeIndex);
567
568 GDIDBG_CAPTUREDELETER(hObj);
569 return Ret;
570 }
571 else
572 {
573 /*
574 * The object is currently locked, so freeing is forbidden!
575 */
576 DPRINT1("Object->cExclusiveLock = %d\n", Object->cExclusiveLock);
577 GDIDBG_TRACECALLER();
578 GDIDBG_TRACELOCKER(GDI_HANDLE_GET_INDEX(hObj));
579 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
580 /* do not assert here for it will call again from dxg.sys it being call twice */
581 //ASSERT(FALSE);
582 }
583 }
584 else
585 {
586 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_FreeObj");
587 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
588 }
589 }
590 else if (PrevProcId == LockedProcessId)
591 {
592 GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
593
594 /* the object is currently locked, wait some time and try again.
595 FIXME - we shouldn't loop forever! Give up after some time! */
596 DelayExecution();
597 /* try again */
598 goto LockHandle;
599 }
600 else
601 {
602 if (!Silent)
603 {
604 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) == 0)
605 {
606 DPRINT1("Attempted to free gdi handle 0x%x that is already deleted!\n", hObj);
607 }
608 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
609 {
610 DPRINT1("Attempted to free global gdi handle 0x%x, caller needs to get ownership first!!!\n", hObj);
611 }
612 else
613 {
614 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);
615 }
616 DPRINT1("Type = 0x%lx, KernelData = 0x%p, ProcessId = 0x%p\n", Entry->Type, Entry->KernelData, Entry->ProcessId);
617 GDIDBG_TRACECALLER();
618 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
619 }
620 }
621
622 return FALSE;
623 }
624
625 BOOL
626 FASTCALL
627 IsObjectDead(HGDIOBJ hObject)
628 {
629 INT Index = GDI_HANDLE_GET_INDEX(hObject);
630 PGDI_TABLE_ENTRY Entry = &GdiHandleTable->Entries[Index];
631 // We check to see if the objects are knocking on deaths door.
632 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
633 return FALSE;
634 else
635 {
636 DPRINT1("Object 0x%x currently being destroyed!!!\n",hObject);
637 return TRUE; // return true and move on.
638 }
639 }
640
641
642 /*!
643 * Delete GDI object
644 * \param hObject object handle
645 * \return if the function fails the returned value is FALSE.
646 */
647 BOOL
648 FASTCALL
649 NtGdiDeleteObject(HGDIOBJ hObject)
650 {
651 DPRINT("NtGdiDeleteObject handle 0x%08x\n", hObject);
652 if (!IsObjectDead(hObject))
653 {
654 return NULL != hObject
655 ? GDIOBJ_FreeObjByHandle(hObject, GDI_OBJECT_TYPE_DONTCARE) : FALSE;
656 }
657 else
658 {
659 DPRINT1("Attempt DeleteObject 0x%x currently being destroyed!!!\n",hObject);
660 return TRUE; // return true and move on.
661 }
662 }
663
664 VOID
665 FASTCALL
666 IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType)
667 {
668 PGDI_TABLE_ENTRY Entry, End;
669 ULONG Index = RESERVE_ENTRIES_COUNT;
670 HANDLE ProcId;
671 PW32PROCESS W32Process;
672
673 W32Process = (PW32PROCESS)Process->Win32Process;
674 ASSERT(W32Process);
675
676 if (W32Process->GDIObjects > 0)
677 {
678 ProcId = Process->UniqueProcessId;
679
680 /* FIXME - Instead of building the handle here and delete it using GDIOBJ_FreeObj
681 we should delete it directly here! */
682
683 End = &GdiHandleTable->Entries[GDI_HANDLE_COUNT];
684 for (Entry = &GdiHandleTable->Entries[RESERVE_ENTRIES_COUNT];
685 Entry != End;
686 Entry++, Index++)
687 {
688 /* ignore the lock bit */
689 if ( (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcId)
690 {
691 if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) == ObjectType ||
692 ObjectType == GDI_OBJECT_TYPE_DONTCARE)
693 {
694 HGDIOBJ ObjectHandle;
695
696 /* Create the object handle for the entry, the lower(!) 16 bit of the
697 Type field includes the type of the object including the stock
698 object flag - but since stock objects don't have a process id we can
699 simply ignore this fact here. */
700 ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
701
702 if (GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) &&
703 W32Process->GDIObjects == 0)
704 {
705 /* there are no more gdi handles for this process, bail */
706 break;
707 }
708 }
709 }
710 }
711 }
712 }
713
714
715 /*!
716 * Internal function. Called when the process is destroyed to free the remaining GDI handles.
717 * \param Process - PID of the process that will be destroyed.
718 */
719 BOOL INTERNAL_CALL
720 GDI_CleanupForProcess(struct _EPROCESS *Process)
721 {
722 PEPROCESS CurrentProcess;
723
724 DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
725 CurrentProcess = PsGetCurrentProcess();
726 if (CurrentProcess != Process)
727 {
728 KeAttachProcess(&Process->Pcb);
729 }
730
731 /* Delete objects. Begin with types that are not referenced by other types */
732 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE);
733 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE);
734 IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BITMAP_TYPE);
735
736 /* Finally finish with what's left */
737 IntDeleteHandlesForProcess(Process, GDI_OBJECT_TYPE_DONTCARE);
738
739 if (CurrentProcess != Process)
740 {
741 KeDetachProcess();
742 }
743
744 #ifdef GDI_DEBUG
745 GdiDbgHTIntegrityCheck();
746 #endif
747
748 DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
749
750 return TRUE;
751 }
752
753 /*!
754 * Return pointer to the object by handle.
755 *
756 * \param hObj Object handle
757 * \return Pointer to the object.
758 *
759 * \note Process can only get pointer to the objects it created or global objects.
760 *
761 * \todo Get rid of the ExpectedType parameter!
762 */
763 PGDIOBJ INTERNAL_CALL
764 GDIOBJ_LockObj(HGDIOBJ hObj, DWORD ExpectedType)
765 {
766 ULONG HandleIndex;
767 PGDI_TABLE_ENTRY Entry;
768 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
769 POBJ Object = NULL;
770 ULONG HandleType, HandleUpper;
771
772 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
773 HandleType = GDI_HANDLE_GET_TYPE(hObj);
774 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
775
776 /* Check that the handle index is valid. */
777 if (HandleIndex >= GDI_HANDLE_COUNT)
778 return NULL;
779
780 Entry = &GdiHandleTable->Entries[HandleIndex];
781
782 /* Check if we have the requested type */
783 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
784 HandleType != ExpectedType) ||
785 HandleType == 0 )
786 {
787 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
788 hObj, HandleType, ExpectedType);
789 GDIDBG_TRACECALLER();
790 GDIDBG_TRACEALLOCATOR(hObj);
791 GDIDBG_TRACEDELETER(hObj);
792 return NULL;
793 }
794
795 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
796 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
797
798 /* Check for invalid owner. */
799 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
800 {
801 DPRINT1("Tried to lock object (0x%p) of wrong owner! ProcessId = %p, HandleProcessId = %p\n", hObj, ProcessId, HandleProcessId);
802 GDIDBG_TRACECALLER();
803 GDIDBG_TRACEALLOCATOR(GDI_HANDLE_GET_INDEX(hObj));
804 return NULL;
805 }
806
807 /*
808 * Prevent the thread from being terminated during the locking process.
809 * It would result in undesired effects and inconsistency of the global
810 * handle table.
811 */
812
813 KeEnterCriticalRegion();
814
815 /*
816 * Loop until we either successfully lock the handle entry & object or
817 * fail some of the check.
818 */
819
820 for (;;)
821 {
822 /* Lock the handle table entry. */
823 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
824 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
825 LockedProcessId,
826 HandleProcessId);
827
828 if (PrevProcId == HandleProcessId)
829 {
830 /*
831 * We're locking an object that belongs to our process or it's a
832 * global object if HandleProcessId is 0 here.
833 */
834
835 if ( (Entry->KernelData != NULL) &&
836 ((Entry->Type << GDI_ENTRY_UPPER_SHIFT) == HandleUpper) )
837 {
838 PW32THREAD Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
839 Object = Entry->KernelData;
840
841 if (Object->cExclusiveLock == 0)
842 {
843 Object->Tid = Thread;
844 Object->cExclusiveLock = 1;
845 GDIDBG_CAPTURELOCKER(GDI_HANDLE_GET_INDEX(hObj))
846 }
847 else
848 {
849 if (Object->Tid != Thread)
850 {
851 /* Unlock the handle table entry. */
852 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
853
854 DelayExecution();
855 continue;
856 }
857 InterlockedIncrement((PLONG)&Object->cExclusiveLock);
858 }
859 }
860 else
861 {
862 /*
863 * Debugging code. Report attempts to lock deleted handles and
864 * locking type mismatches.
865 */
866 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_LockObj");
867 }
868
869 /* Unlock the handle table entry. */
870 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
871
872 break;
873 }
874 else
875 {
876 /*
877 * The handle is currently locked, wait some time and try again.
878 */
879
880 DelayExecution();
881 continue;
882 }
883 }
884
885 KeLeaveCriticalRegion();
886
887 return Object;
888 }
889
890
891 /*!
892 * Return pointer to the object by handle (and allow sharing of the handle
893 * across threads).
894 *
895 * \param hObj Object handle
896 * \return Pointer to the object.
897 *
898 * \note Process can only get pointer to the objects it created or global objects.
899 *
900 * \todo Get rid of the ExpectedType parameter!
901 */
902 PGDIOBJ INTERNAL_CALL
903 GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
904 {
905 ULONG HandleIndex;
906 PGDI_TABLE_ENTRY Entry;
907 HANDLE ProcessId, HandleProcessId, LockedProcessId, PrevProcId;
908 POBJ Object = NULL;
909 ULONG_PTR HandleType, HandleUpper;
910
911 HandleIndex = GDI_HANDLE_GET_INDEX(hObj);
912 HandleType = GDI_HANDLE_GET_TYPE(hObj);
913 HandleUpper = GDI_HANDLE_GET_UPPER(hObj);
914
915 /* Check that the handle index is valid. */
916 if (HandleIndex >= GDI_HANDLE_COUNT)
917 return NULL;
918
919 /* Check if we have the requested type */
920 if ( (ExpectedType != GDI_OBJECT_TYPE_DONTCARE &&
921 HandleType != ExpectedType) ||
922 HandleType == 0 )
923 {
924 DPRINT1("Attempted to lock object 0x%x of wrong type (Handle: 0x%x, requested: 0x%x)\n",
925 hObj, HandleType, ExpectedType);
926 return NULL;
927 }
928
929 Entry = &GdiHandleTable->Entries[HandleIndex];
930
931 ProcessId = (HANDLE)((ULONG_PTR)PsGetCurrentProcessId() & ~1);
932 HandleProcessId = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~1);
933
934 /* Check for invalid owner. */
935 if (ProcessId != HandleProcessId && HandleProcessId != NULL)
936 {
937 return NULL;
938 }
939
940 /*
941 * Prevent the thread from being terminated during the locking process.
942 * It would result in undesired effects and inconsistency of the global
943 * handle table.
944 */
945
946 KeEnterCriticalRegion();
947
948 /*
949 * Loop until we either successfully lock the handle entry & object or
950 * fail some of the check.
951 */
952
953 for (;;)
954 {
955 /* Lock the handle table entry. */
956 LockedProcessId = (HANDLE)((ULONG_PTR)HandleProcessId | 0x1);
957 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId,
958 LockedProcessId,
959 HandleProcessId);
960
961 if (PrevProcId == HandleProcessId)
962 {
963 /*
964 * We're locking an object that belongs to our process or it's a
965 * global object if HandleProcessId is 0 here.
966 */
967
968 if ( (Entry->KernelData != NULL) &&
969 (HandleUpper == (Entry->Type << GDI_ENTRY_UPPER_SHIFT)) )
970 {
971 Object = (POBJ)Entry->KernelData;
972
973 #ifdef GDI_DEBUG
974 if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
975 {
976 memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
977 RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleLocker[HandleIndex], NULL);
978 }
979 #else
980 InterlockedIncrement((PLONG)&Object->ulShareCount);
981 #endif
982 }
983 else
984 {
985 /*
986 * Debugging code. Report attempts to lock deleted handles and
987 * locking type mismatches.
988 */
989 LockErrorDebugOutput(hObj, Entry, "GDIOBJ_ShareLockObj");
990 }
991
992 /* Unlock the handle table entry. */
993 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
994
995 break;
996 }
997 else
998 {
999 /*
1000 * The handle is currently locked, wait some time and try again.
1001 */
1002
1003 DelayExecution();
1004 continue;
1005 }
1006 }
1007
1008 KeLeaveCriticalRegion();
1009
1010 return Object;
1011 }
1012
1013
1014 /*!
1015 * Release GDI object. Every object locked by GDIOBJ_LockObj() must be unlocked. You should unlock the object
1016 * as soon as you don't need to have access to it's data.
1017
1018 * \param Object Object pointer (as returned by GDIOBJ_LockObj).
1019 */
1020 VOID INTERNAL_CALL
1021 GDIOBJ_UnlockObjByPtr(POBJ Object)
1022 {
1023 if (InterlockedDecrement((PLONG)&Object->cExclusiveLock) < 0)
1024 {
1025 DPRINT1("Trying to unlock non-existant object\n");
1026 }
1027 }
1028
1029 VOID INTERNAL_CALL
1030 GDIOBJ_ShareUnlockObjByPtr(POBJ Object)
1031 {
1032 if (InterlockedDecrement((PLONG)&Object->ulShareCount) < 0)
1033 {
1034 DPRINT1("Trying to unlock non-existant object\n");
1035 }
1036 }
1037
1038 BOOL INTERNAL_CALL
1039 GDIOBJ_OwnedByCurrentProcess(HGDIOBJ ObjectHandle)
1040 {
1041 PGDI_TABLE_ENTRY Entry;
1042 HANDLE ProcessId;
1043 BOOL Ret;
1044
1045 DPRINT("GDIOBJ_OwnedByCurrentProcess: ObjectHandle: 0x%08x\n", ObjectHandle);
1046
1047 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1048 {
1049 ProcessId = PsGetCurrentProcessId();
1050
1051 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1052 Ret = Entry->KernelData != NULL &&
1053 (Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 &&
1054 (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) == ProcessId;
1055
1056 return Ret;
1057 }
1058
1059 return FALSE;
1060 }
1061
1062 BOOL INTERNAL_CALL
1063 GDIOBJ_ConvertToStockObj(HGDIOBJ *phObj)
1064 {
1065 /*
1066 * FIXME !!!!! THIS FUNCTION NEEDS TO BE FIXED - IT IS NOT SAFE WHEN OTHER THREADS
1067 * MIGHT ATTEMPT TO LOCK THE OBJECT DURING THIS CALL!!!
1068 */
1069 PGDI_TABLE_ENTRY Entry;
1070 HANDLE ProcessId, LockedProcessId, PrevProcId;
1071 PW32THREAD Thread;
1072 HGDIOBJ hObj;
1073
1074 GDIDBG_INITLOOPTRACE();
1075
1076 ASSERT(phObj);
1077 hObj = *phObj;
1078
1079 DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", hObj);
1080
1081 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1082
1083 if (!GDI_HANDLE_IS_STOCKOBJ(hObj))
1084 {
1085 ProcessId = PsGetCurrentProcessId();
1086 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1087
1088 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, hObj);
1089
1090 LockHandle:
1091 /* lock the object, we must not convert stock objects, so don't check!!! */
1092 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, LockedProcessId, ProcessId);
1093 if (PrevProcId == ProcessId)
1094 {
1095 LONG NewType, PrevType, OldType;
1096
1097 /* we're locking an object that belongs to our process. First calculate
1098 the new object type including the stock object flag and then try to
1099 exchange it.*/
1100 /* On Windows the higher 16 bit of the type field don't contain the
1101 full type from the handle, but the base type.
1102 (type = BRSUH, PEN, EXTPEN, basetype = BRUSH) */
1103 OldType = ((ULONG)hObj & GDI_HANDLE_BASETYPE_MASK) | ((ULONG)hObj >> GDI_ENTRY_UPPER_SHIFT);
1104 /* We are currently not using bits 24..31 (flags) of the type field, but for compatibility
1105 we copy them as we can't get them from the handle */
1106 OldType |= Entry->Type & GDI_ENTRY_FLAGS_MASK;
1107
1108 /* As the object should be a stock object, set it's flag, but only in the lower 16 bits */
1109 NewType = OldType | GDI_ENTRY_STOCK_MASK;
1110
1111 /* Try to exchange the type field - but only if the old (previous type) matches! */
1112 PrevType = InterlockedCompareExchange(&Entry->Type, NewType, OldType);
1113 if (PrevType == OldType && Entry->KernelData != NULL)
1114 {
1115 PW32THREAD PrevThread;
1116 POBJ Object;
1117
1118 /* We successfully set the stock object flag.
1119 KernelData should never be NULL here!!! */
1120 ASSERT(Entry->KernelData);
1121
1122 Object = Entry->KernelData;
1123
1124 PrevThread = Object->Tid;
1125 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1126 {
1127 /* dereference the process' object counter */
1128 if (PrevProcId != GDI_GLOBAL_PROCESS)
1129 {
1130 PEPROCESS OldProcess;
1131 PW32PROCESS W32Process;
1132 NTSTATUS Status;
1133
1134 /* FIXME */
1135 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1136 if (NT_SUCCESS(Status))
1137 {
1138 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1139 if (W32Process != NULL)
1140 {
1141 InterlockedDecrement(&W32Process->GDIObjects);
1142 }
1143 ObDereferenceObject(OldProcess);
1144 }
1145 }
1146
1147 hObj = (HGDIOBJ)((ULONG)(hObj) | GDI_HANDLE_STOCK_MASK);
1148 *phObj = hObj;
1149 Object->hHmgr = hObj;
1150
1151 /* remove the process id lock and make it global */
1152 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, GDI_GLOBAL_PROCESS);
1153
1154 /* we're done, successfully converted the object */
1155 return TRUE;
1156 }
1157 else
1158 {
1159 GDIDBG_TRACELOOP(hObj, PrevThread, Thread);
1160
1161 /* WTF?! The object is already locked by a different thread!
1162 Release the lock, wait a bit and try again!
1163 FIXME - we should give up after some time unless we want to wait forever! */
1164 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1165
1166 DelayExecution();
1167 goto LockHandle;
1168 }
1169 }
1170 else
1171 {
1172 DPRINT1("Attempted to convert object 0x%x that is deleted! Should never get here!!!\n", hObj);
1173 DPRINT1("OldType = 0x%x, Entry->Type = 0x%x, NewType = 0x%x, Entry->KernelData = 0x%x\n", OldType, Entry->Type, NewType, Entry->KernelData);
1174 }
1175 }
1176 else if (PrevProcId == LockedProcessId)
1177 {
1178 GDIDBG_TRACELOOP(hObj, PrevProcId, ProcessId);
1179
1180 /* the object is currently locked, wait some time and try again.
1181 FIXME - we shouldn't loop forever! Give up after some time! */
1182 DelayExecution();
1183 /* try again */
1184 goto LockHandle;
1185 }
1186 else
1187 {
1188 DPRINT1("Attempted to convert invalid handle: 0x%x\n", hObj);
1189 }
1190 }
1191
1192 return FALSE;
1193 }
1194
1195 BOOL INTERNAL_CALL
1196 GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
1197 {
1198 PGDI_TABLE_ENTRY Entry;
1199 HANDLE ProcessId, LockedProcessId, PrevProcId;
1200 PW32THREAD Thread;
1201 BOOL Ret = TRUE;
1202
1203 GDIDBG_INITLOOPTRACE();
1204
1205 DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
1206
1207 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1208
1209 if (!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
1210 {
1211 ProcessId = PsGetCurrentProcessId();
1212 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1213
1214 Entry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, ObjectHandle);
1215
1216 LockHandle:
1217 /* lock the object, we must not convert stock objects, so don't check!!! */
1218 PrevProcId = InterlockedCompareExchangePointer((PVOID*)&Entry->ProcessId, ProcessId, LockedProcessId);
1219 if (PrevProcId == ProcessId)
1220 {
1221 PW32THREAD PrevThread;
1222
1223 if ((Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1224 {
1225 POBJ Object = Entry->KernelData;
1226
1227 PrevThread = Object->Tid;
1228 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1229 {
1230 PEPROCESS OldProcess;
1231 PW32PROCESS W32Process;
1232 NTSTATUS Status;
1233
1234 /* dereference the process' object counter */
1235 /* FIXME */
1236 if ((ULONG_PTR)PrevProcId & ~0x1)
1237 {
1238 Status = PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)PrevProcId & ~0x1), &OldProcess);
1239 if (NT_SUCCESS(Status))
1240 {
1241 W32Process = (PW32PROCESS)OldProcess->Win32Process;
1242 if (W32Process != NULL)
1243 {
1244 InterlockedDecrement(&W32Process->GDIObjects);
1245 }
1246 ObDereferenceObject(OldProcess);
1247 }
1248 }
1249
1250 if (NewOwner != NULL)
1251 {
1252 ProcessId = PsGetProcessId(NewOwner);
1253
1254 /* Increase the new process' object counter */
1255 W32Process = (PW32PROCESS)NewOwner->Win32Process;
1256 if (W32Process != NULL)
1257 {
1258 InterlockedIncrement(&W32Process->GDIObjects);
1259 }
1260 }
1261 else
1262 ProcessId = 0;
1263
1264 /* remove the process id lock and change it to the new process id */
1265 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, ProcessId);
1266
1267 /* we're done! */
1268 return Ret;
1269 }
1270 else
1271 {
1272 GDIDBG_TRACELOOP(ObjectHandle, PrevThread, Thread);
1273
1274 /* WTF?! The object is already locked by a different thread!
1275 Release the lock, wait a bit and try again! DO reset the pid lock
1276 so we make sure we don't access invalid memory in case the object is
1277 being deleted in the meantime (because we don't have aquired a reference
1278 at this point).
1279 FIXME - we should give up after some time unless we want to wait forever! */
1280 (void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
1281
1282 DelayExecution();
1283 goto LockHandle;
1284 }
1285 }
1286 else
1287 {
1288 DPRINT1("Attempted to change ownership of an object 0x%x currently being destroyed!!!\n", ObjectHandle);
1289 DPRINT1("Entry->Type = 0x%lx, Entry->KernelData = 0x%p\n", Entry->Type, Entry->KernelData);
1290 Ret = FALSE;
1291 }
1292 }
1293 else if (PrevProcId == LockedProcessId)
1294 {
1295 GDIDBG_TRACELOOP(ObjectHandle, PrevProcId, ProcessId);
1296
1297 /* the object is currently locked, wait some time and try again.
1298 FIXME - we shouldn't loop forever! Give up after some time! */
1299 DelayExecution();
1300 /* try again */
1301 goto LockHandle;
1302 }
1303 else if (((ULONG_PTR)PrevProcId & ~0x1) == 0)
1304 {
1305 /* allow changing ownership of global objects */
1306 ProcessId = NULL;
1307 LockedProcessId = (HANDLE)((ULONG_PTR)ProcessId | 0x1);
1308 goto LockHandle;
1309 }
1310 else if ((HANDLE)((ULONG_PTR)PrevProcId & ~0x1) != PsGetCurrentProcessId())
1311 {
1312 DPRINT1("Attempted to change ownership of object 0x%x (pid: 0x%x) from pid 0x%x!!!\n", ObjectHandle, (ULONG_PTR)PrevProcId & ~0x1, PsGetCurrentProcessId());
1313 Ret = FALSE;
1314 }
1315 else
1316 {
1317 DPRINT1("Attempted to change owner of invalid handle: 0x%x\n", ObjectHandle);
1318 Ret = FALSE;
1319 }
1320 }
1321 return Ret;
1322 }
1323
1324 BOOL INTERNAL_CALL
1325 GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
1326 {
1327 PGDI_TABLE_ENTRY FromEntry;
1328 PW32THREAD Thread;
1329 HANDLE FromProcessId, FromLockedProcessId, FromPrevProcId;
1330 BOOL Ret = TRUE;
1331
1332 GDIDBG_INITLOOPTRACE();
1333
1334 DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
1335
1336 Thread = (PW32THREAD)PsGetCurrentThreadWin32Thread();
1337
1338 if (!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))
1339 {
1340 FromEntry = GDI_HANDLE_GET_ENTRY(GdiHandleTable, CopyFrom);
1341
1342 FromProcessId = (HANDLE)((ULONG_PTR)FromEntry->ProcessId & ~0x1);
1343 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1344
1345 LockHandleFrom:
1346 /* lock the object, we must not convert stock objects, so don't check!!! */
1347 FromPrevProcId = InterlockedCompareExchangePointer((PVOID*)&FromEntry->ProcessId, FromProcessId, FromLockedProcessId);
1348 if (FromPrevProcId == FromProcessId)
1349 {
1350 PW32THREAD PrevThread;
1351 POBJ Object;
1352
1353 if ((FromEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
1354 {
1355 Object = FromEntry->KernelData;
1356
1357 /* save the pointer to the calling thread so we know it was this thread
1358 that locked the object */
1359 PrevThread = Object->Tid;
1360 if (Object->cExclusiveLock == 0 || PrevThread == Thread)
1361 {
1362 /* now let's change the ownership of the target object */
1363
1364 if (((ULONG_PTR)FromPrevProcId & ~0x1) != 0)
1365 {
1366 PEPROCESS ProcessTo;
1367 /* FIXME */
1368 if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1), &ProcessTo)))
1369 {
1370 GDIOBJ_SetOwnership(CopyTo, ProcessTo);
1371 ObDereferenceObject(ProcessTo);
1372 }
1373 }
1374 else
1375 {
1376 /* mark the object as global */
1377 GDIOBJ_SetOwnership(CopyTo, NULL);
1378 }
1379
1380 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1381 }
1382 else
1383 {
1384 GDIDBG_TRACELOOP(CopyFrom, PrevThread, Thread);
1385
1386 /* WTF?! The object is already locked by a different thread!
1387 Release the lock, wait a bit and try again! DO reset the pid lock
1388 so we make sure we don't access invalid memory in case the object is
1389 being deleted in the meantime (because we don't have aquired a reference
1390 at this point).
1391 FIXME - we should give up after some time unless we want to wait forever! */
1392 (void)InterlockedExchangePointer((PVOID*)&FromEntry->ProcessId, FromPrevProcId);
1393
1394 DelayExecution();
1395 goto LockHandleFrom;
1396 }
1397 }
1398 else
1399 {
1400 DPRINT1("Attempted to copy ownership from an object 0x%x currently being destroyed!!!\n", CopyFrom);
1401 Ret = FALSE;
1402 }
1403 }
1404 else if (FromPrevProcId == FromLockedProcessId)
1405 {
1406 GDIDBG_TRACELOOP(CopyFrom, FromPrevProcId, FromProcessId);
1407
1408 /* the object is currently locked, wait some time and try again.
1409 FIXME - we shouldn't loop forever! Give up after some time! */
1410 DelayExecution();
1411 /* try again */
1412 goto LockHandleFrom;
1413 }
1414 else if ((HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1) != PsGetCurrentProcessId())
1415 {
1416 /* FIXME - should we really allow copying ownership from objects that we don't even own? */
1417 DPRINT1("WARNING! Changing copying ownership of object 0x%x (pid: 0x%x) to pid 0x%x!!!\n", CopyFrom, (ULONG_PTR)FromPrevProcId & ~0x1, PsGetCurrentProcessId());
1418 FromProcessId = (HANDLE)((ULONG_PTR)FromPrevProcId & ~0x1);
1419 FromLockedProcessId = (HANDLE)((ULONG_PTR)FromProcessId | 0x1);
1420 goto LockHandleFrom;
1421 }
1422 else
1423 {
1424 DPRINT1("Attempted to copy ownership from invalid handle: 0x%x\n", CopyFrom);
1425 Ret = FALSE;
1426 }
1427 }
1428 return Ret;
1429 }
1430
1431 PVOID INTERNAL_CALL
1432 GDI_MapHandleTable(PSECTION_OBJECT SectionObject, PEPROCESS Process)
1433 {
1434 PVOID MappedView = NULL;
1435 NTSTATUS Status;
1436 LARGE_INTEGER Offset;
1437 ULONG ViewSize = sizeof(GDI_HANDLE_TABLE);
1438
1439 Offset.QuadPart = 0;
1440
1441 ASSERT(SectionObject != NULL);
1442 ASSERT(Process != NULL);
1443
1444 Status = MmMapViewOfSection(SectionObject,
1445 Process,
1446 &MappedView,
1447 0,
1448 0,
1449 &Offset,
1450 &ViewSize,
1451 ViewUnmap,
1452 SEC_NO_CHANGE,
1453 PAGE_READONLY);
1454
1455 if (!NT_SUCCESS(Status))
1456 return NULL;
1457
1458 return MappedView;
1459 }
1460
1461 /** PUBLIC FUNCTIONS **********************************************************/
1462
1463 /*
1464 Since Brush/Pen and Region objects are sharable,,, we can just use
1465 UserHeapAlloc to allocate the small attribute objects.
1466
1467 Example Allocating:
1468
1469 // Save Kernel Space Pointer
1470 (PGDIBRUSHOBJ)->pBrushAttr = IntGdiAllocObjAttr(GDIObjType_BRUSH_TYPE);
1471
1472 // Kernel Space to User Space Pointer
1473 (PGDI_TABLE_ENTRY)->UserData = pBrushAttr;
1474 // Gdi will adjust for heap delta.
1475
1476 Example Freeing:
1477
1478 (PGDI_TABLE_ENTRY)->UserData = NULL; // Zero the user ptr.
1479 UserHeapFree((PGDIBRUSHOBJ)->pBrushAttr); // Free from kernel ptr.
1480 (PGDIBRUSHOBJ)->pBrushAttr = NULL;
1481
1482 Notes:
1483 Testing with DC_ATTR works but has drawing difficulties.
1484 Base on observation, (Over looking the obvious) we need to supply heap delta
1485 to user space gdi. Now, with testing, looks all normal.
1486
1487 */
1488 PVOID
1489 FASTCALL
1490 IntGdiAllocObjAttr(GDIOBJTYPE Type)
1491 {
1492 PVOID pMemAttr = NULL;
1493
1494 switch( Type )
1495 {
1496 case GDIObjType_DC_TYPE:
1497 pMemAttr = UserHeapAlloc(sizeof(DC_ATTR));
1498 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(DC_ATTR));
1499 break;
1500 case GDIObjType_RGN_TYPE:
1501 pMemAttr = UserHeapAlloc(sizeof(RGN_ATTR));
1502 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(RGN_ATTR));
1503 break;
1504 case GDIObjType_BRUSH_TYPE:
1505 pMemAttr = UserHeapAlloc(sizeof(BRUSH_ATTR));
1506 if (pMemAttr) RtlZeroMemory(pMemAttr, sizeof(BRUSH_ATTR));
1507 break;
1508 default:
1509 break;
1510 }
1511 return pMemAttr;
1512 }
1513
1514
1515 BOOL
1516 FASTCALL
1517 IntGdiSetBrushOwner(PGDIBRUSHOBJ pbr, DWORD OwnerMask)
1518 {
1519 HBRUSH hBR;
1520 PEPROCESS Owner = NULL;
1521 PGDI_TABLE_ENTRY pEntry = NULL;
1522
1523 if (!pbr) return FALSE;
1524
1525 hBR = pbr->BaseObject.hHmgr;
1526
1527 if (!hBR || (GDI_HANDLE_GET_TYPE(hBR) != GDI_OBJECT_TYPE_BRUSH))
1528 return FALSE;
1529 else
1530 {
1531 INT Index = GDI_HANDLE_GET_INDEX((HGDIOBJ)hBR);
1532 pEntry = &GdiHandleTable->Entries[Index];
1533 }
1534
1535 if (pbr->flAttrs & GDIBRUSH_IS_GLOBAL)
1536 {
1537 GDIOBJ_ShareUnlockObjByPtr((POBJ)pbr);
1538 return TRUE;
1539 }
1540
1541 if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE)
1542 {
1543 // Set this Brush to inaccessible mode and to an Owner of NONE.
1544 // if (OwnerMask == GDI_OBJ_HMGR_NONE) Owner = OwnerMask;
1545
1546 if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, Owner))
1547 return FALSE;
1548
1549 // Deny user access to User Data.
1550 pEntry->UserData = NULL; // This hBR is inaccessible!
1551 }
1552
1553 if (OwnerMask == GDI_OBJ_HMGR_POWNED)
1554 {
1555 if (!GDIOBJ_SetOwnership((HGDIOBJ) hBR, PsGetCurrentProcess() ))
1556 return FALSE;
1557
1558 // Allow user access to User Data.
1559 pEntry->UserData = pbr->pBrushAttr;
1560 }
1561 return TRUE;
1562 }
1563
1564
1565 BOOL
1566 FASTCALL
1567 IntGdiSetDCOwnerEx( HDC hDC, DWORD OwnerMask, BOOL NoSetBrush)
1568 {
1569 PDC pDC;
1570 BOOL Ret = FALSE;
1571
1572 if (!hDC || (GDI_HANDLE_GET_TYPE(hDC) != GDI_OBJECT_TYPE_DC)) return FALSE;
1573
1574 if ((OwnerMask == GDI_OBJ_HMGR_PUBLIC) || OwnerMask == GDI_OBJ_HMGR_NONE)
1575 {
1576 pDC = DC_LockDc ( hDC );
1577 MmCopyFromCaller(&pDC->Dc_Attr, pDC->pDc_Attr, sizeof(DC_ATTR));
1578 DC_UnlockDc( pDC );
1579
1580 DC_FreeDcAttr( hDC ); // Free the dcattr!
1581
1582 if (!DC_SetOwnership( hDC, NULL )) // This hDC is inaccessible!
1583 return Ret;
1584 }
1585
1586 if (OwnerMask == GDI_OBJ_HMGR_POWNED)
1587 {
1588 pDC = DC_LockDc ( hDC );
1589 if ( !pDC->pDc_Attr ) Ret = TRUE; // Must be zero.
1590 DC_UnlockDc( pDC );
1591 if (!Ret) return Ret;
1592
1593 if (!DC_SetOwnership( hDC, PsGetCurrentProcess() )) return Ret;
1594
1595 DC_AllocateDcAttr( hDC ); // Allocate new dcattr
1596
1597 DCU_SynchDcAttrtoUser( hDC ); // Copy data from dc to dcattr
1598 }
1599
1600 if ((OwnerMask != GDI_OBJ_HMGR_NONE) && !NoSetBrush)
1601 {
1602 pDC = DC_LockDc ( hDC );
1603 if (IntGdiSetBrushOwner((PGDIBRUSHOBJ)pDC->DcLevel.pbrFill, OwnerMask))
1604 IntGdiSetBrushOwner((PGDIBRUSHOBJ)pDC->DcLevel.pbrLine, OwnerMask);
1605 DC_UnlockDc( pDC );
1606 }
1607 return TRUE;
1608 }
1609
1610 W32KAPI
1611 HANDLE
1612 APIENTRY
1613 NtGdiCreateClientObj(
1614 IN ULONG ulType
1615 )
1616 {
1617 POBJ pObject;
1618 HANDLE handle;
1619
1620 /* Mask out everything that would change the type in a wrong manner */
1621 ulType &= (GDI_HANDLE_TYPE_MASK & ~GDI_HANDLE_BASETYPE_MASK);
1622
1623 /* Allocate a new object */
1624 pObject = GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_CLIOBJ | ulType);
1625 if (!pObject)
1626 {
1627 return NULL;
1628 }
1629
1630 /* get the handle */
1631 handle = pObject->hHmgr;
1632
1633 /* Unlock it */
1634 GDIOBJ_UnlockObjByPtr(pObject);
1635
1636 return handle;
1637 }
1638
1639 W32KAPI
1640 BOOL
1641 APIENTRY
1642 NtGdiDeleteClientObj(
1643 IN HANDLE h
1644 )
1645 {
1646 /* We first need to get the real type from the handle */
1647 ULONG type = GDI_HANDLE_GET_TYPE(h);
1648
1649 /* Check if it's really a CLIENTOBJ */
1650 if ((type & GDI_HANDLE_BASETYPE_MASK) != GDILoObjType_LO_CLIENTOBJ_TYPE)
1651 {
1652 /* FIXME: SetLastError? */
1653 return FALSE;
1654 }
1655 return GDIOBJ_FreeObjByHandle(h, type);
1656 }
1657
1658 INT
1659 FASTCALL
1660 IntGdiGetObject(IN HANDLE Handle,
1661 IN INT cbCount,
1662 IN LPVOID lpBuffer)
1663 {
1664 PVOID pGdiObject;
1665 INT Result = 0;
1666 DWORD dwObjectType;
1667
1668 pGdiObject = GDIOBJ_LockObj(Handle, GDI_OBJECT_TYPE_DONTCARE);
1669 if (!pGdiObject)
1670 {
1671 SetLastWin32Error(ERROR_INVALID_HANDLE);
1672 return 0;
1673 }
1674
1675 dwObjectType = GDIOBJ_GetObjectType(Handle);
1676 switch (dwObjectType)
1677 {
1678 case GDI_OBJECT_TYPE_PEN:
1679 case GDI_OBJECT_TYPE_EXTPEN:
1680 Result = PEN_GetObject((PGDIBRUSHOBJ) pGdiObject, cbCount, (PLOGPEN) lpBuffer); // IntGdiCreatePenIndirect
1681 break;
1682
1683 case GDI_OBJECT_TYPE_BRUSH:
1684 Result = BRUSH_GetObject((PGDIBRUSHOBJ ) pGdiObject, cbCount, (LPLOGBRUSH)lpBuffer);
1685 break;
1686
1687 case GDI_OBJECT_TYPE_BITMAP:
1688 Result = BITMAP_GetObject((SURFACE *) pGdiObject, cbCount, lpBuffer);
1689 break;
1690 case GDI_OBJECT_TYPE_FONT:
1691 Result = FontGetObject((PTEXTOBJ) pGdiObject, cbCount, lpBuffer);
1692 #if 0
1693 // Fix the LOGFONT structure for the stock fonts
1694 if (FIRST_STOCK_HANDLE <= Handle && Handle <= LAST_STOCK_HANDLE)
1695 {
1696 FixStockFontSizeW(Handle, cbCount, lpBuffer);
1697 }
1698 #endif
1699 break;
1700
1701 case GDI_OBJECT_TYPE_PALETTE:
1702 Result = PALETTE_GetObject((PPALGDI) pGdiObject, cbCount, lpBuffer);
1703 break;
1704
1705 default:
1706 DPRINT1("GDI object type 0x%08x not implemented\n", dwObjectType);
1707 break;
1708 }
1709
1710 GDIOBJ_UnlockObjByPtr(pGdiObject);
1711
1712 return Result;
1713 }
1714
1715
1716
1717 W32KAPI
1718 INT
1719 APIENTRY
1720 NtGdiExtGetObjectW(IN HANDLE hGdiObj,
1721 IN INT cbCount,
1722 OUT LPVOID lpBuffer)
1723 {
1724 INT iRetCount = 0;
1725 INT cbCopyCount;
1726 union
1727 {
1728 BITMAP bitmap;
1729 DIBSECTION dibsection;
1730 LOGPEN logpen;
1731 LOGBRUSH logbrush;
1732 LOGFONTW logfontw;
1733 EXTLOGFONTW extlogfontw;
1734 ENUMLOGFONTEXDVW enumlogfontexdvw;
1735 } Object;
1736
1737 // Normalize to the largest supported object size
1738 cbCount = min((UINT)cbCount, sizeof(Object));
1739
1740 // Now do the actual call
1741 iRetCount = IntGdiGetObject(hGdiObj, cbCount, lpBuffer ? &Object : NULL);
1742 cbCopyCount = min((UINT)cbCount, (UINT)iRetCount);
1743
1744 // Make sure we have a buffer and a copy size
1745 if ((cbCopyCount) && (lpBuffer))
1746 {
1747 // Enter SEH for buffer transfer
1748 _SEH2_TRY
1749 {
1750 // Probe the buffer and copy it
1751 ProbeForWrite(lpBuffer, cbCopyCount, sizeof(WORD));
1752 RtlCopyMemory(lpBuffer, &Object, cbCopyCount);
1753 }
1754 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1755 {
1756 // Clear the return value.
1757 // Do *NOT* set last error here!
1758 iRetCount = 0;
1759 }
1760 _SEH2_END;
1761 }
1762 // Return the count
1763 return iRetCount;
1764 }
1765
1766 /* EOF */