xref: /reactos/ntoskrnl/ex/handle.c (revision 579eab8a)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ex/handle.c
5  * PURPOSE:         Generic Executive Handle Tables
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Thomas Weidenmueller <w3seek@reactos.com>
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 LIST_ENTRY HandleTableListHead;
19 EX_PUSH_LOCK HandleTableListLock;
20 #define SizeOfHandle(x) (sizeof(HANDLE) * (x))
21 #define INDEX_TO_HANDLE_VALUE(x) ((x) << HANDLE_TAG_BITS)
22 
23 /* PRIVATE FUNCTIONS *********************************************************/
24 
25 #ifdef _WIN64
26 #define strtoulptr strtoull
27 #else
28 #define strtoulptr strtoul
29 #endif
30 
31 CODE_SEG("INIT")
32 VOID
33 NTAPI
ExpInitializeHandleTables(VOID)34 ExpInitializeHandleTables(VOID)
35 {
36     /* Initialize the list of handle tables and the lock */
37     InitializeListHead(&HandleTableListHead);
38     ExInitializePushLock(&HandleTableListLock);
39 }
40 
41 PHANDLE_TABLE_ENTRY
42 NTAPI
ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,IN EXHANDLE Handle)43 ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,
44                           IN EXHANDLE Handle)
45 {
46     ULONG TableLevel;
47     ULONG_PTR TableBase;
48     PHANDLE_TABLE_ENTRY HandleArray, Entry;
49     PVOID *PointerArray;
50 
51     /* Clear the tag bits */
52     Handle.TagBits = 0;
53 
54     /* Check if the handle is in the allocated range */
55     if (Handle.Value >= HandleTable->NextHandleNeedingPool)
56     {
57         return NULL;
58     }
59 
60     /* Get the table code */
61     TableBase = HandleTable->TableCode;
62 
63     /* Extract the table level and actual table base */
64     TableLevel = (ULONG)(TableBase & 3);
65     TableBase &= ~3;
66 
67     PointerArray = (PVOID*)TableBase;
68     HandleArray = (PHANDLE_TABLE_ENTRY)TableBase;
69 
70     /* Check what level we're running at */
71     switch (TableLevel)
72     {
73         case 2:
74 
75             /* Get the mid level pointer array */
76             PointerArray = PointerArray[Handle.HighIndex];
77             ASSERT(PointerArray != NULL);
78 
79             /* Fall through */
80         case 1:
81 
82             /* Get the handle array */
83             HandleArray = PointerArray[Handle.MidIndex];
84             ASSERT(HandleArray != NULL);
85 
86             /* Fall through */
87         case 0:
88 
89             /* Get the entry using the low index */
90             Entry = &HandleArray[Handle.LowIndex];
91 
92             /* All done */
93             break;
94 
95         default:
96 
97             ASSERT(FALSE);
98             Entry = NULL;
99     }
100 
101     /* Return the handle entry */
102     return Entry;
103 }
104 
105 PVOID
106 NTAPI
ExpAllocateTablePagedPool(IN PEPROCESS Process OPTIONAL,IN SIZE_T Size)107 ExpAllocateTablePagedPool(IN PEPROCESS Process OPTIONAL,
108                           IN SIZE_T Size)
109 {
110     PVOID Buffer;
111     NTSTATUS Status;
112 
113     /* Do the allocation */
114     Buffer = ExAllocatePoolWithTag(PagedPool, Size, TAG_OBJECT_TABLE);
115     if (Buffer)
116     {
117         /* Clear the memory */
118         RtlZeroMemory(Buffer, Size);
119 
120         /* Check if we have a process to charge quota */
121         if (Process)
122         {
123             /* Charge quota */
124             Status = PsChargeProcessPagedPoolQuota(Process, Size);
125             if (!NT_SUCCESS(Status))
126             {
127                 ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE);
128                 return NULL;
129             }
130         }
131     }
132 
133     /* Return the allocated memory */
134     return Buffer;
135 }
136 
137 PVOID
138 NTAPI
ExpAllocateTablePagedPoolNoZero(IN PEPROCESS Process OPTIONAL,IN SIZE_T Size)139 ExpAllocateTablePagedPoolNoZero(IN PEPROCESS Process OPTIONAL,
140                                 IN SIZE_T Size)
141 {
142     PVOID Buffer;
143     NTSTATUS Status;
144 
145     /* Do the allocation */
146     Buffer = ExAllocatePoolWithTag(PagedPool, Size, TAG_OBJECT_TABLE);
147     if (Buffer)
148     {
149         /* Check if we have a process to charge quota */
150         if (Process)
151         {
152             /* Charge quota */
153             Status = PsChargeProcessPagedPoolQuota(Process, Size);
154             if (!NT_SUCCESS(Status))
155             {
156                 ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE);
157                 return NULL;
158             }
159         }
160     }
161 
162     /* Return the allocated memory */
163     return Buffer;
164 }
165 
166 VOID
167 NTAPI
ExpFreeTablePagedPool(IN PEPROCESS Process OPTIONAL,IN PVOID Buffer,IN SIZE_T Size)168 ExpFreeTablePagedPool(IN PEPROCESS Process OPTIONAL,
169                       IN PVOID Buffer,
170                       IN SIZE_T Size)
171 {
172     /* Free the buffer */
173     ExFreePoolWithTag(Buffer, TAG_OBJECT_TABLE);
174     if (Process)
175     {
176         /* Release quota */
177         PsReturnProcessPagedPoolQuota(Process, Size);
178     }
179 }
180 
181 VOID
182 NTAPI
ExpFreeLowLevelTable(IN PEPROCESS Process,IN PHANDLE_TABLE_ENTRY TableEntry)183 ExpFreeLowLevelTable(IN PEPROCESS Process,
184                      IN PHANDLE_TABLE_ENTRY TableEntry)
185 {
186     /* Check if we have an entry */
187     if (TableEntry[0].Object)
188     {
189         /* Free the entry */
190         ExpFreeTablePagedPool(Process,
191                               TableEntry[0].Object,
192                               LOW_LEVEL_ENTRIES *
193                               sizeof(HANDLE_TABLE_ENTRY_INFO));
194     }
195 
196     /* Free the table */
197     ExpFreeTablePagedPool(Process, TableEntry, PAGE_SIZE);
198 }
199 
200 VOID
201 NTAPI
ExpFreeHandleTable(IN PHANDLE_TABLE HandleTable)202 ExpFreeHandleTable(IN PHANDLE_TABLE HandleTable)
203 {
204     PEPROCESS Process = HandleTable->QuotaProcess;
205     ULONG i, j;
206     ULONG_PTR TableCode = HandleTable->TableCode;
207     ULONG_PTR TableBase = TableCode & ~3;
208     ULONG TableLevel = (ULONG)(TableCode & 3);
209     PHANDLE_TABLE_ENTRY Level1, *Level2, **Level3;
210     PAGED_CODE();
211 
212     /* Check which level we're at */
213     if (TableLevel == 0)
214     {
215         /* Select the first level table base and just free it */
216         Level1 = (PVOID)TableBase;
217         ExpFreeLowLevelTable(Process, Level1);
218     }
219     else if (TableLevel == 1)
220     {
221         /* Select the second level table base */
222         Level2 = (PVOID)TableBase;
223 
224         /* Loop each mid level entry */
225         for (i = 0; i < MID_LEVEL_ENTRIES; i++)
226         {
227             /* Leave if we've reached the last entry */
228             if (!Level2[i]) break;
229 
230             /* Free the second level table */
231             ExpFreeLowLevelTable(Process, Level2[i]);
232         }
233 
234         /* Free the second level table */
235         ExpFreeTablePagedPool(Process, Level2, PAGE_SIZE);
236     }
237     else
238     {
239         /* Select the third level table base */
240         Level3 = (PVOID)TableBase;
241 
242         /* Loop each high level entry */
243         for (i = 0; i < HIGH_LEVEL_ENTRIES; i++)
244         {
245             /* Leave if we've reached the last entry */
246             if (!Level3[i]) break;
247 
248             /* Loop each mid level entry */
249             for (j = 0; j < MID_LEVEL_ENTRIES; j++)
250             {
251                 /* Leave if we've reached the last entry */
252                 if (!Level3[i][j]) break;
253 
254                 /* Free the second level table */
255                 ExpFreeLowLevelTable(Process, Level3[i][j]);
256             }
257 
258             /* Free the third level table entry */
259             ExpFreeTablePagedPool(Process, Level3[i], PAGE_SIZE);
260         }
261 
262         /* Free the third level table */
263         ExpFreeTablePagedPool(Process,
264                               Level3,
265                               SizeOfHandle(HIGH_LEVEL_ENTRIES));
266     }
267 
268     /* Free the actual table and check if we need to release quota */
269     ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE);
270     if (Process)
271     {
272         /* Release the quota it was taking up */
273         PsReturnProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE));
274     }
275 }
276 
277 VOID
278 NTAPI
ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable,IN EXHANDLE Handle,IN PHANDLE_TABLE_ENTRY HandleTableEntry)279 ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable,
280                         IN EXHANDLE Handle,
281                         IN PHANDLE_TABLE_ENTRY HandleTableEntry)
282 {
283     ULONG OldValue, *Free;
284     ULONG LockIndex;
285     PAGED_CODE();
286 
287     /* Sanity checks */
288     ASSERT(HandleTableEntry->Object == NULL);
289     ASSERT(HandleTableEntry == ExpLookupHandleTableEntry(HandleTable, Handle));
290 
291     /* Decrement the handle count */
292     InterlockedDecrement(&HandleTable->HandleCount);
293 
294     /* Mark the handle as free */
295     Handle.TagBits = 0;
296 
297     /* Check if we're FIFO */
298     if (!HandleTable->StrictFIFO)
299     {
300         /* Select a lock index */
301         LockIndex = Handle.Index % 4;
302 
303         /* Select which entry to use */
304         Free = (HandleTable->HandleTableLock[LockIndex].Locked) ?
305                 &HandleTable->FirstFree : &HandleTable->LastFree;
306     }
307     else
308     {
309         /* No need to worry about locking, take the last entry */
310         Free = &HandleTable->LastFree;
311     }
312 
313     /* Start value change loop */
314     for (;;)
315     {
316         /* Get the current value and write */
317         OldValue = *Free;
318         HandleTableEntry->NextFreeTableEntry = OldValue;
319         if (InterlockedCompareExchange((PLONG)Free, Handle.AsULONG, OldValue) == OldValue)
320         {
321             /* Break out, we're done. Make sure the handle value makes sense */
322             ASSERT((OldValue & FREE_HANDLE_MASK) <
323                    HandleTable->NextHandleNeedingPool);
324             break;
325         }
326     }
327 }
328 
329 PHANDLE_TABLE
330 NTAPI
ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL,IN BOOLEAN NewTable)331 ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL,
332                        IN BOOLEAN NewTable)
333 {
334     PHANDLE_TABLE HandleTable;
335     PHANDLE_TABLE_ENTRY HandleTableTable, HandleEntry;
336     ULONG i;
337     NTSTATUS Status;
338     PAGED_CODE();
339 
340     /* Allocate the table */
341     HandleTable = ExAllocatePoolWithTag(PagedPool,
342                                         sizeof(HANDLE_TABLE),
343                                         TAG_OBJECT_TABLE);
344     if (!HandleTable) return NULL;
345 
346     /* Check if we have a process */
347     if (Process)
348     {
349         /* Charge quota */
350         Status = PsChargeProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE));
351         if (!NT_SUCCESS(Status))
352         {
353             ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE);
354             return NULL;
355         }
356     }
357 
358     /* Clear the table */
359     RtlZeroMemory(HandleTable, sizeof(HANDLE_TABLE));
360 
361     /* Now allocate the first level structures */
362     HandleTableTable = ExpAllocateTablePagedPoolNoZero(Process, PAGE_SIZE);
363     if (!HandleTableTable)
364     {
365         /* Failed, free the table */
366         ExFreePoolWithTag(HandleTable, TAG_OBJECT_TABLE);
367 
368         /* Return the quota it was taking up */
369         if (Process)
370         {
371             PsReturnProcessPagedPoolQuota(Process, sizeof(HANDLE_TABLE));
372         }
373 
374         return NULL;
375     }
376 
377     /* Write the pointer to our first level structures */
378     HandleTable->TableCode = (ULONG_PTR)HandleTableTable;
379 
380     /* Initialize the first entry */
381     HandleEntry = &HandleTableTable[0];
382     HandleEntry->NextFreeTableEntry = -2;
383     HandleEntry->Value = 0;
384 
385     /* Check if this is a new table */
386     if (NewTable)
387     {
388         /* Go past the root entry */
389         HandleEntry++;
390 
391         /* Loop every low level entry */
392         for (i = 1; i < (LOW_LEVEL_ENTRIES - 1); i++)
393         {
394             /* Set up the free data */
395             HandleEntry->Value = 0;
396             HandleEntry->NextFreeTableEntry = INDEX_TO_HANDLE_VALUE(i + 1);
397 
398             /* Move to the next entry */
399             HandleEntry++;
400         }
401 
402         /* Terminate the last entry */
403         HandleEntry->Value = 0;
404         HandleEntry->NextFreeTableEntry = 0;
405         HandleTable->FirstFree = INDEX_TO_HANDLE_VALUE(1);
406     }
407 
408     /* Set the next handle needing pool after our allocated page from above */
409     HandleTable->NextHandleNeedingPool = INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES);
410 
411     /* Setup the rest of the handle table data */
412     HandleTable->QuotaProcess = Process;
413     HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId;
414     HandleTable->Flags = 0;
415 
416     /* Loop all the handle table locks */
417     for (i = 0; i < 4; i++)
418     {
419         /* Initialize the handle table lock */
420         ExInitializePushLock(&HandleTable->HandleTableLock[i]);
421     }
422 
423     /* Initialize the contention event lock and return the lock */
424     ExInitializePushLock(&HandleTable->HandleContentionEvent);
425     return HandleTable;
426 }
427 
428 PHANDLE_TABLE_ENTRY
429 NTAPI
ExpAllocateLowLevelTable(IN PHANDLE_TABLE HandleTable,IN BOOLEAN DoInit)430 ExpAllocateLowLevelTable(IN PHANDLE_TABLE HandleTable,
431                          IN BOOLEAN DoInit)
432 {
433     ULONG i, Base;
434     PHANDLE_TABLE_ENTRY Low, HandleEntry;
435 
436     /* Allocate the low level table */
437     Low = ExpAllocateTablePagedPoolNoZero(HandleTable->QuotaProcess,
438                                           PAGE_SIZE);
439     if (!Low) return NULL;
440 
441     /* Setup the initial entry */
442     HandleEntry = &Low[0];
443     HandleEntry->NextFreeTableEntry = -2;
444     HandleEntry->Value = 0;
445 
446     /* Check if we're initializing */
447     if (DoInit)
448     {
449         /* Go to the next entry and the base entry */
450         HandleEntry++;
451         Base = HandleTable->NextHandleNeedingPool + INDEX_TO_HANDLE_VALUE(2);
452 
453         /* Loop each entry */
454         for (i = Base;
455              i < Base + INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES - 2);
456              i += INDEX_TO_HANDLE_VALUE(1))
457         {
458             /* Free this entry and move on to the next one */
459             HandleEntry->NextFreeTableEntry = i;
460             HandleEntry->Value = 0;
461             HandleEntry++;
462         }
463 
464         /* Terminate the last entry */
465         HandleEntry->NextFreeTableEntry = 0;
466         HandleEntry->Value = 0;
467     }
468 
469     /* Return the low level table */
470     return Low;
471 }
472 
473 PHANDLE_TABLE_ENTRY*
474 NTAPI
ExpAllocateMidLevelTable(IN PHANDLE_TABLE HandleTable,IN BOOLEAN DoInit,OUT PHANDLE_TABLE_ENTRY * LowTableEntry)475 ExpAllocateMidLevelTable(IN PHANDLE_TABLE HandleTable,
476                          IN BOOLEAN DoInit,
477                          OUT PHANDLE_TABLE_ENTRY *LowTableEntry)
478 {
479     PHANDLE_TABLE_ENTRY *Mid, Low;
480 
481     /* Allocate the mid level table */
482     Mid = ExpAllocateTablePagedPool(HandleTable->QuotaProcess, PAGE_SIZE);
483     if (!Mid) return NULL;
484 
485     /* Allocate a new low level for it */
486     Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
487     if (!Low)
488     {
489         /* We failed, free the mid table */
490         ExpFreeTablePagedPool(HandleTable->QuotaProcess, Mid, PAGE_SIZE);
491         return NULL;
492     }
493 
494     /* Link the tables and return the pointer */
495     Mid[0] = Low;
496     *LowTableEntry = Low;
497     return Mid;
498 }
499 
500 BOOLEAN
501 NTAPI
ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable,IN BOOLEAN DoInit)502 ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable,
503                                 IN BOOLEAN DoInit)
504 {
505     ULONG i, j, Index;
506     PHANDLE_TABLE_ENTRY Low = NULL, *Mid, **High, *SecondLevel, **ThirdLevel;
507     ULONG NewFree, FirstFree;
508     PVOID Value;
509     ULONG_PTR TableCode = HandleTable->TableCode;
510     ULONG_PTR TableBase = TableCode & ~3;
511     ULONG TableLevel = (ULONG)(TableCode & 3);
512     PAGED_CODE();
513 
514     /* Check how many levels we already have */
515     if (TableLevel == 0)
516     {
517         /* Allocate a mid level, since we only have a low level */
518         Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
519         if (!Mid) return FALSE;
520 
521         /* Link up the tables */
522         Mid[1] = Mid[0];
523         Mid[0] = (PVOID)TableBase;
524 
525         /* Write the new level and attempt to change the table code */
526         TableBase = ((ULONG_PTR)Mid) | 1;
527         Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode, (PVOID)TableBase);
528     }
529     else if (TableLevel == 1)
530     {
531         /* Setup the 2nd level table */
532         SecondLevel = (PVOID)TableBase;
533 
534         /* Get if the next index can fit in the table */
535         i = HandleTable->NextHandleNeedingPool /
536             INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES);
537         if (i < MID_LEVEL_ENTRIES)
538         {
539             /* We need to allocate a new table */
540             Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
541             if (!Low) return FALSE;
542 
543             /* Update the table */
544             Value = InterlockedExchangePointer((PVOID*)&SecondLevel[i], Low);
545             ASSERT(Value == NULL);
546         }
547         else
548         {
549             /* We need a new high level table */
550             High = ExpAllocateTablePagedPool(HandleTable->QuotaProcess,
551                                              SizeOfHandle(HIGH_LEVEL_ENTRIES));
552             if (!High) return FALSE;
553 
554             /* Allocate a new mid level table as well */
555             Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
556             if (!Mid)
557             {
558                 /* We failed, free the high level table as well */
559                 ExpFreeTablePagedPool(HandleTable->QuotaProcess,
560                                       High,
561                                       SizeOfHandle(HIGH_LEVEL_ENTRIES));
562                 return FALSE;
563             }
564 
565             /* Link up the tables */
566             High[0] = (PVOID)TableBase;
567             High[1] = Mid;
568 
569             /* Write the new table and change the table code */
570             TableBase = ((ULONG_PTR)High) | 2;
571             Value = InterlockedExchangePointer((PVOID*)&HandleTable->TableCode,
572                                                (PVOID)TableBase);
573         }
574     }
575     else if (TableLevel == 2)
576     {
577         /* Setup the 3rd level table */
578         ThirdLevel = (PVOID)TableBase;
579 
580         /* Get the index and check if it can fit */
581         i = HandleTable->NextHandleNeedingPool / INDEX_TO_HANDLE_VALUE(MAX_MID_INDEX);
582         if (i >= HIGH_LEVEL_ENTRIES) return FALSE;
583 
584         /* Check if there's no mid-level table */
585         if (!ThirdLevel[i])
586         {
587             /* Allocate a new mid level table */
588             Mid = ExpAllocateMidLevelTable(HandleTable, DoInit, &Low);
589             if (!Mid) return FALSE;
590 
591             /* Update the table pointer */
592             Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i], Mid);
593             ASSERT(Value == NULL);
594         }
595         else
596         {
597             /* We have one, check at which index we should insert our entry */
598             Index = (HandleTable->NextHandleNeedingPool / INDEX_TO_HANDLE_VALUE(1)) -
599                      i * MAX_MID_INDEX;
600             j = Index / LOW_LEVEL_ENTRIES;
601 
602             /* Allocate a new low level */
603             Low = ExpAllocateLowLevelTable(HandleTable, DoInit);
604             if (!Low) return FALSE;
605 
606             /* Update the table pointer */
607             Value = InterlockedExchangePointer((PVOID*)&ThirdLevel[i][j], Low);
608             ASSERT(Value == NULL);
609         }
610     }
611     else
612     {
613         /* Something is really broken */
614         ASSERT(FALSE);
615     }
616 
617     /* Update the index of the next handle */
618     Index = InterlockedExchangeAdd((PLONG) &HandleTable->NextHandleNeedingPool,
619                                    INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES));
620 
621     /* Check if need to initialize the table */
622     if (DoInit)
623     {
624         /* Create a new index number */
625         Index += INDEX_TO_HANDLE_VALUE(1);
626 
627         /* Start free index change loop */
628         for (;;)
629         {
630             /* Setup the first free index */
631             FirstFree = HandleTable->FirstFree;
632             Low[LOW_LEVEL_ENTRIES - 1].NextFreeTableEntry = FirstFree;
633 
634             /* Change the index */
635             NewFree = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree,
636                                                  Index,
637                                                  FirstFree);
638             if (NewFree == FirstFree) break;
639         }
640     }
641 
642     /* All done */
643     return TRUE;
644 }
645 
646 ULONG
647 NTAPI
ExpMoveFreeHandles(IN PHANDLE_TABLE HandleTable)648 ExpMoveFreeHandles(IN PHANDLE_TABLE HandleTable)
649 {
650     ULONG LastFree, i;
651 
652     /* Clear the last free index */
653     LastFree = InterlockedExchange((PLONG) &HandleTable->LastFree, 0);
654 
655     /* Check if we had no index */
656     if (!LastFree) return LastFree;
657 
658     /* Acquire the locks we need */
659     for (i = 1; i < 4; i++)
660     {
661         /* Acquire this lock exclusively */
662         ExWaitOnPushLock(&HandleTable->HandleTableLock[i]);
663     }
664 
665     /* Check if we're not strict FIFO */
666     if (!HandleTable->StrictFIFO)
667     {
668         /* Update the first free index */
669         if (!InterlockedCompareExchange((PLONG) &HandleTable->FirstFree, LastFree, 0))
670         {
671             /* We're done, exit */
672             return LastFree;
673         }
674     }
675 
676     /* We are strict FIFO, we need to reverse the entries */
677     ASSERT(FALSE);
678     return LastFree;
679 }
680 
681 PHANDLE_TABLE_ENTRY
682 NTAPI
ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,OUT PEXHANDLE NewHandle)683 ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable,
684                             OUT PEXHANDLE NewHandle)
685 {
686     ULONG OldValue, NewValue, NewValue1;
687     PHANDLE_TABLE_ENTRY Entry;
688     EXHANDLE Handle, OldHandle;
689     BOOLEAN Result;
690     ULONG i;
691 
692     /* Start allocation loop */
693     for (;;)
694     {
695         /* Get the current link */
696         OldValue = HandleTable->FirstFree;
697         while (!OldValue)
698         {
699             /* No free entries remain, lock the handle table */
700             KeEnterCriticalRegion();
701             ExAcquirePushLockExclusive(&HandleTable->HandleTableLock[0]);
702 
703             /* Check the value again */
704             OldValue = HandleTable->FirstFree;
705             if (OldValue)
706             {
707                 /* Another thread has already created a new level, bail out */
708                 ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
709                 KeLeaveCriticalRegion();
710                 break;
711             }
712 
713             /* Now move any free handles */
714             OldValue = ExpMoveFreeHandles(HandleTable);
715             if (OldValue)
716             {
717                 /* Another thread has already moved them, bail out */
718                 ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
719                 KeLeaveCriticalRegion();
720                 break;
721             }
722 
723             /* We're the first one through, so do the actual allocation */
724             Result = ExpAllocateHandleTableEntrySlow(HandleTable, TRUE);
725 
726             /* Unlock the table and get the value now */
727             ExReleasePushLockExclusive(&HandleTable->HandleTableLock[0]);
728             KeLeaveCriticalRegion();
729             OldValue = HandleTable->FirstFree;
730 
731             /* Check if allocation failed */
732             if (!Result)
733             {
734                 /* Check if nobody else went through here */
735                 if (!OldValue)
736                 {
737                     /* We're still the only thread around, so fail */
738                     NewHandle->GenericHandleOverlay = NULL;
739                     return NULL;
740                 }
741             }
742         }
743 
744         /* We made it, write the current value */
745         Handle.Value = (OldValue & FREE_HANDLE_MASK);
746 
747         /* Lookup the entry for this handle */
748         Entry = ExpLookupHandleTableEntry(HandleTable, Handle);
749 
750         /* Get an available lock and acquire it */
751         OldHandle.Value = OldValue;
752         i = OldHandle.Index % 4;
753         KeEnterCriticalRegion();
754         ExAcquirePushLockShared(&HandleTable->HandleTableLock[i]);
755 
756         /* Check if the value changed after acquiring the lock */
757         if (OldValue != *(volatile ULONG*)&HandleTable->FirstFree)
758         {
759             /* It did, so try again */
760             ExReleasePushLockShared(&HandleTable->HandleTableLock[i]);
761             KeLeaveCriticalRegion();
762             continue;
763         }
764 
765         /* Now get the next value and do the compare */
766         NewValue = *(volatile ULONG*)&Entry->NextFreeTableEntry;
767         NewValue1 = InterlockedCompareExchange((PLONG) &HandleTable->FirstFree,
768                                                NewValue,
769                                                OldValue);
770 
771         /* The change was done, so release the lock */
772         ExReleasePushLockShared(&HandleTable->HandleTableLock[i]);
773         KeLeaveCriticalRegion();
774 
775         /* Check if the compare was successful */
776         if (NewValue1 == OldValue)
777         {
778             /* Make sure that the new handle is in range, and break out */
779             ASSERT((NewValue & FREE_HANDLE_MASK) <
780                    HandleTable->NextHandleNeedingPool);
781             break;
782         }
783         else
784         {
785             /* The compare failed, make sure we expected it */
786             ASSERT((NewValue1 & FREE_HANDLE_MASK) !=
787                    (OldValue & FREE_HANDLE_MASK));
788         }
789     }
790 
791     /* Increase the number of handles */
792     InterlockedIncrement(&HandleTable->HandleCount);
793 
794     /* Return the handle and the entry */
795     *NewHandle = Handle;
796     return Entry;
797 }
798 
799 PHANDLE_TABLE
800 NTAPI
ExCreateHandleTable(IN PEPROCESS Process OPTIONAL)801 ExCreateHandleTable(IN PEPROCESS Process OPTIONAL)
802 {
803     PHANDLE_TABLE HandleTable;
804     PAGED_CODE();
805 
806     /* Allocate the handle table */
807     HandleTable = ExpAllocateHandleTable(Process, TRUE);
808     if (!HandleTable) return NULL;
809 
810     /* Acquire the handle table lock */
811     KeEnterCriticalRegion();
812     ExAcquirePushLockExclusive(&HandleTableListLock);
813 
814     /* Insert it into the list */
815     InsertTailList(&HandleTableListHead, &HandleTable->HandleTableList);
816 
817     /* Release the lock */
818     ExReleasePushLockExclusive(&HandleTableListLock);
819     KeLeaveCriticalRegion();
820 
821     /* Return the handle table */
822     return HandleTable;
823 }
824 
825 HANDLE
826 NTAPI
ExCreateHandle(IN PHANDLE_TABLE HandleTable,IN PHANDLE_TABLE_ENTRY HandleTableEntry)827 ExCreateHandle(IN PHANDLE_TABLE HandleTable,
828                IN PHANDLE_TABLE_ENTRY HandleTableEntry)
829 {
830     EXHANDLE Handle;
831     PHANDLE_TABLE_ENTRY NewEntry;
832     PAGED_CODE();
833 
834     /* Start with a clean handle */
835     Handle.GenericHandleOverlay = NULL;
836 
837     /* Allocate a new entry */
838     NewEntry = ExpAllocateHandleTableEntry(HandleTable, &Handle);
839     if (NewEntry)
840     {
841         /* Enter a critical region */
842         KeEnterCriticalRegion();
843 
844         /* Write the entry */
845         *NewEntry = *HandleTableEntry;
846 
847         /* Unlock it and leave the critical region */
848         ExUnlockHandleTableEntry(HandleTable, NewEntry);
849         KeLeaveCriticalRegion();
850     }
851 
852     /* Return the handle value */
853     return Handle.GenericHandleOverlay;
854 }
855 
856 VOID
857 NTAPI
ExpBlockOnLockedHandleEntry(IN PHANDLE_TABLE HandleTable,IN PHANDLE_TABLE_ENTRY HandleTableEntry)858 ExpBlockOnLockedHandleEntry(IN PHANDLE_TABLE HandleTable,
859                             IN PHANDLE_TABLE_ENTRY HandleTableEntry)
860 {
861     LONG_PTR OldValue;
862     EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
863 
864     /* Block on the pushlock */
865     ExBlockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock);
866 
867     /* Get the current value and check if it's been unlocked */
868     OldValue = HandleTableEntry->Value;
869     if (!(OldValue) || (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT))
870     {
871         /* Unblock the pushlock and return */
872         ExfUnblockPushLock(&HandleTable->HandleContentionEvent, &WaitBlock);
873     }
874     else
875     {
876         /* Wait for it to be unblocked */
877         ExWaitForUnblockPushLock(&HandleTable->HandleContentionEvent,
878                                  &WaitBlock);
879     }
880 }
881 
882 BOOLEAN
883 NTAPI
ExpLockHandleTableEntry(IN PHANDLE_TABLE HandleTable,IN PHANDLE_TABLE_ENTRY HandleTableEntry)884 ExpLockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
885                         IN PHANDLE_TABLE_ENTRY HandleTableEntry)
886 {
887     LONG_PTR NewValue, OldValue;
888 
889     /* Sanity check */
890     ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) ||
891            (KeGetCurrentIrql() == APC_LEVEL));
892 
893     /* Start lock loop */
894     for (;;)
895     {
896         /* Get the current value and check if it's locked */
897         OldValue = *(volatile LONG_PTR *)&HandleTableEntry->Object;
898         if (OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT)
899         {
900             /* It's not locked, remove the lock bit to lock it */
901             NewValue = OldValue & ~EXHANDLE_TABLE_ENTRY_LOCK_BIT;
902             if (InterlockedCompareExchangePointer(&HandleTableEntry->Object,
903                                                   (PVOID)NewValue,
904                                                   (PVOID)OldValue) == (PVOID)OldValue)
905             {
906                 /* We locked it, get out */
907                 return TRUE;
908             }
909         }
910         else
911         {
912             /* We couldn't lock it, bail out if it's been freed */
913             if (!OldValue) return FALSE;
914         }
915 
916         /* It's locked, wait for it to be unlocked */
917         ExpBlockOnLockedHandleEntry(HandleTable, HandleTableEntry);
918     }
919 }
920 
921 VOID
922 NTAPI
ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable,IN PHANDLE_TABLE_ENTRY HandleTableEntry)923 ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable,
924                          IN PHANDLE_TABLE_ENTRY HandleTableEntry)
925 {
926     LONG_PTR OldValue;
927     PAGED_CODE();
928 
929     /* Sanity check */
930     ASSERT((KeGetCurrentThread()->CombinedApcDisable != 0) ||
931            (KeGetCurrentIrql() == APC_LEVEL));
932 
933     /* Set the lock bit and make sure it wasn't earlier */
934     OldValue = InterlockedOr((PLONG) &HandleTableEntry->Value,
935                              EXHANDLE_TABLE_ENTRY_LOCK_BIT);
936     ASSERT((OldValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
937 
938     /* Unblock any waiters */
939     ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL);
940 }
941 
942 VOID
943 NTAPI
ExRemoveHandleTable(IN PHANDLE_TABLE HandleTable)944 ExRemoveHandleTable(IN PHANDLE_TABLE HandleTable)
945 {
946     PAGED_CODE();
947 
948     /* Acquire the table lock */
949     KeEnterCriticalRegion();
950     ExAcquirePushLockExclusive(&HandleTableListLock);
951 
952     /* Remove the table and reset the list */
953     RemoveEntryList(&HandleTable->HandleTableList);
954     InitializeListHead(&HandleTable->HandleTableList);
955 
956     /* Release the lock */
957     ExReleasePushLockExclusive(&HandleTableListLock);
958     KeLeaveCriticalRegion();
959 }
960 
961 VOID
962 NTAPI
ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable,IN PVOID DestroyHandleProcedure OPTIONAL)963 ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable,
964                      IN PVOID DestroyHandleProcedure OPTIONAL)
965 {
966     PAGED_CODE();
967 
968     /* Remove the handle from the list */
969     ExRemoveHandleTable(HandleTable);
970 
971     /* Check if we have a destroy callback */
972     if (DestroyHandleProcedure)
973     {
974         /* FIXME: */
975         ASSERT(FALSE);
976     }
977 
978     /* Free the handle table */
979     ExpFreeHandleTable(HandleTable);
980 }
981 
982 BOOLEAN
983 NTAPI
ExDestroyHandle(IN PHANDLE_TABLE HandleTable,IN HANDLE Handle,IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL)984 ExDestroyHandle(IN PHANDLE_TABLE HandleTable,
985                 IN HANDLE Handle,
986                 IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL)
987 {
988     EXHANDLE ExHandle;
989     PVOID Object;
990     PAGED_CODE();
991 
992     /* Setup the actual handle value */
993     ExHandle.GenericHandleOverlay = Handle;
994 
995     /* Enter a critical region and check if we have to lookup the handle */
996     KeEnterCriticalRegion();
997     if (!HandleTableEntry)
998     {
999         /* Lookup the entry */
1000         HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
1001 
1002         /* Make sure that we found an entry, and that it's valid */
1003         if (!(HandleTableEntry) ||
1004             !(HandleTableEntry->Object) ||
1005             (HandleTableEntry->NextFreeTableEntry == -2))
1006         {
1007             /* Invalid handle, fail */
1008             KeLeaveCriticalRegion();
1009             return FALSE;
1010         }
1011 
1012         /* Lock the entry */
1013         if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1014         {
1015             /* Couldn't lock, fail */
1016             KeLeaveCriticalRegion();
1017             return FALSE;
1018         }
1019     }
1020     else
1021     {
1022         /* Make sure the handle is locked */
1023         ASSERT((HandleTableEntry->Value & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
1024     }
1025 
1026     /* Clear the handle */
1027     Object = InterlockedExchangePointer((PVOID*)&HandleTableEntry->Object, NULL);
1028 
1029     /* Sanity checks */
1030     ASSERT(Object != NULL);
1031     ASSERT((((ULONG_PTR)Object) & EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
1032 
1033     /* Unblock the pushlock */
1034     ExfUnblockPushLock(&HandleTable->HandleContentionEvent, NULL);
1035 
1036     /* Free the actual entry */
1037     ExpFreeHandleTableEntry(HandleTable, ExHandle, HandleTableEntry);
1038 
1039     /* If we got here, return success */
1040     KeLeaveCriticalRegion();
1041     return TRUE;
1042 }
1043 
1044 PHANDLE_TABLE_ENTRY
1045 NTAPI
ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,IN HANDLE Handle)1046 ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
1047                      IN HANDLE Handle)
1048 {
1049     EXHANDLE ExHandle;
1050     PHANDLE_TABLE_ENTRY HandleTableEntry;
1051     PAGED_CODE();
1052 
1053     /* Set the handle value */
1054     ExHandle.GenericHandleOverlay = Handle;
1055 
1056     /* Fail if we got an invalid index */
1057     if (!(ExHandle.Index & (LOW_LEVEL_ENTRIES - 1))) return NULL;
1058 
1059     /* Do the lookup */
1060     HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
1061     if (!HandleTableEntry) return NULL;
1062 
1063     /* Lock it */
1064     if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) return NULL;
1065 
1066     /* Return the entry */
1067     return HandleTableEntry;
1068 }
1069 
1070 PHANDLE_TABLE
1071 NTAPI
ExDupHandleTable(IN PEPROCESS Process,IN PHANDLE_TABLE HandleTable,IN PEX_DUPLICATE_HANDLE_CALLBACK DupHandleProcedure,IN ULONG_PTR Mask)1072 ExDupHandleTable(IN PEPROCESS Process,
1073                  IN PHANDLE_TABLE HandleTable,
1074                  IN PEX_DUPLICATE_HANDLE_CALLBACK DupHandleProcedure,
1075                  IN ULONG_PTR Mask)
1076 {
1077     PHANDLE_TABLE NewTable;
1078     EXHANDLE Handle;
1079     PHANDLE_TABLE_ENTRY HandleTableEntry, NewEntry;
1080     BOOLEAN Failed = FALSE;
1081     PAGED_CODE();
1082 
1083     /* Allocate the duplicated copy */
1084     NewTable = ExpAllocateHandleTable(Process, FALSE);
1085     if (!NewTable) return NULL;
1086 
1087     /* Loop each entry */
1088     while (NewTable->NextHandleNeedingPool <
1089            HandleTable->NextHandleNeedingPool)
1090     {
1091         /* Insert it into the duplicated copy */
1092         if (!ExpAllocateHandleTableEntrySlow(NewTable, FALSE))
1093         {
1094             /* Insert failed, free the new copy and return */
1095             ExpFreeHandleTable(NewTable);
1096             return NULL;
1097         }
1098     }
1099 
1100     /* Setup the initial handle table data */
1101     NewTable->HandleCount = 0;
1102     NewTable->ExtraInfoPages = 0;
1103     NewTable->FirstFree = 0;
1104 
1105     /* Setup the first handle value  */
1106     Handle.Value = INDEX_TO_HANDLE_VALUE(1);
1107 
1108     /* Enter a critical region and lookup the new entry */
1109     KeEnterCriticalRegion();
1110     while ((NewEntry = ExpLookupHandleTableEntry(NewTable, Handle)))
1111     {
1112         /* Lookup the old entry */
1113         HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle);
1114 
1115         /* Loop each entry */
1116         do
1117         {
1118             /* Check if it doesn't match the audit mask */
1119             if (!(HandleTableEntry->Value & Mask))
1120             {
1121                 /* Free it since we won't use it */
1122                 Failed = TRUE;
1123             }
1124             else
1125             {
1126                 /* Lock the entry */
1127                 if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1128                 {
1129                     /* Free it since we can't lock it, so we won't use it */
1130                     Failed = TRUE;
1131                 }
1132                 else
1133                 {
1134                     /* Copy the handle value */
1135                     *NewEntry = *HandleTableEntry;
1136 
1137                     /* Call the duplicate callback */
1138                     if (DupHandleProcedure(Process,
1139                                            HandleTable,
1140                                            HandleTableEntry,
1141                                            NewEntry))
1142                     {
1143                         /* Clear failure flag */
1144                         Failed = FALSE;
1145 
1146                         /* Lock the entry, increase the handle count */
1147                         NewEntry->Value |= EXHANDLE_TABLE_ENTRY_LOCK_BIT;
1148                         NewTable->HandleCount++;
1149                     }
1150                     else
1151                     {
1152                         /* Duplication callback refused, fail */
1153                         Failed = TRUE;
1154                     }
1155                 }
1156             }
1157 
1158             /* Check if we failed earlier and need to free */
1159             if (Failed)
1160             {
1161                 /* Free this entry */
1162                 NewEntry->Object = NULL;
1163                 NewEntry->NextFreeTableEntry = NewTable->FirstFree;
1164                 NewTable->FirstFree = (ULONG)Handle.Value;
1165             }
1166 
1167             /* Increase the handle value and move to the next entry */
1168             Handle.Value += INDEX_TO_HANDLE_VALUE(1);
1169             NewEntry++;
1170             HandleTableEntry++;
1171         } while (Handle.Value % INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES));
1172 
1173         /* We're done, skip the last entry */
1174         Handle.Value += INDEX_TO_HANDLE_VALUE(1);
1175     }
1176 
1177     /* Acquire the table lock and insert this new table into the list */
1178     ExAcquirePushLockExclusive(&HandleTableListLock);
1179     InsertTailList(&HandleTableListHead, &NewTable->HandleTableList);
1180     ExReleasePushLockExclusive(&HandleTableListLock);
1181 
1182     /* Leave the critical region we entered previously and return the table */
1183     KeLeaveCriticalRegion();
1184     return NewTable;
1185 }
1186 
1187 BOOLEAN
1188 NTAPI
ExChangeHandle(IN PHANDLE_TABLE HandleTable,IN HANDLE Handle,IN PEX_CHANGE_HANDLE_CALLBACK ChangeRoutine,IN ULONG_PTR Context)1189 ExChangeHandle(IN PHANDLE_TABLE HandleTable,
1190                IN HANDLE Handle,
1191                IN PEX_CHANGE_HANDLE_CALLBACK ChangeRoutine,
1192                IN ULONG_PTR Context)
1193 {
1194     EXHANDLE ExHandle;
1195     PHANDLE_TABLE_ENTRY HandleTableEntry;
1196     BOOLEAN Result = FALSE;
1197     PAGED_CODE();
1198 
1199     /* Set the handle value */
1200     ExHandle.GenericHandleOverlay = Handle;
1201 
1202     /* Find the entry for this handle */
1203     HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle);
1204 
1205     /* Make sure that we found an entry, and that it's valid */
1206     if (!(HandleTableEntry) ||
1207         !(HandleTableEntry->Object) ||
1208         (HandleTableEntry->NextFreeTableEntry == -2))
1209     {
1210         /* It isn't, fail */
1211         return FALSE;
1212     }
1213 
1214     /* Enter a critical region */
1215     KeEnterCriticalRegion();
1216 
1217     /* Try locking the handle entry */
1218     if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1219     {
1220         /* Call the change routine and unlock the entry */
1221         Result = ChangeRoutine(HandleTableEntry, Context);
1222         ExUnlockHandleTableEntry(HandleTable, HandleTableEntry);
1223     }
1224 
1225     /* Leave the critical region and return the callback result */
1226     KeLeaveCriticalRegion();
1227     return Result;
1228 }
1229 
1230 VOID
1231 NTAPI
ExSweepHandleTable(IN PHANDLE_TABLE HandleTable,IN PEX_SWEEP_HANDLE_CALLBACK EnumHandleProcedure,IN PVOID Context)1232 ExSweepHandleTable(IN PHANDLE_TABLE HandleTable,
1233                    IN PEX_SWEEP_HANDLE_CALLBACK EnumHandleProcedure,
1234                    IN PVOID Context)
1235 {
1236     EXHANDLE Handle;
1237     PHANDLE_TABLE_ENTRY HandleTableEntry;
1238     PAGED_CODE();
1239 
1240     /* Set the initial value and loop the entries */
1241     Handle.Value = INDEX_TO_HANDLE_VALUE(1);
1242     while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle)))
1243     {
1244         /* Loop each handle */
1245         do
1246         {
1247             /* Lock the entry */
1248             if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1249             {
1250                 /* Notify the callback routine */
1251                 EnumHandleProcedure(HandleTableEntry,
1252                                     Handle.GenericHandleOverlay,
1253                                     Context);
1254             }
1255 
1256             /* Go to the next handle and entry */
1257             Handle.Value += INDEX_TO_HANDLE_VALUE(1);
1258             HandleTableEntry++;
1259         } while (Handle.Value % INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES));
1260 
1261         /* Skip past the last entry */
1262         Handle.Value += INDEX_TO_HANDLE_VALUE(1);
1263     }
1264 }
1265 
1266 /*
1267  * @implemented
1268  */
1269 BOOLEAN
1270 NTAPI
ExEnumHandleTable(IN PHANDLE_TABLE HandleTable,IN PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,IN OUT PVOID Context,OUT PHANDLE EnumHandle OPTIONAL)1271 ExEnumHandleTable(IN PHANDLE_TABLE HandleTable,
1272                   IN PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure,
1273                   IN OUT PVOID Context,
1274                   OUT PHANDLE EnumHandle OPTIONAL)
1275 {
1276     EXHANDLE Handle;
1277     PHANDLE_TABLE_ENTRY HandleTableEntry;
1278     BOOLEAN Result = FALSE;
1279     PAGED_CODE();
1280 
1281     /* Enter a critical region */
1282     KeEnterCriticalRegion();
1283 
1284     /* Set the initial value and loop the entries */
1285     Handle.Value = 0;
1286     while ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, Handle)))
1287     {
1288         /* Validate the entry */
1289         if ((HandleTableEntry->Object) &&
1290             (HandleTableEntry->NextFreeTableEntry != -2))
1291         {
1292             /* Lock the entry */
1293             if (ExpLockHandleTableEntry(HandleTable, HandleTableEntry))
1294             {
1295                 /* Notify the callback routine */
1296                 Result = EnumHandleProcedure(HandleTableEntry,
1297                                              Handle.GenericHandleOverlay,
1298                                              Context);
1299 
1300                 /* Unlock it */
1301                 ExUnlockHandleTableEntry(HandleTable, HandleTableEntry);
1302 
1303                 /* Was this the one looked for? */
1304                 if (Result)
1305                 {
1306                     /* If so, return it if requested */
1307                     if (EnumHandle) *EnumHandle = Handle.GenericHandleOverlay;
1308                     break;
1309                 }
1310             }
1311         }
1312 
1313         /* Go to the next entry */
1314         Handle.Value += INDEX_TO_HANDLE_VALUE(1);
1315     }
1316 
1317     /* Leave the critical region and return callback result */
1318     KeLeaveCriticalRegion();
1319     return Result;
1320 }
1321 
1322 #if DBG && defined(KDBG)
1323 
1324 #include <kdbg/kdb.h>
1325 
ExpKdbgExtHandle(ULONG Argc,PCHAR Argv[])1326 BOOLEAN ExpKdbgExtHandle(ULONG Argc, PCHAR Argv[])
1327 {
1328     USHORT i;
1329     char *endptr;
1330     HANDLE ProcessId;
1331     EXHANDLE ExHandle;
1332     PLIST_ENTRY Entry;
1333     PEPROCESS Process;
1334     WCHAR KeyPath[256];
1335     PFILE_OBJECT FileObject;
1336     PHANDLE_TABLE HandleTable;
1337     POBJECT_HEADER ObjectHeader;
1338     PHANDLE_TABLE_ENTRY TableEntry;
1339     ULONG NeededLength = 0;
1340     ULONG NameLength;
1341     PCM_KEY_CONTROL_BLOCK Kcb, CurrentKcb;
1342     POBJECT_HEADER_NAME_INFO ObjectNameInfo;
1343 
1344     if (Argc > 1)
1345     {
1346         /* Get EPROCESS address or PID */
1347         i = 0;
1348         while (Argv[1][i])
1349         {
1350             if (!isdigit(Argv[1][i]))
1351             {
1352                 i = 0;
1353                 break;
1354             }
1355 
1356             ++i;
1357         }
1358 
1359         if (i == 0)
1360         {
1361             if (!KdbpGetHexNumber(Argv[1], (PVOID)&Process))
1362             {
1363                 KdbpPrint("Invalid parameter: %s\n", Argv[1]);
1364                 return TRUE;
1365             }
1366 
1367             /* In the end, we always want a PID */
1368             ProcessId = PsGetProcessId(Process);
1369         }
1370         else
1371         {
1372             ProcessId = (HANDLE)strtoulptr(Argv[1], &endptr, 10);
1373             if (*endptr != '\0')
1374             {
1375                 KdbpPrint("Invalid parameter: %s\n", Argv[1]);
1376                 return TRUE;
1377             }
1378         }
1379     }
1380     else
1381     {
1382         ProcessId = PsGetCurrentProcessId();
1383     }
1384 
1385     for (Entry = HandleTableListHead.Flink;
1386          Entry != &HandleTableListHead;
1387          Entry = Entry->Flink)
1388     {
1389         /* Only return matching PID
1390          * 0 matches everything
1391          */
1392         HandleTable = CONTAINING_RECORD(Entry, HANDLE_TABLE, HandleTableList);
1393         if (ProcessId != 0 && HandleTable->UniqueProcessId != ProcessId)
1394         {
1395             continue;
1396         }
1397 
1398         KdbpPrint("\n");
1399 
1400         KdbpPrint("Handle table at %p with %d entries in use\n", HandleTable, HandleTable->HandleCount);
1401 
1402         ExHandle.Value = 0;
1403         while ((TableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle)))
1404         {
1405             if ((TableEntry->Object) &&
1406                 (TableEntry->NextFreeTableEntry != -2))
1407             {
1408                 ObjectHeader = ObpGetHandleObject(TableEntry);
1409 
1410                 KdbpPrint("%p: Object: %p GrantedAccess: %x Entry: %p\n", ExHandle.Value, &ObjectHeader->Body, TableEntry->GrantedAccess, TableEntry);
1411                 KdbpPrint("Object: %p Type: (%x) %wZ\n", &ObjectHeader->Body, ObjectHeader->Type, &ObjectHeader->Type->Name);
1412                 KdbpPrint("\tObjectHeader: %p\n", ObjectHeader);
1413                 KdbpPrint("\t\tHandleCount: %u PointerCount: %u\n", ObjectHeader->HandleCount, ObjectHeader->PointerCount);
1414 
1415                 /* Specific objects debug prints */
1416 
1417                 /* For file, display path */
1418                 if (ObjectHeader->Type == IoFileObjectType)
1419                 {
1420                     FileObject = (PFILE_OBJECT)&ObjectHeader->Body;
1421 
1422                     KdbpPrint("\t\t\tName: %wZ\n", &FileObject->FileName);
1423                 }
1424 
1425                 /* For directory, and win32k objects, display object name */
1426                 else if (ObjectHeader->Type == ObpDirectoryObjectType ||
1427                          ObjectHeader->Type == ExWindowStationObjectType ||
1428                          ObjectHeader->Type == ExDesktopObjectType ||
1429                          ObjectHeader->Type == MmSectionObjectType)
1430                 {
1431                     ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1432                     if (ObjectNameInfo != NULL && ObjectNameInfo->Name.Buffer != NULL)
1433                     {
1434                         KdbpPrint("\t\t\tName: %wZ\n", &ObjectNameInfo->Name);
1435                     }
1436                 }
1437 
1438                 /* For registry keys, display full path */
1439                 else if (ObjectHeader->Type == CmpKeyObjectType)
1440                 {
1441                     Kcb = ((PCM_KEY_BODY)&ObjectHeader->Body)->KeyControlBlock;
1442                     if (!Kcb->Delete)
1443                     {
1444                         CurrentKcb = Kcb;
1445 
1446                         /* See: CmpQueryNameInformation() */
1447 
1448                         while (CurrentKcb != NULL)
1449                         {
1450                             if (CurrentKcb->NameBlock->Compressed)
1451                                 NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1452                             else
1453                                 NeededLength += CurrentKcb->NameBlock->NameLength;
1454 
1455                             NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR);
1456 
1457                             CurrentKcb = CurrentKcb->ParentKcb;
1458                         }
1459 
1460                         if (NeededLength < sizeof(KeyPath))
1461                         {
1462                             CurrentKcb = Kcb;
1463 
1464                             while (CurrentKcb != NULL)
1465                             {
1466                                 if (CurrentKcb->NameBlock->Compressed)
1467                                 {
1468                                     NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1469                                     CmpCopyCompressedName(&KeyPath[(NeededLength - NameLength)/sizeof(WCHAR)],
1470                                                           NameLength,
1471                                                           CurrentKcb->NameBlock->Name,
1472                                                           CurrentKcb->NameBlock->NameLength);
1473                                 }
1474                                 else
1475                                 {
1476                                     NameLength = CurrentKcb->NameBlock->NameLength;
1477                                     RtlCopyMemory(&KeyPath[(NeededLength - NameLength)/sizeof(WCHAR)],
1478                                                   CurrentKcb->NameBlock->Name,
1479                                                   NameLength);
1480                                 }
1481 
1482                                 NeededLength -= NameLength;
1483                                 NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
1484                                 KeyPath[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
1485 
1486                                 CurrentKcb = CurrentKcb->ParentKcb;
1487                             }
1488                         }
1489 
1490                         KdbpPrint("\t\t\tName: %S\n", KeyPath);
1491                     }
1492                 }
1493             }
1494 
1495             ExHandle.Value += INDEX_TO_HANDLE_VALUE(1);
1496         }
1497     }
1498 
1499     return TRUE;
1500 }
1501 
1502 #endif // DBG && defined(KDBG)
1503