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