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