Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4093 lines
108 KiB

  1. /*++
  2. Copyright (c) 1989-1995 Microsoft Corporation
  3. Module Name:
  4. handle.c
  5. Abstract:
  6. This module implements a set of functions for supporting handles.
  7. Author:
  8. Steve Wood (stevewo) 25-Apr-1989
  9. David N. Cutler (davec) 17-May-1995 (rewrite)
  10. Gary Kimura (GaryKi) 9-Dec-1997 (rerewrite)
  11. Adrian Marinescu (adrmarin) 24-May-2000
  12. Support dynamic changes to the number of levels we use. The code
  13. performs the best for typical handle table sizes and scales better.
  14. Neill Clift (NeillC) 24-Jul-2000
  15. Make the handle allocate, free and duplicate paths mostly lock free except
  16. for the lock entry locks, table expansion and locks to solve the A-B-A problem.
  17. Revision History:
  18. --*/
  19. #include "exp.h"
  20. #pragma hdrstop
  21. //
  22. // Local constants and support routines
  23. //
  24. //
  25. // Define global structures that link all handle tables together except the
  26. // ones where the user has called RemoveHandleTable
  27. //
  28. #if !DBG // Make this a const varible so its optimized away on free
  29. const
  30. #endif
  31. BOOLEAN ExTraceAllTables = FALSE;
  32. EX_PUSH_LOCK HandleTableListLock;
  33. ULONG TotalTraceBuffers = 0;
  34. #ifdef ALLOC_PRAGMA
  35. #pragma data_seg("PAGED")
  36. #endif
  37. LIST_ENTRY HandleTableListHead;
  38. #ifdef ALLOC_PRAGMA
  39. #pragma data_seg()
  40. #endif
  41. #if DBG
  42. #define EXHANDLE_EXTRA_CHECKS 0
  43. #else
  44. #define EXHANDLE_EXTRA_CHECKS 0
  45. #endif
  46. #if EXHANDLE_EXTRA_CHECKS
  47. #define EXASSERT( exp ) \
  48. ((!(exp)) ? \
  49. (RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
  50. TRUE)
  51. #else
  52. #define EXASSERT ASSERT
  53. #endif
  54. //
  55. // This is the sign low bit used to lock handle table entries
  56. //
  57. #define EXHANDLE_TABLE_ENTRY_LOCK_BIT 1
  58. #define EX_ADDITIONAL_INFO_SIGNATURE (-2)
  59. #define ExpIsValidObjectEntry(Entry) \
  60. ( (Entry != NULL) && (Entry->Object != NULL) && (Entry->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE) )
  61. #define TABLE_PAGE_SIZE PAGE_SIZE
  62. //
  63. // Absolute maximum number of handles allowed
  64. //
  65. #define MAX_HANDLES (1<<24)
  66. #if EXHANDLE_EXTRA_CHECKS
  67. //
  68. // Mask for next free value from the free lists.
  69. //
  70. #define FREE_HANDLE_MASK ((MAX_HANDLES<<2) - 1)
  71. #else
  72. //
  73. // When no checks compiled in this gets optimized away
  74. //
  75. #define FREE_HANDLE_MASK 0xFFFFFFFF
  76. #endif
  77. //
  78. // Mask for the free list sequence number
  79. //
  80. #define FREE_SEQ_MASK (0xFFFFFFFF & ~FREE_HANDLE_MASK)
  81. #if (FREE_HANDLE_MASK == 0xFFFFFFFF)
  82. #define FREE_SEQ_INC 0
  83. #define GetNextSeq() 0
  84. #else
  85. //
  86. // Increment value to progress the sequence number
  87. //
  88. #define FREE_SEQ_INC (FREE_HANDLE_MASK + 1)
  89. ULONG CurrentSeq = 0;
  90. #define GetNextSeq() (CurrentSeq += FREE_SEQ_INC)
  91. #endif
  92. #define LOWLEVEL_COUNT (TABLE_PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
  93. #define MIDLEVEL_COUNT (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))
  94. #define HIGHLEVEL_COUNT MAX_HANDLES / (LOWLEVEL_COUNT * MIDLEVEL_COUNT)
  95. #define LOWLEVEL_THRESHOLD LOWLEVEL_COUNT
  96. #define MIDLEVEL_THRESHOLD (MIDLEVEL_COUNT * LOWLEVEL_COUNT)
  97. #define HIGHLEVEL_THRESHOLD (MIDLEVEL_COUNT * MIDLEVEL_COUNT * LOWLEVEL_COUNT)
  98. #define HIGHLEVEL_SIZE (HIGHLEVEL_COUNT * sizeof (PHANDLE_TABLE_ENTRY))
  99. #define LEVEL_CODE_MASK 3
  100. //
  101. // Local support routines
  102. //
  103. PHANDLE_TABLE
  104. ExpAllocateHandleTable (
  105. IN PEPROCESS Process OPTIONAL,
  106. IN BOOLEAN DoInit
  107. );
  108. VOID
  109. ExpFreeHandleTable (
  110. IN PHANDLE_TABLE HandleTable
  111. );
  112. BOOLEAN
  113. ExpAllocateHandleTableEntrySlow (
  114. IN PHANDLE_TABLE HandleTable,
  115. IN BOOLEAN DoInit
  116. );
  117. PHANDLE_TABLE_ENTRY
  118. ExpAllocateHandleTableEntry (
  119. IN PHANDLE_TABLE HandleTable,
  120. OUT PEXHANDLE Handle
  121. );
  122. VOID
  123. ExpFreeHandleTableEntry (
  124. IN PHANDLE_TABLE HandleTable,
  125. IN EXHANDLE Handle,
  126. IN PHANDLE_TABLE_ENTRY HandleTableEntry
  127. );
  128. PHANDLE_TABLE_ENTRY
  129. ExpLookupHandleTableEntry (
  130. IN PHANDLE_TABLE HandleTable,
  131. IN EXHANDLE Handle
  132. );
  133. PHANDLE_TABLE_ENTRY *
  134. ExpAllocateMidLevelTable (
  135. IN PHANDLE_TABLE HandleTable,
  136. IN BOOLEAN DoInit,
  137. OUT PHANDLE_TABLE_ENTRY *pNewLowLevel
  138. );
  139. PVOID
  140. ExpAllocateTablePagedPool (
  141. IN PEPROCESS QuotaProcess OPTIONAL,
  142. IN SIZE_T NumberOfBytes
  143. );
  144. VOID
  145. ExpFreeTablePagedPool (
  146. IN PEPROCESS QuotaProcess OPTIONAL,
  147. IN PVOID PoolMemory,
  148. IN SIZE_T NumberOfBytes
  149. );
  150. PHANDLE_TABLE_ENTRY
  151. ExpAllocateLowLevelTable (
  152. IN PHANDLE_TABLE HandleTable,
  153. IN BOOLEAN DoInit
  154. );
  155. VOID
  156. ExpFreeLowLevelTable (
  157. IN PEPROCESS QuotaProcess,
  158. IN PHANDLE_TABLE_ENTRY TableLevel1
  159. );
  160. VOID
  161. ExpBlockOnLockedHandleEntry (
  162. PHANDLE_TABLE HandleTable,
  163. PHANDLE_TABLE_ENTRY HandleTableEntry
  164. );
  165. ULONG
  166. ExpMoveFreeHandles (
  167. IN PHANDLE_TABLE HandleTable
  168. );
  169. VOID
  170. ExpUpdateDebugInfo(
  171. PHANDLE_TABLE HandleTable,
  172. PETHREAD CurrentThread,
  173. HANDLE Handle,
  174. ULONG Type
  175. );
  176. PVOID
  177. ExpAllocateTablePagedPoolNoZero (
  178. IN PEPROCESS QuotaProcess OPTIONAL,
  179. IN SIZE_T NumberOfBytes
  180. );
  181. #ifdef ALLOC_PRAGMA
  182. #pragma alloc_text(INIT, ExInitializeHandleTablePackage)
  183. #pragma alloc_text(INIT, ExSetHandleTableStrictFIFO)
  184. #pragma alloc_text(PAGE, ExUnlockHandleTableEntry)
  185. #pragma alloc_text(PAGE, ExCreateHandleTable)
  186. #pragma alloc_text(PAGE, ExRemoveHandleTable)
  187. #pragma alloc_text(PAGE, ExDestroyHandleTable)
  188. #pragma alloc_text(PAGE, ExEnumHandleTable)
  189. #pragma alloc_text(PAGE, ExDupHandleTable)
  190. #pragma alloc_text(PAGE, ExSnapShotHandleTables)
  191. #pragma alloc_text(PAGE, ExCreateHandle)
  192. #pragma alloc_text(PAGE, ExDestroyHandle)
  193. #pragma alloc_text(PAGE, ExChangeHandle)
  194. #pragma alloc_text(PAGE, ExMapHandleToPointer)
  195. #pragma alloc_text(PAGE, ExMapHandleToPointerEx)
  196. #pragma alloc_text(PAGE, ExpAllocateHandleTable)
  197. #pragma alloc_text(PAGE, ExpFreeHandleTable)
  198. #pragma alloc_text(PAGE, ExpAllocateHandleTableEntry)
  199. #pragma alloc_text(PAGE, ExpAllocateHandleTableEntrySlow)
  200. #pragma alloc_text(PAGE, ExpFreeHandleTableEntry)
  201. #pragma alloc_text(PAGE, ExpLookupHandleTableEntry)
  202. #pragma alloc_text(PAGE, ExSweepHandleTable)
  203. #pragma alloc_text(PAGE, ExpAllocateMidLevelTable)
  204. #pragma alloc_text(PAGE, ExpAllocateTablePagedPool)
  205. #pragma alloc_text(PAGE, ExpAllocateTablePagedPoolNoZero)
  206. #pragma alloc_text(PAGE, ExpFreeTablePagedPool)
  207. #pragma alloc_text(PAGE, ExpAllocateLowLevelTable)
  208. #pragma alloc_text(PAGE, ExSetHandleInfo)
  209. #pragma alloc_text(PAGE, ExpGetHandleInfo)
  210. #pragma alloc_text(PAGE, ExSnapShotHandleTablesEx)
  211. #pragma alloc_text(PAGE, ExpFreeLowLevelTable)
  212. #pragma alloc_text(PAGE, ExpBlockOnLockedHandleEntry)
  213. #pragma alloc_text(PAGE, ExpMoveFreeHandles)
  214. #pragma alloc_text(PAGE, ExEnableHandleTracing)
  215. #pragma alloc_text(PAGE, ExDereferenceHandleDebugInfo)
  216. #pragma alloc_text(PAGE, ExReferenceHandleDebugInfo)
  217. #pragma alloc_text(PAGE, ExpUpdateDebugInfo)
  218. #endif
  219. //
  220. // Define macros to lock and unlock the handle table.
  221. // We use this lock only for handle table expansion.
  222. //
  223. #define ExpLockHandleTableExclusive(xxHandleTable,xxCurrentThread) { \
  224. KeEnterCriticalRegionThread (xxCurrentThread); \
  225. ExAcquirePushLockExclusive (&xxHandleTable->HandleTableLock[0]); \
  226. }
  227. #define ExpUnlockHandleTableExclusive(xxHandleTable,xxCurrentThread) { \
  228. ExReleasePushLockExclusive (&xxHandleTable->HandleTableLock[0]); \
  229. KeLeaveCriticalRegionThread (xxCurrentThread); \
  230. }
  231. #define ExpLockHandleTableShared(xxHandleTable,xxCurrentThread,xxIdx) { \
  232. KeEnterCriticalRegionThread (xxCurrentThread); \
  233. ExAcquirePushLockShared (&xxHandleTable->HandleTableLock[xxIdx]); \
  234. }
  235. #define ExpUnlockHandleTableShared(xxHandleTable,xxCurrentThread,xxIdx) { \
  236. ExReleasePushLockShared (&xxHandleTable->HandleTableLock[xxIdx]); \
  237. KeLeaveCriticalRegionThread (xxCurrentThread); \
  238. }
  239. FORCEINLINE
  240. ULONG
  241. ExpInterlockedExchange (
  242. IN OUT PULONG Index,
  243. IN ULONG FirstIndex,
  244. IN PHANDLE_TABLE_ENTRY Entry
  245. )
  246. /*++
  247. Routine Description:
  248. This performs the following steps:
  249. 1. Set Entry->NextFreeTableEntry = *Index
  250. 2. Loops until *Index == (the value of *Index when we entered the function)
  251. When they're equal, we set *Index = FirstIndex
  252. Arguments:
  253. Index - Points to the ULONG we want to set.
  254. FirstIndex - New value to set Index to.
  255. Entry - TableEntry that will get the initial value of *Index before it's
  256. updated.
  257. Return Value:
  258. New value of *Index (i.e. FirstIndex).
  259. --*/
  260. {
  261. ULONG OldIndex, NewIndex;
  262. EXASSERT (Entry->Object == NULL);
  263. //
  264. // Load new value and generate the sequence number on pushes
  265. //
  266. NewIndex = FirstIndex + GetNextSeq();
  267. while (1) {
  268. //
  269. // remember original value and
  270. // archive it in NextFreeTableEntry.
  271. //
  272. OldIndex = *Index;
  273. Entry->NextFreeTableEntry = OldIndex;
  274. //
  275. // Swap in the new value, and if the swap occurs
  276. // successfully, we're done.
  277. //
  278. if (OldIndex == (ULONG) InterlockedCompareExchange ((PLONG)Index,
  279. NewIndex,
  280. OldIndex)) {
  281. return OldIndex;
  282. }
  283. }
  284. }
  285. ULONG
  286. ExpMoveFreeHandles (
  287. IN PHANDLE_TABLE HandleTable
  288. )
  289. {
  290. ULONG OldValue, NewValue;
  291. ULONG Index, OldIndex, NewIndex, FreeSize;
  292. PHANDLE_TABLE_ENTRY Entry, FirstEntry;
  293. EXHANDLE Handle;
  294. ULONG Idx;
  295. BOOLEAN StrictFIFO;
  296. //
  297. // First remove all the handles from the free list so we can add them to the
  298. // list we use for allocates.
  299. //
  300. OldValue = InterlockedExchange ((PLONG)&HandleTable->LastFree,
  301. 0);
  302. Index = OldValue;
  303. if (Index == 0) {
  304. //
  305. // There are no free handles. Nothing to do.
  306. //
  307. return OldValue;
  308. }
  309. //
  310. // We are pushing old entries onto the free list.
  311. // We have the A-B-A problem here as these items may have been moved here because
  312. // another thread was using them in the pop code.
  313. //
  314. for (Idx = 1; Idx < HANDLE_TABLE_LOCKS; Idx++) {
  315. ExAcquireReleasePushLockExclusive (&HandleTable->HandleTableLock[Idx]);
  316. }
  317. StrictFIFO = HandleTable->StrictFIFO;
  318. //
  319. // If we are strict FIFO then reverse the list to make handle reuse rare.
  320. //
  321. if (!StrictFIFO) {
  322. //
  323. // We have a complete chain here. If there is no existing chain we
  324. // can just push this one without any hassles. If we can't then
  325. // we can just fall into the reversing code anyway as we need
  326. // to find the end of the chain to continue it.
  327. //
  328. //
  329. // This is a push so create a new sequence number
  330. //
  331. if (InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
  332. OldValue + GetNextSeq(),
  333. 0) == 0) {
  334. return OldValue;
  335. }
  336. }
  337. //
  338. // Loop over all the entries and reverse the chain.
  339. //
  340. FreeSize = OldIndex = 0;
  341. FirstEntry = NULL;
  342. while (1) {
  343. FreeSize++;
  344. Handle.Value = Index;
  345. Entry = ExpLookupHandleTableEntry (HandleTable, Handle);
  346. EXASSERT (Entry->Object == NULL);
  347. NewIndex = Entry->NextFreeTableEntry;
  348. Entry->NextFreeTableEntry = OldIndex;
  349. if (OldIndex == 0) {
  350. FirstEntry = Entry;
  351. }
  352. OldIndex = Index;
  353. if (NewIndex == 0) {
  354. break;
  355. }
  356. Index = NewIndex;
  357. }
  358. NewValue = ExpInterlockedExchange (&HandleTable->FirstFree,
  359. OldIndex,
  360. FirstEntry);
  361. //
  362. // If we haven't got a pool of a few handles then force
  363. // table expansion to keep the free handle size high
  364. //
  365. if (FreeSize < 100 && StrictFIFO) {
  366. OldValue = 0;
  367. }
  368. return OldValue;
  369. }
  370. PHANDLE_TABLE_ENTRY
  371. ExpAllocateHandleTableEntry (
  372. IN PHANDLE_TABLE HandleTable,
  373. OUT PEXHANDLE pHandle
  374. )
  375. /*++
  376. Routine Description:
  377. This routine does a fast allocate of a free handle. It's lock free if
  378. possible.
  379. Only the rare case of handle table expansion is covered by the handle
  380. table lock.
  381. Arguments:
  382. HandleTable - Supplies the handle table being allocated from.
  383. pHandle - Handle returned
  384. Return Value:
  385. PHANDLE_TABLE_ENTRY - The allocated handle table entry pointer or NULL
  386. on failure.
  387. --*/
  388. {
  389. PKTHREAD CurrentThread;
  390. ULONG OldValue, NewValue, NewValue1;
  391. PHANDLE_TABLE_ENTRY Entry;
  392. EXHANDLE Handle;
  393. BOOLEAN RetVal;
  394. ULONG Idx;
  395. CurrentThread = KeGetCurrentThread ();
  396. while (1) {
  397. OldValue = HandleTable->FirstFree;
  398. while (OldValue == 0) {
  399. //
  400. // Lock the handle table for exclusive access as we will be
  401. // allocating a new table level.
  402. //
  403. ExpLockHandleTableExclusive (HandleTable, CurrentThread);
  404. //
  405. // If we have multiple threads trying to expand the table at
  406. // the same time then by just acquiring the table lock we
  407. // force those threads to complete their allocations and
  408. // populate the free list. We must check the free list here
  409. // so we don't expand the list twice without needing to.
  410. //
  411. OldValue = HandleTable->FirstFree;
  412. if (OldValue != 0) {
  413. ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
  414. break;
  415. }
  416. //
  417. // See if we have any handles on the alternate free list
  418. // These handles need some locking to move them over.
  419. //
  420. OldValue = ExpMoveFreeHandles (HandleTable);
  421. if (OldValue != 0) {
  422. ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
  423. break;
  424. }
  425. //
  426. // This must be the first thread attempting expansion or all the
  427. // free handles allocated by another thread got used up in the gap.
  428. //
  429. RetVal = ExpAllocateHandleTableEntrySlow (HandleTable, TRUE);
  430. ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
  431. OldValue = HandleTable->FirstFree;
  432. //
  433. // If ExpAllocateHandleTableEntrySlow had a failed allocation
  434. // then we want to fail the call. We check for free entries
  435. // before we exit just in case they got allocated or freed by
  436. // somebody else in the gap.
  437. //
  438. if (!RetVal) {
  439. if (OldValue == 0) {
  440. pHandle->GenericHandleOverlay = NULL;
  441. return NULL;
  442. }
  443. }
  444. }
  445. Handle.Value = (OldValue & FREE_HANDLE_MASK);
  446. Entry = ExpLookupHandleTableEntry (HandleTable, Handle);
  447. Idx = ((OldValue & FREE_HANDLE_MASK)>>2) % HANDLE_TABLE_LOCKS;
  448. ExpLockHandleTableShared (HandleTable, CurrentThread, Idx);
  449. if (OldValue != *(volatile ULONG *) &HandleTable->FirstFree) {
  450. ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx);
  451. continue;
  452. }
  453. KeMemoryBarrier ();
  454. NewValue = *(volatile ULONG *) &Entry->NextFreeTableEntry;
  455. NewValue1 = InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
  456. NewValue,
  457. OldValue);
  458. ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx);
  459. if (NewValue1 == OldValue) {
  460. EXASSERT ((NewValue & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool);
  461. break;
  462. } else {
  463. //
  464. // We should have eliminated the A-B-A problem so if only the sequence number has
  465. // changed we are broken.
  466. //
  467. EXASSERT ((NewValue1 & FREE_HANDLE_MASK) != (OldValue & FREE_HANDLE_MASK));
  468. }
  469. }
  470. InterlockedIncrement (&HandleTable->HandleCount);
  471. *pHandle = Handle;
  472. return Entry;
  473. }
  474. VOID
  475. ExpBlockOnLockedHandleEntry (
  476. PHANDLE_TABLE HandleTable,
  477. PHANDLE_TABLE_ENTRY HandleTableEntry
  478. )
  479. {
  480. EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
  481. LONG_PTR CurrentValue;
  482. //
  483. // Queue our wait block to be signaled by a releasing thread.
  484. //
  485. ExBlockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
  486. CurrentValue = HandleTableEntry->Value;
  487. if (CurrentValue == 0 || (CurrentValue&EXHANDLE_TABLE_ENTRY_LOCK_BIT) != 0) {
  488. ExUnblockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
  489. } else {
  490. ExWaitForUnblockPushLock (&HandleTable->HandleContentionEvent, &WaitBlock);
  491. }
  492. }
  493. BOOLEAN
  494. FORCEINLINE
  495. ExpLockHandleTableEntry (
  496. PHANDLE_TABLE HandleTable,
  497. PHANDLE_TABLE_ENTRY HandleTableEntry
  498. )
  499. /*++
  500. Routine Description:
  501. This routine locks the specified handle table entry. After the entry is
  502. locked the sign bit will be set.
  503. Arguments:
  504. HandleTable - Supplies the handle table containing the entry being locked.
  505. HandleTableEntry - Supplies the handle table entry being locked.
  506. Return Value:
  507. TRUE if the entry is valid and locked, and FALSE if the entry is
  508. marked free.
  509. --*/
  510. {
  511. LONG_PTR NewValue;
  512. LONG_PTR CurrentValue;
  513. //
  514. // We are about to take a lock. Make sure we are protected.
  515. //
  516. ASSERT ((KeGetCurrentThread()->CombinedApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL));
  517. //
  518. // We'll keep on looping reading in the value, making sure it is not null,
  519. // and if it is not currently locked we'll try for the lock and return
  520. // true if we get it. Otherwise we'll pause a bit and then try again.
  521. //
  522. while (TRUE) {
  523. CurrentValue = *((volatile LONG_PTR *)&HandleTableEntry->Object);
  524. //
  525. // If the handle value is greater than zero then it is not currently
  526. // locked and we should try for the lock, by setting the lock bit and
  527. // doing an interlocked exchange.
  528. //
  529. if (CurrentValue & EXHANDLE_TABLE_ENTRY_LOCK_BIT) {
  530. //
  531. // Remove the
  532. //
  533. NewValue = CurrentValue - EXHANDLE_TABLE_ENTRY_LOCK_BIT;
  534. if ((LONG_PTR)(InterlockedCompareExchangePointer (&HandleTableEntry->Object,
  535. (PVOID)NewValue,
  536. (PVOID)CurrentValue)) == CurrentValue) {
  537. return TRUE;
  538. }
  539. } else {
  540. //
  541. // Make sure the handle table entry is not freed
  542. //
  543. if (CurrentValue == 0) {
  544. return FALSE;
  545. }
  546. }
  547. ExpBlockOnLockedHandleEntry (HandleTable, HandleTableEntry);
  548. }
  549. }
  550. NTKERNELAPI
  551. VOID
  552. FORCEINLINE
  553. ExUnlockHandleTableEntry (
  554. PHANDLE_TABLE HandleTable,
  555. PHANDLE_TABLE_ENTRY HandleTableEntry
  556. )
  557. /*++
  558. Routine Description:
  559. This routine unlocks the specified handle table entry. After the entry is
  560. unlocked the sign bit will be clear.
  561. Arguments:
  562. HandleTable - Supplies the handle table containing the entry being unlocked.
  563. HandleTableEntry - Supplies the handle table entry being unlocked.
  564. Return Value:
  565. None.
  566. --*/
  567. {
  568. LONG_PTR OldValue;
  569. PAGED_CODE();
  570. //
  571. // We are about to release a lock. Make sure we are protected from suspension.
  572. //
  573. ASSERT ((KeGetCurrentThread()->CombinedApcDisable != 0) || (KeGetCurrentIrql() == APC_LEVEL));
  574. //
  575. // This routine does not need to loop and attempt the unlock opeation more
  576. // than once because by definition the caller has the entry already locked
  577. // and no one can be changing the value without the lock.
  578. //
  579. #if defined (_WIN64)
  580. OldValue = InterlockedExchangeAdd64 ((PLONGLONG) &HandleTableEntry->Value, EXHANDLE_TABLE_ENTRY_LOCK_BIT);
  581. #else
  582. OldValue = InterlockedOr ((LONG *) &HandleTableEntry->Value, EXHANDLE_TABLE_ENTRY_LOCK_BIT);
  583. #endif
  584. EXASSERT ((OldValue&EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
  585. //
  586. // Unblock any waiters waiting for this table entry.
  587. //
  588. ExUnblockPushLock (&HandleTable->HandleContentionEvent, NULL);
  589. return;
  590. }
  591. NTKERNELAPI
  592. VOID
  593. ExInitializeHandleTablePackage (
  594. VOID
  595. )
  596. /*++
  597. Routine Description:
  598. This routine is called once at system initialization to setup the ex handle
  599. table package
  600. Arguments:
  601. None.
  602. Return Value:
  603. None.
  604. --*/
  605. {
  606. //
  607. // Initialize the handle table synchronization resource and list head
  608. //
  609. InitializeListHead( &HandleTableListHead );
  610. ExInitializePushLock( &HandleTableListLock );
  611. return;
  612. }
  613. NTKERNELAPI
  614. PHANDLE_TABLE
  615. ExCreateHandleTable (
  616. IN struct _EPROCESS *Process OPTIONAL
  617. )
  618. /*++
  619. Routine Description:
  620. This function allocate and initialize a new new handle table
  621. Arguments:
  622. Process - Supplies an optional pointer to the process against which quota
  623. will be charged.
  624. Return Value:
  625. If a handle table is successfully created, then the address of the
  626. handle table is returned as the function value. Otherwize, a value
  627. NULL is returned.
  628. --*/
  629. {
  630. PKTHREAD CurrentThread;
  631. PHANDLE_TABLE HandleTable;
  632. PAGED_CODE();
  633. CurrentThread = KeGetCurrentThread ();
  634. //
  635. // Allocate and initialize a handle table descriptor
  636. //
  637. HandleTable = ExpAllocateHandleTable( Process, TRUE );
  638. if (HandleTable == NULL) {
  639. return NULL;
  640. }
  641. //
  642. // Insert the handle table in the handle table list.
  643. //
  644. KeEnterCriticalRegionThread (CurrentThread);
  645. ExAcquirePushLockExclusive( &HandleTableListLock );
  646. InsertTailList( &HandleTableListHead, &HandleTable->HandleTableList );
  647. ExReleasePushLockExclusive( &HandleTableListLock );
  648. KeLeaveCriticalRegionThread (CurrentThread);
  649. //
  650. // And return to our caller
  651. //
  652. return HandleTable;
  653. }
  654. NTKERNELAPI
  655. VOID
  656. ExRemoveHandleTable (
  657. IN PHANDLE_TABLE HandleTable
  658. )
  659. /*++
  660. Routine Description:
  661. This function removes the specified exhandle table from the list of
  662. exhandle tables. Used by PS and ATOM packages to make sure their handle
  663. tables are not in the list enumerated by the ExSnapShotHandleTables
  664. routine and the !handle debugger extension.
  665. Arguments:
  666. HandleTable - Supplies a pointer to a handle table
  667. Return Value:
  668. None.
  669. --*/
  670. {
  671. PKTHREAD CurrentThread;
  672. PAGED_CODE();
  673. CurrentThread = KeGetCurrentThread ();
  674. //
  675. // First, acquire the global handle table lock
  676. //
  677. KeEnterCriticalRegionThread (CurrentThread);
  678. ExAcquirePushLockExclusive( &HandleTableListLock );
  679. //
  680. // Remove the handle table from the handle table list. This routine is
  681. // written so that multiple calls to remove a handle table will not
  682. // corrupt the system.
  683. //
  684. RemoveEntryList( &HandleTable->HandleTableList );
  685. InitializeListHead( &HandleTable->HandleTableList );
  686. //
  687. // Now release the global lock and return to our caller
  688. //
  689. ExReleasePushLockExclusive( &HandleTableListLock );
  690. KeLeaveCriticalRegionThread (CurrentThread);
  691. return;
  692. }
  693. NTKERNELAPI
  694. VOID
  695. ExDestroyHandleTable (
  696. IN PHANDLE_TABLE HandleTable,
  697. IN EX_DESTROY_HANDLE_ROUTINE DestroyHandleProcedure OPTIONAL
  698. )
  699. /*++
  700. Routine Description:
  701. This function destroys the specified handle table.
  702. Arguments:
  703. HandleTable - Supplies a pointer to a handle table
  704. DestroyHandleProcedure - Supplies a pointer to a function to call for each
  705. valid handle entry in the handle table.
  706. Return Value:
  707. None.
  708. --*/
  709. {
  710. EXHANDLE Handle;
  711. PHANDLE_TABLE_ENTRY HandleTableEntry;
  712. PAGED_CODE();
  713. //
  714. // Remove the handle table from the handle table list
  715. //
  716. ExRemoveHandleTable( HandleTable );
  717. //
  718. // Iterate through the handle table and for each handle that is allocated
  719. // we'll invoke the call back. Note that this loop exits when we get a
  720. // null handle table entry. We know there will be no more possible
  721. // entries after the first null one is encountered because we allocate
  722. // memory of the handles in a dense fashion. But first test that we have
  723. // call back to use
  724. //
  725. if (ARGUMENT_PRESENT(DestroyHandleProcedure)) {
  726. for (Handle.Value = 0;
  727. (HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
  728. Handle.Value += HANDLE_VALUE_INC) {
  729. //
  730. // Only do the callback if the entry is not free
  731. //
  732. if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
  733. (*DestroyHandleProcedure)( Handle.GenericHandleOverlay );
  734. }
  735. }
  736. }
  737. //
  738. // Now free up the handle table memory and return to our caller
  739. //
  740. ExpFreeHandleTable( HandleTable );
  741. return;
  742. }
  743. NTKERNELAPI
  744. VOID
  745. ExSweepHandleTable (
  746. IN PHANDLE_TABLE HandleTable,
  747. IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
  748. IN PVOID EnumParameter
  749. )
  750. /*++
  751. Routine Description:
  752. This function sweeps a handle table in a unsynchronized manner.
  753. Arguments:
  754. HandleTable - Supplies a pointer to a handle table
  755. EnumHandleProcedure - Supplies a pointer to a fucntion to call for
  756. each valid handle in the enumerated handle table.
  757. EnumParameter - Supplies an uninterpreted 32-bit value that is passed
  758. to the EnumHandleProcedure each time it is called.
  759. Return Value:
  760. None.
  761. --*/
  762. {
  763. EXHANDLE Handle;
  764. PHANDLE_TABLE_ENTRY HandleTableEntry;
  765. PAGED_CODE();
  766. //
  767. // Iterate through the handle table and for each handle that is allocated
  768. // we'll invoke the call back. Note that this loop exits when we get a
  769. // null handle table entry. We know there will be no more possible
  770. // entries after the first null one is encountered because we allocate
  771. // memory of the handles in a dense fashion.
  772. //
  773. Handle.Value = HANDLE_VALUE_INC;
  774. while ((HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL) {
  775. do {
  776. //
  777. // Only do the callback if the entry is not free
  778. //
  779. //
  780. if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  781. (*EnumHandleProcedure)( HandleTableEntry,
  782. Handle.GenericHandleOverlay,
  783. EnumParameter );
  784. }
  785. Handle.Value += HANDLE_VALUE_INC;
  786. HandleTableEntry++;
  787. } while ((Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC)) != 0);
  788. // Skip past the first entry that's not a real entry
  789. Handle.Value += HANDLE_VALUE_INC;
  790. }
  791. return;
  792. }
  793. NTKERNELAPI
  794. BOOLEAN
  795. ExEnumHandleTable (
  796. IN PHANDLE_TABLE HandleTable,
  797. IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
  798. IN PVOID EnumParameter,
  799. OUT PHANDLE Handle OPTIONAL
  800. )
  801. /*++
  802. Routine Description:
  803. This function enumerates all the valid handles in a handle table.
  804. For each valid handle in the handle table, the specified eumeration
  805. function is called. If the enumeration function returns TRUE, then
  806. the enumeration is stopped, the current handle is returned to the
  807. caller via the optional Handle parameter, and this function returns
  808. TRUE to indicated that the enumeration stopped at a specific handle.
  809. Arguments:
  810. HandleTable - Supplies a pointer to a handle table.
  811. EnumHandleProcedure - Supplies a pointer to a fucntion to call for
  812. each valid handle in the enumerated handle table.
  813. EnumParameter - Supplies an uninterpreted 32-bit value that is passed
  814. to the EnumHandleProcedure each time it is called.
  815. Handle - Supplies an optional pointer a variable that receives the
  816. Handle value that the enumeration stopped at. Contents of the
  817. variable only valid if this function returns TRUE.
  818. Return Value:
  819. If the enumeration stopped at a specific handle, then a value of TRUE
  820. is returned. Otherwise, a value of FALSE is returned.
  821. --*/
  822. {
  823. PKTHREAD CurrentThread;
  824. BOOLEAN ResultValue;
  825. EXHANDLE LocalHandle;
  826. PHANDLE_TABLE_ENTRY HandleTableEntry;
  827. PAGED_CODE();
  828. CurrentThread = KeGetCurrentThread ();
  829. //
  830. // Our initial return value is false until the enumeration callback
  831. // function tells us otherwise
  832. //
  833. ResultValue = FALSE;
  834. //
  835. // Iterate through the handle table and for each handle that is
  836. // allocated we'll invoke the call back. Note that this loop exits
  837. // when we get a null handle table entry. We know there will be no
  838. // more possible entries after the first null one is encountered
  839. // because we allocate memory for the handles in a dense fashion
  840. //
  841. KeEnterCriticalRegionThread (CurrentThread);
  842. for (LocalHandle.Value = 0; // does essentially the following "LocalHandle.Index = 0, LocalHandle.TagBits = 0;"
  843. (HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, LocalHandle )) != NULL;
  844. LocalHandle.Value += HANDLE_VALUE_INC) {
  845. //
  846. // Only do the callback if the entry is not free
  847. //
  848. if ( ExpIsValidObjectEntry( HandleTableEntry ) ) {
  849. //
  850. // Lock the handle table entry because we're about to give
  851. // it to the callback function, then release the entry
  852. // right after the call back.
  853. //
  854. if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  855. //
  856. // Invoke the callback, and if it returns true then set
  857. // the proper output values and break out of the loop.
  858. //
  859. ResultValue = (*EnumHandleProcedure)( HandleTableEntry,
  860. LocalHandle.GenericHandleOverlay,
  861. EnumParameter );
  862. ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
  863. if (ResultValue) {
  864. if (ARGUMENT_PRESENT( Handle )) {
  865. *Handle = LocalHandle.GenericHandleOverlay;
  866. }
  867. break;
  868. }
  869. }
  870. }
  871. }
  872. KeLeaveCriticalRegionThread (CurrentThread);
  873. return ResultValue;
  874. }
  875. NTKERNELAPI
  876. PHANDLE_TABLE
  877. ExDupHandleTable (
  878. IN struct _EPROCESS *Process OPTIONAL,
  879. IN PHANDLE_TABLE OldHandleTable,
  880. IN EX_DUPLICATE_HANDLE_ROUTINE DupHandleProcedure,
  881. IN ULONG_PTR Mask
  882. )
  883. /*++
  884. Routine Description:
  885. This function creates a duplicate copy of the specified handle table.
  886. Arguments:
  887. Process - Supplies an optional to the process to charge quota to.
  888. OldHandleTable - Supplies a pointer to a handle table.
  889. DupHandleProcedure - Supplies an optional pointer to a function to call
  890. for each valid handle in the duplicated handle table.
  891. Mask - Mask applied to the object pointer to work outif we need to duplicate
  892. Return Value:
  893. If the specified handle table is successfully duplicated, then the
  894. address of the new handle table is returned as the function value.
  895. Otherwize, a value NULL is returned.
  896. --*/
  897. {
  898. PKTHREAD CurrentThread;
  899. PHANDLE_TABLE NewHandleTable;
  900. EXHANDLE Handle;
  901. PHANDLE_TABLE_ENTRY OldHandleTableEntry;
  902. PHANDLE_TABLE_ENTRY NewHandleTableEntry;
  903. BOOLEAN FreeEntry;
  904. NTSTATUS Status;
  905. PAGED_CODE();
  906. CurrentThread = KeGetCurrentThread ();
  907. //
  908. // First allocate a new handle table. If this fails then
  909. // return immediately to our caller
  910. //
  911. NewHandleTable = ExpAllocateHandleTable( Process, FALSE );
  912. if (NewHandleTable == NULL) {
  913. return NULL;
  914. }
  915. //
  916. // Now we'll build up the new handle table. We do this by calling
  917. // allocating new handle table entries, and "fooling" the worker
  918. // routine to allocate keep on allocating until the next free
  919. // index needing pool are equal
  920. //
  921. while (NewHandleTable->NextHandleNeedingPool < OldHandleTable->NextHandleNeedingPool) {
  922. //
  923. // Call the worker routine to grow the new handle table. If
  924. // not successful then free the new table as far as we got,
  925. // set our output variable and exit out here
  926. //
  927. if (!ExpAllocateHandleTableEntrySlow (NewHandleTable, FALSE)) {
  928. ExpFreeHandleTable (NewHandleTable);
  929. return NULL;
  930. }
  931. }
  932. //
  933. // Make sure any table reads occur after the value we fetched from NextHandleNeedingPool
  934. //
  935. KeMemoryBarrier ();
  936. //
  937. // Now modify the new handle table to think it has zero handles
  938. // and set its free list to start on the same index as the old
  939. // free list
  940. //
  941. NewHandleTable->HandleCount = 0;
  942. NewHandleTable->ExtraInfoPages = 0;
  943. NewHandleTable->FirstFree = 0;
  944. //
  945. // Now for every valid index value we'll copy over the old entry into
  946. // the new entry
  947. //
  948. Handle.Value = HANDLE_VALUE_INC;
  949. KeEnterCriticalRegionThread (CurrentThread);
  950. while ((NewHandleTableEntry = ExpLookupHandleTableEntry( NewHandleTable, Handle )) != NULL) {
  951. //
  952. // Lookup the old entry.
  953. //
  954. OldHandleTableEntry = ExpLookupHandleTableEntry( OldHandleTable, Handle );
  955. do {
  956. //
  957. // If the old entry is free then simply copy over the entire
  958. // old entry to the new entry. The lock command will tell us
  959. // if the entry is free.
  960. //
  961. if ((OldHandleTableEntry->Value&Mask) == 0 ||
  962. !ExpLockHandleTableEntry( OldHandleTable, OldHandleTableEntry )) {
  963. FreeEntry = TRUE;
  964. } else {
  965. PHANDLE_TABLE_ENTRY_INFO EntryInfo;
  966. //
  967. // Otherwise we have a non empty entry. So now copy it
  968. // over, and unlock the old entry. In both cases we bump
  969. // the handle count because either the entry is going into
  970. // the new table or we're going to remove it with Exp Free
  971. // Handle Table Entry which will decrement the handle count
  972. //
  973. *NewHandleTableEntry = *OldHandleTableEntry;
  974. //
  975. // Copy the entry info data, if any
  976. //
  977. Status = STATUS_SUCCESS;
  978. EntryInfo = ExGetHandleInfo(OldHandleTable, Handle.GenericHandleOverlay, TRUE);
  979. if (EntryInfo) {
  980. Status = ExSetHandleInfo(NewHandleTable, Handle.GenericHandleOverlay, EntryInfo, TRUE);
  981. }
  982. //
  983. // Invoke the callback and if it returns true then we
  984. // unlock the new entry
  985. //
  986. if (NT_SUCCESS (Status)) {
  987. if ((*DupHandleProcedure) (Process,
  988. OldHandleTable,
  989. OldHandleTableEntry,
  990. NewHandleTableEntry)) {
  991. if (NewHandleTable->DebugInfo != NULL) {
  992. ExpUpdateDebugInfo(
  993. NewHandleTable,
  994. PsGetCurrentThread (),
  995. Handle.GenericHandleOverlay,
  996. HANDLE_TRACE_DB_OPEN);
  997. }
  998. //
  999. // Since there is no route to the new table yet we can just
  1000. // clear the lock bit
  1001. //
  1002. NewHandleTableEntry->Value |= EXHANDLE_TABLE_ENTRY_LOCK_BIT;
  1003. NewHandleTable->HandleCount += 1;
  1004. FreeEntry = FALSE;
  1005. } else {
  1006. if (EntryInfo) {
  1007. EntryInfo->AuditMask = 0;
  1008. }
  1009. FreeEntry = TRUE;
  1010. }
  1011. } else {
  1012. //
  1013. // Duplicate routine doesn't want this handle duplicated so free it
  1014. //
  1015. ExUnlockHandleTableEntry( OldHandleTable, OldHandleTableEntry );
  1016. FreeEntry = TRUE;
  1017. }
  1018. }
  1019. if (FreeEntry) {
  1020. NewHandleTableEntry->Object = NULL;
  1021. NewHandleTableEntry->NextFreeTableEntry =
  1022. NewHandleTable->FirstFree;
  1023. NewHandleTable->FirstFree = (ULONG) Handle.Value;
  1024. }
  1025. Handle.Value += HANDLE_VALUE_INC;
  1026. NewHandleTableEntry++;
  1027. OldHandleTableEntry++;
  1028. } while ((Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC)) != 0);
  1029. Handle.Value += HANDLE_VALUE_INC; // Skip past the first entry thats not a real entry
  1030. }
  1031. //
  1032. // Insert the handle table in the handle table list.
  1033. //
  1034. ExAcquirePushLockExclusive( &HandleTableListLock );
  1035. InsertTailList( &HandleTableListHead, &NewHandleTable->HandleTableList );
  1036. ExReleasePushLockExclusive( &HandleTableListLock );
  1037. KeLeaveCriticalRegionThread (CurrentThread);
  1038. //
  1039. // lastly return the new handle table to our caller
  1040. //
  1041. return NewHandleTable;
  1042. }
  1043. NTKERNELAPI
  1044. NTSTATUS
  1045. ExSnapShotHandleTables (
  1046. IN PEX_SNAPSHOT_HANDLE_ENTRY SnapShotHandleEntry,
  1047. IN OUT PSYSTEM_HANDLE_INFORMATION HandleInformation,
  1048. IN ULONG Length,
  1049. IN OUT PULONG RequiredLength
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. This function visits and invokes the specified callback for every valid
  1054. handle that it can find off of the handle table.
  1055. Arguments:
  1056. SnapShotHandleEntry - Supplies a pointer to a function to call for
  1057. each valid handle we encounter.
  1058. HandleInformation - Supplies a handle information structure to
  1059. be filled in for each handle table we encounter. This routine
  1060. fills in the handle count, but relies on a callback to fill in
  1061. entry info fields.
  1062. Length - Supplies a parameter for the callback. In reality this is
  1063. the total size, in bytes, of the Handle Information buffer.
  1064. RequiredLength - Supplies a parameter for the callback. In reality
  1065. this is a final size in bytes used to store the requested
  1066. information.
  1067. Return Value:
  1068. The last return status of the callback
  1069. --*/
  1070. {
  1071. NTSTATUS Status;
  1072. PKTHREAD CurrentThread;
  1073. PSYSTEM_HANDLE_TABLE_ENTRY_INFO HandleEntryInfo;
  1074. PLIST_ENTRY NextEntry;
  1075. PHANDLE_TABLE HandleTable;
  1076. EXHANDLE Handle;
  1077. PHANDLE_TABLE_ENTRY HandleTableEntry;
  1078. PAGED_CODE();
  1079. CurrentThread = KeGetCurrentThread ();
  1080. Status = STATUS_SUCCESS;
  1081. //
  1082. // Setup the output buffer pointer that the callback will maintain
  1083. //
  1084. HandleEntryInfo = &HandleInformation->Handles[0];
  1085. //
  1086. // Zero out the handle count
  1087. //
  1088. HandleInformation->NumberOfHandles = 0;
  1089. //
  1090. // Lock the handle table list exclusive and traverse the list of handle
  1091. // tables.
  1092. //
  1093. KeEnterCriticalRegionThread (CurrentThread);
  1094. ExAcquirePushLockShared( &HandleTableListLock );
  1095. //
  1096. // Iterate through all the handle tables in the system.
  1097. //
  1098. for (NextEntry = HandleTableListHead.Flink;
  1099. NextEntry != &HandleTableListHead;
  1100. NextEntry = NextEntry->Flink) {
  1101. //
  1102. // Get the address of the next handle table, lock the handle
  1103. // table exclusive, and scan the list of handle entries.
  1104. //
  1105. HandleTable = CONTAINING_RECORD( NextEntry,
  1106. HANDLE_TABLE,
  1107. HandleTableList );
  1108. // Iterate through the handle table and for each handle that
  1109. // is allocated we'll invoke the call back. Note that this
  1110. // loop exits when we get a null handle table entry. We know
  1111. // there will be no more possible entries after the first null
  1112. // one is encountered because we allocate memory of the
  1113. // handles in a dense fashion
  1114. //
  1115. for (Handle.Value = 0;
  1116. (HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
  1117. Handle.Value += HANDLE_VALUE_INC) {
  1118. //
  1119. // Only do the callback if the entry is not free
  1120. //
  1121. if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
  1122. //
  1123. // Increment the handle count information in the
  1124. // information buffer
  1125. //
  1126. HandleInformation->NumberOfHandles += 1;
  1127. //
  1128. // Lock the handle table entry because we're about to
  1129. // give it to the callback function, then release the
  1130. // entry right after the call back.
  1131. //
  1132. if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  1133. Status = (*SnapShotHandleEntry)( &HandleEntryInfo,
  1134. HandleTable->UniqueProcessId,
  1135. HandleTableEntry,
  1136. Handle.GenericHandleOverlay,
  1137. Length,
  1138. RequiredLength );
  1139. ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
  1140. }
  1141. }
  1142. }
  1143. }
  1144. ExReleasePushLockShared( &HandleTableListLock );
  1145. KeLeaveCriticalRegionThread (CurrentThread);
  1146. return Status;
  1147. }
  1148. NTKERNELAPI
  1149. NTSTATUS
  1150. ExSnapShotHandleTablesEx (
  1151. IN PEX_SNAPSHOT_HANDLE_ENTRY_EX SnapShotHandleEntry,
  1152. IN OUT PSYSTEM_HANDLE_INFORMATION_EX HandleInformation,
  1153. IN ULONG Length,
  1154. IN OUT PULONG RequiredLength
  1155. )
  1156. /*++
  1157. Routine Description:
  1158. This function visits and invokes the specified callback for every valid
  1159. handle that it can find off of the handle table.
  1160. Arguments:
  1161. SnapShotHandleEntry - Supplies a pointer to a function to call for
  1162. each valid handle we encounter.
  1163. HandleInformation - Supplies a handle information structure to
  1164. be filled in for each handle table we encounter. This routine
  1165. fills in the handle count, but relies on a callback to fill in
  1166. entry info fields.
  1167. Length - Supplies a parameter for the callback. In reality this is
  1168. the total size, in bytes, of the Handle Information buffer.
  1169. RequiredLength - Supplies a parameter for the callback. In reality
  1170. this is a final size in bytes used to store the requested
  1171. information.
  1172. Return Value:
  1173. The last return status of the callback
  1174. --*/
  1175. {
  1176. NTSTATUS Status;
  1177. PKTHREAD CurrentThread;
  1178. PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntryInfo;
  1179. PLIST_ENTRY NextEntry;
  1180. PHANDLE_TABLE HandleTable;
  1181. EXHANDLE Handle;
  1182. PHANDLE_TABLE_ENTRY HandleTableEntry;
  1183. PAGED_CODE();
  1184. CurrentThread = KeGetCurrentThread ();
  1185. Status = STATUS_SUCCESS;
  1186. //
  1187. // Setup the output buffer pointer that the callback will maintain
  1188. //
  1189. HandleEntryInfo = &HandleInformation->Handles[0];
  1190. //
  1191. // Zero out the handle count
  1192. //
  1193. HandleInformation->NumberOfHandles = 0;
  1194. //
  1195. // Lock the handle table list exclusive and traverse the list of handle
  1196. // tables.
  1197. //
  1198. KeEnterCriticalRegionThread (CurrentThread);
  1199. ExAcquirePushLockShared( &HandleTableListLock );
  1200. //
  1201. // Iterate through all the handle tables in the system.
  1202. //
  1203. for (NextEntry = HandleTableListHead.Flink;
  1204. NextEntry != &HandleTableListHead;
  1205. NextEntry = NextEntry->Flink) {
  1206. //
  1207. // Get the address of the next handle table, lock the handle
  1208. // table exclusive, and scan the list of handle entries.
  1209. //
  1210. HandleTable = CONTAINING_RECORD( NextEntry,
  1211. HANDLE_TABLE,
  1212. HandleTableList );
  1213. // Iterate through the handle table and for each handle that
  1214. // is allocated we'll invoke the call back. Note that this
  1215. // loop exits when we get a null handle table entry. We know
  1216. // there will be no more possible entries after the first null
  1217. // one is encountered because we allocate memory of the
  1218. // handles in a dense fashion
  1219. //
  1220. for (Handle.Value = 0;
  1221. (HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, Handle )) != NULL;
  1222. Handle.Value += HANDLE_VALUE_INC) {
  1223. //
  1224. // Only do the callback if the entry is not free
  1225. //
  1226. if ( ExpIsValidObjectEntry(HandleTableEntry) ) {
  1227. //
  1228. // Increment the handle count information in the
  1229. // information buffer
  1230. //
  1231. HandleInformation->NumberOfHandles += 1;
  1232. //
  1233. // Lock the handle table entry because we're about to
  1234. // give it to the callback function, then release the
  1235. // entry right after the call back.
  1236. //
  1237. if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  1238. Status = (*SnapShotHandleEntry)( &HandleEntryInfo,
  1239. HandleTable->UniqueProcessId,
  1240. HandleTableEntry,
  1241. Handle.GenericHandleOverlay,
  1242. Length,
  1243. RequiredLength );
  1244. ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
  1245. }
  1246. }
  1247. }
  1248. }
  1249. ExReleasePushLockShared( &HandleTableListLock );
  1250. KeLeaveCriticalRegionThread (CurrentThread);
  1251. return Status;
  1252. }
  1253. NTKERNELAPI
  1254. HANDLE
  1255. ExCreateHandle (
  1256. IN PHANDLE_TABLE HandleTable,
  1257. IN PHANDLE_TABLE_ENTRY HandleTableEntry
  1258. )
  1259. /*++
  1260. Routine Description:
  1261. This function creates a handle entry in the specified handle table and
  1262. returns a handle for the entry.
  1263. Arguments:
  1264. HandleTable - Supplies a pointer to a handle table
  1265. HandleEntry - Supplies a poiner to the handle entry for which a
  1266. handle entry is created.
  1267. Return Value:
  1268. If the handle entry is successfully created, then value of the created
  1269. handle is returned as the function value. Otherwise, a value of zero is
  1270. returned.
  1271. --*/
  1272. {
  1273. EXHANDLE Handle;
  1274. PETHREAD CurrentThread;
  1275. PHANDLE_TABLE_ENTRY NewHandleTableEntry;
  1276. PAGED_CODE();
  1277. //
  1278. // Set out output variable to zero (i.e., null) before going on
  1279. //
  1280. //
  1281. // Clears Handle.Index and Handle.TagBits
  1282. //
  1283. Handle.GenericHandleOverlay = NULL;
  1284. //
  1285. // Allocate a new handle table entry, and get the handle value
  1286. //
  1287. NewHandleTableEntry = ExpAllocateHandleTableEntry( HandleTable,
  1288. &Handle );
  1289. //
  1290. // If we really got a handle then copy over the template and unlock
  1291. // the entry
  1292. //
  1293. if (NewHandleTableEntry != NULL) {
  1294. CurrentThread = PsGetCurrentThread ();
  1295. //
  1296. // We are about to create a locked entry so protect against suspension
  1297. //
  1298. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1299. *NewHandleTableEntry = *HandleTableEntry;
  1300. //
  1301. // If we are debugging handle operations then save away the details
  1302. //
  1303. if (HandleTable->DebugInfo != NULL) {
  1304. ExpUpdateDebugInfo(HandleTable, CurrentThread, Handle.GenericHandleOverlay, HANDLE_TRACE_DB_OPEN);
  1305. }
  1306. ExUnlockHandleTableEntry( HandleTable, NewHandleTableEntry );
  1307. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1308. }
  1309. return Handle.GenericHandleOverlay;
  1310. }
  1311. NTKERNELAPI
  1312. BOOLEAN
  1313. ExDestroyHandle (
  1314. IN PHANDLE_TABLE HandleTable,
  1315. IN HANDLE Handle,
  1316. IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL
  1317. )
  1318. /*++
  1319. Routine Description:
  1320. This function removes a handle from a handle table.
  1321. Arguments:
  1322. HandleTable - Supplies a pointer to a handle table
  1323. Handle - Supplies the handle value of the entry to remove.
  1324. HandleTableEntry - Optionally supplies a pointer to the handle
  1325. table entry being destroyed. If supplied the entry is
  1326. assume to be locked.
  1327. Return Value:
  1328. If the specified handle is successfully removed, then a value of
  1329. TRUE is returned. Otherwise, a value of FALSE is returned.
  1330. --*/
  1331. {
  1332. EXHANDLE LocalHandle;
  1333. PETHREAD CurrentThread;
  1334. PVOID Object;
  1335. PAGED_CODE();
  1336. LocalHandle.GenericHandleOverlay = Handle;
  1337. CurrentThread = PsGetCurrentThread ();
  1338. //
  1339. // If the caller did not supply the optional handle table entry then
  1340. // locate the entry via the supplied handle, make sure it is real, and
  1341. // then lock the entry.
  1342. //
  1343. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1344. if (HandleTableEntry == NULL) {
  1345. HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
  1346. LocalHandle );
  1347. if (!ExpIsValidObjectEntry(HandleTableEntry)) {
  1348. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1349. return FALSE;
  1350. }
  1351. if (!ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  1352. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1353. return FALSE;
  1354. }
  1355. } else {
  1356. EXASSERT ((HandleTableEntry->Value&EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
  1357. }
  1358. //
  1359. // If we are debugging handle operations then save away the details
  1360. //
  1361. if (HandleTable->DebugInfo != NULL) {
  1362. ExpUpdateDebugInfo(HandleTable, CurrentThread, Handle, HANDLE_TRACE_DB_CLOSE);
  1363. }
  1364. //
  1365. // At this point we have a locked handle table entry. Now mark it free
  1366. // which does the implicit unlock. The system will not allocate it
  1367. // again until we add it to the free list which we will do right after
  1368. // we take out the lock
  1369. //
  1370. Object = InterlockedExchangePointer (&HandleTableEntry->Object, NULL);
  1371. EXASSERT (Object != NULL);
  1372. EXASSERT ((((ULONG_PTR)Object)&EXHANDLE_TABLE_ENTRY_LOCK_BIT) == 0);
  1373. //
  1374. // Unblock any waiters waiting for this table entry.
  1375. //
  1376. ExUnblockPushLock (&HandleTable->HandleContentionEvent, NULL);
  1377. ExpFreeHandleTableEntry( HandleTable,
  1378. LocalHandle,
  1379. HandleTableEntry );
  1380. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1381. return TRUE;
  1382. }
  1383. NTKERNELAPI
  1384. BOOLEAN
  1385. ExChangeHandle (
  1386. IN PHANDLE_TABLE HandleTable,
  1387. IN HANDLE Handle,
  1388. IN PEX_CHANGE_HANDLE_ROUTINE ChangeRoutine,
  1389. IN ULONG_PTR Parameter
  1390. )
  1391. /*++
  1392. Routine Description:
  1393. This function provides the capability to change the contents of the
  1394. handle entry corrsponding to the specified handle.
  1395. Arguments:
  1396. HandleTable - Supplies a pointer to a handle table.
  1397. Handle - Supplies the handle for the handle entry that is changed.
  1398. ChangeRoutine - Supplies a pointer to a function that is called to
  1399. perform the change.
  1400. Parameter - Supplies an uninterpreted parameter that is passed to
  1401. the change routine.
  1402. Return Value:
  1403. If the operation was successfully performed, then a value of TRUE
  1404. is returned. Otherwise, a value of FALSE is returned.
  1405. --*/
  1406. {
  1407. EXHANDLE LocalHandle;
  1408. PKTHREAD CurrentThread;
  1409. PHANDLE_TABLE_ENTRY HandleTableEntry;
  1410. BOOLEAN ReturnValue;
  1411. PAGED_CODE();
  1412. LocalHandle.GenericHandleOverlay = Handle;
  1413. CurrentThread = KeGetCurrentThread ();
  1414. //
  1415. // Translate the input handle to a handle table entry and make
  1416. // sure it is a valid handle.
  1417. //
  1418. HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
  1419. LocalHandle );
  1420. if ((HandleTableEntry == NULL) ||
  1421. !ExpIsValidObjectEntry(HandleTableEntry)) {
  1422. return FALSE;
  1423. }
  1424. //
  1425. // Try and lock the handle table entry, If this fails then that's
  1426. // because someone freed the handle
  1427. //
  1428. //
  1429. // Make sure we can't get suspended and then invoke the callback
  1430. //
  1431. KeEnterCriticalRegionThread (CurrentThread);
  1432. if (ExpLockHandleTableEntry( HandleTable, HandleTableEntry )) {
  1433. ReturnValue = (*ChangeRoutine)( HandleTableEntry, Parameter );
  1434. ExUnlockHandleTableEntry( HandleTable, HandleTableEntry );
  1435. } else {
  1436. ReturnValue = FALSE;
  1437. }
  1438. KeLeaveCriticalRegionThread (CurrentThread);
  1439. return ReturnValue;
  1440. }
  1441. NTKERNELAPI
  1442. PHANDLE_TABLE_ENTRY
  1443. ExMapHandleToPointer (
  1444. IN PHANDLE_TABLE HandleTable,
  1445. IN HANDLE Handle
  1446. )
  1447. /*++
  1448. Routine Description:
  1449. This function maps a handle to a pointer to a handle table entry. If the
  1450. map operation is successful then the handle table entry is locked when
  1451. we return.
  1452. Arguments:
  1453. HandleTable - Supplies a pointer to a handle table.
  1454. Handle - Supplies the handle to be mapped to a handle entry.
  1455. Return Value:
  1456. If the handle was successfully mapped to a pointer to a handle entry,
  1457. then the address of the handle table entry is returned as the function
  1458. value with the entry locked. Otherwise, a value of NULL is returned.
  1459. --*/
  1460. {
  1461. EXHANDLE LocalHandle;
  1462. PHANDLE_TABLE_ENTRY HandleTableEntry;
  1463. PAGED_CODE();
  1464. LocalHandle.GenericHandleOverlay = Handle;
  1465. if ((LocalHandle.Index & (LOWLEVEL_COUNT - 1)) == 0) {
  1466. return NULL;
  1467. }
  1468. //
  1469. // Translate the input handle to a handle table entry and make
  1470. // sure it is a valid handle.
  1471. //
  1472. HandleTableEntry = ExpLookupHandleTableEntry( HandleTable,
  1473. LocalHandle );
  1474. if ((HandleTableEntry == NULL) ||
  1475. !ExpLockHandleTableEntry( HandleTable, HandleTableEntry)) {
  1476. //
  1477. // If we are debugging handle operations then save away the details
  1478. //
  1479. if (HandleTable->DebugInfo != NULL) {
  1480. ExpUpdateDebugInfo(HandleTable, PsGetCurrentThread (), Handle, HANDLE_TRACE_DB_BADREF);
  1481. }
  1482. return NULL;
  1483. }
  1484. //
  1485. // Return the locked valid handle table entry
  1486. //
  1487. return HandleTableEntry;
  1488. }
  1489. NTKERNELAPI
  1490. PHANDLE_TABLE_ENTRY
  1491. ExMapHandleToPointerEx (
  1492. IN PHANDLE_TABLE HandleTable,
  1493. IN HANDLE Handle,
  1494. IN KPROCESSOR_MODE PreviousMode
  1495. )
  1496. /*++
  1497. Routine Description:
  1498. This function maps a handle to a pointer to a handle table entry. If the
  1499. map operation is successful then the handle table entry is locked when
  1500. we return.
  1501. Arguments:
  1502. HandleTable - Supplies a pointer to a handle table.
  1503. Handle - Supplies the handle to be mapped to a handle entry.
  1504. PreviousMode - Previous mode of caller
  1505. Return Value:
  1506. If the handle was successfully mapped to a pointer to a handle entry,
  1507. then the address of the handle table entry is returned as the function
  1508. value with the entry locked. Otherwise, a value of NULL is returned.
  1509. --*/
  1510. {
  1511. EXHANDLE LocalHandle;
  1512. PHANDLE_TABLE_ENTRY HandleTableEntry = NULL;
  1513. PETHREAD CurrentThread;
  1514. PAGED_CODE();
  1515. LocalHandle.GenericHandleOverlay = Handle;
  1516. //
  1517. // Translate the input handle to a handle table entry and make
  1518. // sure it is a valid handle.
  1519. //
  1520. if (((LocalHandle.Index & (LOWLEVEL_COUNT - 1)) == 0) ||
  1521. ((HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, LocalHandle)) == NULL) ||
  1522. !ExpLockHandleTableEntry( HandleTable, HandleTableEntry)) {
  1523. //
  1524. // If we are debugging handle operations then save away the details
  1525. //
  1526. if (HandleTable->DebugInfo != NULL) {
  1527. CurrentThread = PsGetCurrentThread ();
  1528. ExpUpdateDebugInfo(HandleTable, CurrentThread, Handle, HANDLE_TRACE_DB_BADREF);
  1529. //
  1530. // Since we have a non-null DebugInfo for the handle table of this
  1531. // process it means application verifier was enabled for this process.
  1532. //
  1533. if (PreviousMode == UserMode) {
  1534. if (!KeIsAttachedProcess()) {
  1535. //
  1536. // If the current process is marked for verification
  1537. // then we will raise an exception in user mode. In case
  1538. // application verifier is enabled system wide we will
  1539. // break first.
  1540. //
  1541. if ((NtGlobalFlag & FLG_APPLICATION_VERIFIER)) {
  1542. DbgPrint ("AVRF: Invalid handle %p in process %p \n",
  1543. Handle,
  1544. PsGetCurrentProcess());
  1545. // DbgBreakPoint ();
  1546. }
  1547. KeRaiseUserException (STATUS_INVALID_HANDLE);
  1548. }
  1549. } else {
  1550. //
  1551. // We bugcheck for kernel handles only if we have the handle
  1552. // exceptions flag set system-wide. This way a user enabling
  1553. // application verifier for a process will not get bugchecks
  1554. // only user mode errors.
  1555. //
  1556. if ((NtGlobalFlag & FLG_ENABLE_HANDLE_EXCEPTIONS)) {
  1557. KeBugCheckEx(INVALID_KERNEL_HANDLE,
  1558. (ULONG_PTR)Handle,
  1559. (ULONG_PTR)HandleTable,
  1560. (ULONG_PTR)HandleTableEntry,
  1561. 0x1);
  1562. }
  1563. }
  1564. }
  1565. return NULL;
  1566. }
  1567. //
  1568. // Return the locked valid handle table entry
  1569. //
  1570. return HandleTableEntry;
  1571. }
  1572. //
  1573. // Local Support Routine
  1574. //
  1575. PVOID
  1576. ExpAllocateTablePagedPool (
  1577. IN PEPROCESS QuotaProcess OPTIONAL,
  1578. IN SIZE_T NumberOfBytes
  1579. )
  1580. {
  1581. PVOID PoolMemory;
  1582. PoolMemory = ExAllocatePoolWithTag( PagedPool,
  1583. NumberOfBytes,
  1584. 'btbO' );
  1585. if (PoolMemory != NULL) {
  1586. RtlZeroMemory( PoolMemory,
  1587. NumberOfBytes );
  1588. if (ARGUMENT_PRESENT(QuotaProcess)) {
  1589. if (!NT_SUCCESS (PsChargeProcessPagedPoolQuota ( QuotaProcess,
  1590. NumberOfBytes ))) {
  1591. ExFreePool( PoolMemory );
  1592. PoolMemory = NULL;
  1593. }
  1594. }
  1595. }
  1596. return PoolMemory;
  1597. }
  1598. PVOID
  1599. ExpAllocateTablePagedPoolNoZero (
  1600. IN PEPROCESS QuotaProcess OPTIONAL,
  1601. IN SIZE_T NumberOfBytes
  1602. )
  1603. {
  1604. PVOID PoolMemory;
  1605. PoolMemory = ExAllocatePoolWithTag( PagedPool,
  1606. NumberOfBytes,
  1607. 'btbO' );
  1608. if (PoolMemory != NULL) {
  1609. if (ARGUMENT_PRESENT(QuotaProcess)) {
  1610. if (!NT_SUCCESS (PsChargeProcessPagedPoolQuota ( QuotaProcess,
  1611. NumberOfBytes ))) {
  1612. ExFreePool( PoolMemory );
  1613. PoolMemory = NULL;
  1614. }
  1615. }
  1616. }
  1617. return PoolMemory;
  1618. }
  1619. //
  1620. // Local Support Routine
  1621. //
  1622. VOID
  1623. ExpFreeTablePagedPool (
  1624. IN PEPROCESS QuotaProcess OPTIONAL,
  1625. IN PVOID PoolMemory,
  1626. IN SIZE_T NumberOfBytes
  1627. )
  1628. {
  1629. ExFreePool( PoolMemory );
  1630. if ( QuotaProcess ) {
  1631. PsReturnProcessPagedPoolQuota( QuotaProcess,
  1632. NumberOfBytes
  1633. );
  1634. }
  1635. }
  1636. PHANDLE_TRACE_DEBUG_INFO
  1637. ExReferenceHandleDebugInfo (
  1638. IN PHANDLE_TABLE HandleTable
  1639. )
  1640. {
  1641. LONG RetVal;
  1642. PHANDLE_TRACE_DEBUG_INFO DebugInfo;
  1643. PKTHREAD CurrentThread;
  1644. CurrentThread = KeGetCurrentThread ();
  1645. ExpLockHandleTableShared (HandleTable, CurrentThread, 0);
  1646. DebugInfo = HandleTable->DebugInfo;
  1647. if (DebugInfo != NULL) {
  1648. RetVal = InterlockedIncrement (&DebugInfo->RefCount);
  1649. ASSERT (RetVal > 0);
  1650. }
  1651. ExpUnlockHandleTableShared (HandleTable, CurrentThread, 0);
  1652. return DebugInfo;
  1653. }
  1654. VOID
  1655. ExDereferenceHandleDebugInfo (
  1656. IN PHANDLE_TABLE HandleTable,
  1657. IN PHANDLE_TRACE_DEBUG_INFO DebugInfo
  1658. )
  1659. {
  1660. ULONG TraceSize;
  1661. ULONG TableSize;
  1662. LONG RetVal;
  1663. RetVal = InterlockedDecrement (&DebugInfo->RefCount);
  1664. ASSERT (RetVal >= 0);
  1665. if (RetVal == 0) {
  1666. TableSize = DebugInfo->TableSize;
  1667. TraceSize = sizeof (*DebugInfo) + TableSize * sizeof (DebugInfo->TraceDb[0]) - sizeof (DebugInfo->TraceDb);
  1668. ExFreePool (DebugInfo);
  1669. if (HandleTable->QuotaProcess != NULL) {
  1670. PsReturnProcessNonPagedPoolQuota (HandleTable->QuotaProcess,
  1671. TraceSize);
  1672. }
  1673. InterlockedExchangeAdd ((PLONG) &TotalTraceBuffers, -(LONG)TableSize);
  1674. }
  1675. }
  1676. NTKERNELAPI
  1677. NTSTATUS
  1678. ExDisableHandleTracing (
  1679. IN PHANDLE_TABLE HandleTable
  1680. )
  1681. /*++
  1682. Routine Description:
  1683. This routine turns off handle tracing for the specified table
  1684. Arguments:
  1685. HandleTable - Table to disable tracing in
  1686. Return Value:
  1687. NTSTATUS - Status of operation
  1688. --*/
  1689. {
  1690. PHANDLE_TRACE_DEBUG_INFO DebugInfo;
  1691. PKTHREAD CurrentThread;
  1692. CurrentThread = KeGetCurrentThread ();
  1693. ExpLockHandleTableExclusive (HandleTable, CurrentThread);
  1694. DebugInfo = HandleTable->DebugInfo;
  1695. HandleTable->DebugInfo = NULL;
  1696. ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
  1697. if (DebugInfo != NULL) {
  1698. ExDereferenceHandleDebugInfo (HandleTable, DebugInfo);
  1699. }
  1700. return STATUS_SUCCESS;
  1701. }
  1702. NTKERNELAPI
  1703. NTSTATUS
  1704. ExEnableHandleTracing (
  1705. IN PHANDLE_TABLE HandleTable,
  1706. IN ULONG Slots
  1707. )
  1708. /*++
  1709. Routine Description:
  1710. This routine turns on handle tracing for the specified table
  1711. Arguments:
  1712. HandleTable - Table to enable tracing in
  1713. Return Value:
  1714. NTSTATUS - Status of operation
  1715. --*/
  1716. {
  1717. PHANDLE_TRACE_DEBUG_INFO DebugInfo, OldDebugInfo;
  1718. PEPROCESS Process;
  1719. PKTHREAD CurrentThread;
  1720. NTSTATUS Status;
  1721. SIZE_T TotalNow;
  1722. extern SIZE_T MmMaximumNonPagedPoolInBytes;
  1723. SIZE_T TraceSize;
  1724. LONG TotalSlots;
  1725. if (Slots == 0) {
  1726. TotalSlots = HANDLE_TRACE_DB_DEFAULT_STACKS;
  1727. } else {
  1728. if (Slots < HANDLE_TRACE_DB_MIN_STACKS) {
  1729. TotalSlots = HANDLE_TRACE_DB_MIN_STACKS;
  1730. } else {
  1731. TotalSlots = Slots;
  1732. }
  1733. if (TotalSlots > HANDLE_TRACE_DB_MAX_STACKS) {
  1734. TotalSlots = HANDLE_TRACE_DB_MAX_STACKS;
  1735. }
  1736. //
  1737. // Round the value up to the next power of 2
  1738. //
  1739. while ((TotalSlots & (TotalSlots - 1)) != 0) {
  1740. TotalSlots |= (TotalSlots - 1);
  1741. TotalSlots += 1;
  1742. }
  1743. }
  1744. //
  1745. // Total slots needs to be a power of two
  1746. //
  1747. ASSERT ((TotalSlots & (TotalSlots - 1)) == 0);
  1748. ASSERT (TotalSlots > 0 && TotalSlots <= HANDLE_TRACE_DB_MAX_STACKS);
  1749. TraceSize = sizeof (*DebugInfo) + TotalSlots * sizeof (DebugInfo->TraceDb[0]) - sizeof (DebugInfo->TraceDb);
  1750. TotalNow = InterlockedExchangeAdd ((PLONG) &TotalTraceBuffers, TotalSlots);
  1751. //
  1752. // See if we used more than 30% of nonpaged pool.
  1753. //
  1754. if ((SIZE_T)TotalNow * sizeof (DebugInfo->TraceDb[0]) > (MmMaximumNonPagedPoolInBytes * 30 / 100)) {
  1755. Status = STATUS_INSUFFICIENT_RESOURCES;
  1756. goto return_and_exit;
  1757. }
  1758. Process = HandleTable->QuotaProcess;
  1759. if (Process) {
  1760. Status = PsChargeProcessNonPagedPoolQuota (Process,
  1761. TraceSize);
  1762. if (!NT_SUCCESS (Status)) {
  1763. goto return_and_exit;
  1764. }
  1765. }
  1766. //
  1767. // Allocate the handle debug database
  1768. //
  1769. DebugInfo = ExAllocatePoolWithTag (NonPagedPool,
  1770. TraceSize,
  1771. 'dtbO');
  1772. if (DebugInfo == NULL) {
  1773. Status = STATUS_INSUFFICIENT_RESOURCES;
  1774. goto quota_return_and_exit;
  1775. }
  1776. RtlZeroMemory (DebugInfo, TraceSize);
  1777. DebugInfo->RefCount = 1;
  1778. DebugInfo->TableSize = TotalSlots;
  1779. ExInitializeFastMutex(&DebugInfo->CloseCompactionLock);
  1780. //
  1781. // Since we are tracing then we should enforce strict FIFO
  1782. // Only do this for tables with processes so we leave atom tables alone.
  1783. //
  1784. if (Process != NULL) {
  1785. HandleTable->StrictFIFO = TRUE;
  1786. }
  1787. //
  1788. // Put the new table in place releasing any existing table
  1789. //
  1790. CurrentThread = KeGetCurrentThread ();
  1791. ExpLockHandleTableExclusive (HandleTable, CurrentThread);
  1792. OldDebugInfo = HandleTable->DebugInfo;
  1793. HandleTable->DebugInfo = DebugInfo;
  1794. ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
  1795. if (OldDebugInfo != NULL) {
  1796. ExDereferenceHandleDebugInfo (HandleTable, OldDebugInfo);
  1797. }
  1798. return STATUS_SUCCESS;
  1799. quota_return_and_exit:
  1800. if (Process) {
  1801. PsReturnProcessNonPagedPoolQuota (Process,
  1802. TraceSize);
  1803. }
  1804. return_and_exit:
  1805. InterlockedExchangeAdd ((PLONG) &TotalTraceBuffers, -TotalSlots);
  1806. return Status;
  1807. }
  1808. //
  1809. // Local Support Routine
  1810. //
  1811. PHANDLE_TABLE
  1812. ExpAllocateHandleTable (
  1813. IN PEPROCESS Process OPTIONAL,
  1814. IN BOOLEAN DoInit
  1815. )
  1816. /*++
  1817. Routine Description:
  1818. This worker routine will allocate and initialize a new handle table
  1819. structure. The new structure consists of the basic handle table
  1820. struct plus the first allocation needed to store handles. This is
  1821. really one page divided up into the top level node, the first mid
  1822. level node, and one bottom level node.
  1823. Arguments:
  1824. Process - Optionally supplies the process to charge quota for the
  1825. handle table
  1826. DoInit - If FALSE then we are being called by duplicate and we don't need
  1827. the free list built for the caller
  1828. Return Value:
  1829. A pointer to the new handle table or NULL if unsuccessful at getting
  1830. pool.
  1831. --*/
  1832. {
  1833. PHANDLE_TABLE HandleTable;
  1834. PHANDLE_TABLE_ENTRY HandleTableTable, HandleEntry;
  1835. ULONG i, Idx;
  1836. PAGED_CODE();
  1837. //
  1838. // If any alloation or quota failures happen we will catch it in the
  1839. // following try-except clause and cleanup after outselves before
  1840. // we return null
  1841. //
  1842. //
  1843. // First allocate the handle table, make sure we got one, charge quota
  1844. // for it and then zero it out
  1845. //
  1846. HandleTable = (PHANDLE_TABLE)ExAllocatePoolWithTag (PagedPool,
  1847. sizeof(HANDLE_TABLE),
  1848. 'btbO');
  1849. if (HandleTable == NULL) {
  1850. return NULL;
  1851. }
  1852. if (ARGUMENT_PRESENT(Process)) {
  1853. if (!NT_SUCCESS (PsChargeProcessPagedPoolQuota( Process,
  1854. sizeof(HANDLE_TABLE)))) {
  1855. ExFreePool( HandleTable );
  1856. return NULL;
  1857. }
  1858. }
  1859. RtlZeroMemory( HandleTable, sizeof(HANDLE_TABLE) );
  1860. //
  1861. // Now allocate space of the top level, one mid level and one bottom
  1862. // level table structure. This will all fit on a page, maybe two.
  1863. //
  1864. HandleTableTable = ExpAllocateTablePagedPoolNoZero ( Process,
  1865. TABLE_PAGE_SIZE
  1866. );
  1867. if ( HandleTableTable == NULL ) {
  1868. ExFreePool( HandleTable );
  1869. if (ARGUMENT_PRESENT(Process)) {
  1870. PsReturnProcessPagedPoolQuota (Process,
  1871. sizeof(HANDLE_TABLE));
  1872. }
  1873. return NULL;
  1874. }
  1875. HandleTable->TableCode = (ULONG_PTR)HandleTableTable;
  1876. //
  1877. // We stamp with EX_ADDITIONAL_INFO_SIGNATURE to recognize in the future this
  1878. // is a special information entry
  1879. //
  1880. HandleEntry = &HandleTableTable[0];
  1881. HandleEntry->NextFreeTableEntry = EX_ADDITIONAL_INFO_SIGNATURE;
  1882. HandleEntry->Value = 0;
  1883. //
  1884. // For duplicate calls we skip building the free list as we rebuild it manually as
  1885. // we traverse the old table we are duplicating
  1886. //
  1887. if (DoInit) {
  1888. HandleEntry++;
  1889. //
  1890. // Now setup the free list. We do this by chaining together the free
  1891. // entries such that each free entry give the next free index (i.e.,
  1892. // like a fat chain). The chain is terminated with a 0. Note that
  1893. // we'll skip handle zero because our callers will get that value
  1894. // confused with null.
  1895. //
  1896. for (i = 1; i < LOWLEVEL_COUNT - 1; i += 1) {
  1897. HandleEntry->Value = 0;
  1898. HandleEntry->NextFreeTableEntry = (i+1)*HANDLE_VALUE_INC;
  1899. HandleEntry++;
  1900. }
  1901. HandleEntry->Value = 0;
  1902. HandleEntry->NextFreeTableEntry = 0;
  1903. HandleTable->FirstFree = HANDLE_VALUE_INC;
  1904. }
  1905. HandleTable->NextHandleNeedingPool = LOWLEVEL_COUNT * HANDLE_VALUE_INC;
  1906. //
  1907. // Setup the necessary process information
  1908. //
  1909. HandleTable->QuotaProcess = Process;
  1910. HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId;
  1911. HandleTable->Flags = 0;
  1912. #if DBG && !EXHANDLE_EXTRA_CHECKS
  1913. if (Process != NULL) {
  1914. HandleTable->StrictFIFO = TRUE;
  1915. }
  1916. #endif
  1917. //
  1918. // Initialize the handle table lock. This is only used by table expansion.
  1919. //
  1920. for (Idx = 0; Idx < HANDLE_TABLE_LOCKS; Idx++) {
  1921. ExInitializePushLock (&HandleTable->HandleTableLock[Idx]);
  1922. }
  1923. //
  1924. // Initialize the blocker for handle entry lock contention.
  1925. //
  1926. ExInitializePushLock (&HandleTable->HandleContentionEvent);
  1927. if (ExTraceAllTables) {
  1928. ExEnableHandleTracing (HandleTable, 0);
  1929. }
  1930. //
  1931. // And return to our caller
  1932. //
  1933. return HandleTable;
  1934. }
  1935. //
  1936. // Local Support Routine
  1937. //
  1938. VOID
  1939. ExpFreeLowLevelTable (
  1940. IN PEPROCESS QuotaProcess,
  1941. IN PHANDLE_TABLE_ENTRY TableLevel1
  1942. )
  1943. /*++
  1944. Routine Description:
  1945. This worker routine frees a low-level handle table
  1946. and the additional info memory, if any.
  1947. Arguments:
  1948. HandleTable - Supplies the handle table being freed
  1949. Return Value:
  1950. None.
  1951. --*/
  1952. {
  1953. //
  1954. // Check whether we have a pool allocated for the additional info
  1955. //
  1956. if (TableLevel1[0].Object) {
  1957. ExpFreeTablePagedPool( QuotaProcess,
  1958. TableLevel1[0].Object,
  1959. LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO)
  1960. );
  1961. }
  1962. //
  1963. // Now free the low level table and return the quota for the process
  1964. //
  1965. ExpFreeTablePagedPool( QuotaProcess,
  1966. TableLevel1,
  1967. TABLE_PAGE_SIZE
  1968. );
  1969. //
  1970. // And return to our caller
  1971. //
  1972. return;
  1973. }
  1974. //
  1975. // Local Support Routine
  1976. //
  1977. VOID
  1978. ExpFreeHandleTable (
  1979. IN PHANDLE_TABLE HandleTable
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. This worker routine tears down and frees the specified handle table.
  1984. Arguments:
  1985. HandleTable - Supplies the handle table being freed
  1986. Return Value:
  1987. None.
  1988. --*/
  1989. {
  1990. PEPROCESS Process;
  1991. ULONG i,j;
  1992. ULONG_PTR CapturedTable = HandleTable->TableCode;
  1993. ULONG TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
  1994. PAGED_CODE();
  1995. //
  1996. // Unmask the level bits
  1997. //
  1998. CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;
  1999. Process = HandleTable->QuotaProcess;
  2000. //
  2001. // We need to free all pages. We have 3 cases, depending on the number
  2002. // of levels
  2003. //
  2004. if (TableLevel == 0) {
  2005. //
  2006. // There is a single level handle table. We'll simply free the buffer
  2007. //
  2008. PHANDLE_TABLE_ENTRY TableLevel1 = (PHANDLE_TABLE_ENTRY)CapturedTable;
  2009. ExpFreeLowLevelTable( Process, TableLevel1 );
  2010. } else if (TableLevel == 1) {
  2011. //
  2012. // We have 2 levels in the handle table
  2013. //
  2014. PHANDLE_TABLE_ENTRY *TableLevel2 = (PHANDLE_TABLE_ENTRY *)CapturedTable;
  2015. for (i = 0; i < MIDLEVEL_COUNT; i++) {
  2016. //
  2017. // loop through the pointers to the low-level tables, and free each one
  2018. //
  2019. if (TableLevel2[i] == NULL) {
  2020. break;
  2021. }
  2022. ExpFreeLowLevelTable( Process, TableLevel2[i] );
  2023. }
  2024. //
  2025. // Free the top level table
  2026. //
  2027. ExpFreeTablePagedPool( Process,
  2028. TableLevel2,
  2029. PAGE_SIZE
  2030. );
  2031. } else {
  2032. //
  2033. // Here we handle the case where we have a 3 level handle table
  2034. //
  2035. PHANDLE_TABLE_ENTRY **TableLevel3 = (PHANDLE_TABLE_ENTRY **)CapturedTable;
  2036. //
  2037. // Iterates through the high-level pointers to mid-table
  2038. //
  2039. for (i = 0; i < HIGHLEVEL_COUNT; i++) {
  2040. if (TableLevel3[i] == NULL) {
  2041. break;
  2042. }
  2043. //
  2044. // Iterate through the mid-level table
  2045. // and free every low-level page
  2046. //
  2047. for (j = 0; j < MIDLEVEL_COUNT; j++) {
  2048. if (TableLevel3[i][j] == NULL) {
  2049. break;
  2050. }
  2051. ExpFreeLowLevelTable( Process, TableLevel3[i][j] );
  2052. }
  2053. ExpFreeTablePagedPool( Process,
  2054. TableLevel3[i],
  2055. PAGE_SIZE
  2056. );
  2057. }
  2058. //
  2059. // Free the top-level array
  2060. //
  2061. ExpFreeTablePagedPool( Process,
  2062. TableLevel3,
  2063. HIGHLEVEL_SIZE
  2064. );
  2065. }
  2066. //
  2067. // Free any debug info if we have any.
  2068. //
  2069. if (HandleTable->DebugInfo != NULL) {
  2070. ExDereferenceHandleDebugInfo (HandleTable, HandleTable->DebugInfo);
  2071. }
  2072. //
  2073. // Finally deallocate the handle table itself
  2074. //
  2075. ExFreePool( HandleTable );
  2076. if (Process != NULL) {
  2077. PsReturnProcessPagedPoolQuota (Process,
  2078. sizeof(HANDLE_TABLE));
  2079. }
  2080. //
  2081. // And return to our caller
  2082. //
  2083. return;
  2084. }
  2085. //
  2086. // Local Support Routine
  2087. //
  2088. PHANDLE_TABLE_ENTRY
  2089. ExpAllocateLowLevelTable (
  2090. IN PHANDLE_TABLE HandleTable,
  2091. IN BOOLEAN DoInit
  2092. )
  2093. /*++
  2094. Routine Description:
  2095. This worker routine allocates a new low level table
  2096. Note: The caller must have already locked the handle table
  2097. Arguments:
  2098. HandleTable - Supplies the handle table being used
  2099. DoInit - If FALSE the caller (duplicate) doesn't need the free list maintained
  2100. Return Value:
  2101. Returns - a pointer to a low-level table if allocation is
  2102. successful otherwise the return value is null.
  2103. --*/
  2104. {
  2105. ULONG k;
  2106. PHANDLE_TABLE_ENTRY NewLowLevel = NULL, HandleEntry;
  2107. ULONG BaseHandle;
  2108. //
  2109. // Allocate the pool for lower level
  2110. //
  2111. NewLowLevel = ExpAllocateTablePagedPoolNoZero( HandleTable->QuotaProcess,
  2112. TABLE_PAGE_SIZE
  2113. );
  2114. if (NewLowLevel == NULL) {
  2115. return NULL;
  2116. }
  2117. //
  2118. // We stamp with EX_ADDITIONAL_INFO_SIGNATURE to recognize in the future this
  2119. // is a special information entry
  2120. //
  2121. HandleEntry = &NewLowLevel[0];
  2122. HandleEntry->NextFreeTableEntry = EX_ADDITIONAL_INFO_SIGNATURE;
  2123. HandleEntry->Value = 0;
  2124. //
  2125. // Initialize the free list within this page if the caller wants this
  2126. //
  2127. if (DoInit) {
  2128. HandleEntry++;
  2129. //
  2130. // Now add the new entries to the free list. To do this we
  2131. // chain the new free entries together. We are guaranteed to
  2132. // have at least one new buffer. The second buffer we need
  2133. // to check for.
  2134. //
  2135. // We reserve the first entry in the table to the structure with
  2136. // additional info
  2137. //
  2138. //
  2139. // Do the guaranteed first buffer
  2140. //
  2141. BaseHandle = HandleTable->NextHandleNeedingPool + 2 * HANDLE_VALUE_INC;
  2142. for (k = BaseHandle; k < BaseHandle + (LOWLEVEL_COUNT - 2) * HANDLE_VALUE_INC; k += HANDLE_VALUE_INC) {
  2143. HandleEntry->NextFreeTableEntry = k;
  2144. HandleEntry->Value = 0;
  2145. HandleEntry++;
  2146. }
  2147. HandleEntry->NextFreeTableEntry = 0;
  2148. HandleEntry->Value = 0;
  2149. }
  2150. return NewLowLevel;
  2151. }
  2152. PHANDLE_TABLE_ENTRY *
  2153. ExpAllocateMidLevelTable (
  2154. IN PHANDLE_TABLE HandleTable,
  2155. IN BOOLEAN DoInit,
  2156. OUT PHANDLE_TABLE_ENTRY *pNewLowLevel
  2157. )
  2158. /*++
  2159. Routine Description:
  2160. This worker routine allocates a mid-level table. This is an array with
  2161. pointers to low-level tables.
  2162. It will allocate also a low-level table and will save it in the first index
  2163. Note: The caller must have already locked the handle table
  2164. Arguments:
  2165. HandleTable - Supplies the handle table being used
  2166. DoInit - If FALSE the caller (duplicate) does not want the free list build
  2167. pNewLowLevel - Returns the new low level taible for later free list chaining
  2168. Return Value:
  2169. Returns a pointer to the new mid-level table allocated
  2170. --*/
  2171. {
  2172. PHANDLE_TABLE_ENTRY *NewMidLevel;
  2173. PHANDLE_TABLE_ENTRY NewLowLevel;
  2174. NewMidLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
  2175. PAGE_SIZE
  2176. );
  2177. if (NewMidLevel == NULL) {
  2178. return NULL;
  2179. }
  2180. //
  2181. // If we need a new mid-level, we'll need a low-level too.
  2182. // We'll create one and if success we'll save it at the first position
  2183. //
  2184. NewLowLevel = ExpAllocateLowLevelTable( HandleTable, DoInit );
  2185. if (NewLowLevel == NULL) {
  2186. ExpFreeTablePagedPool( HandleTable->QuotaProcess,
  2187. NewMidLevel,
  2188. PAGE_SIZE
  2189. );
  2190. return NULL;
  2191. }
  2192. //
  2193. // Set the low-level table at the first index
  2194. //
  2195. NewMidLevel[0] = NewLowLevel;
  2196. *pNewLowLevel = NewLowLevel;
  2197. return NewMidLevel;
  2198. }
  2199. BOOLEAN
  2200. ExpAllocateHandleTableEntrySlow (
  2201. IN PHANDLE_TABLE HandleTable,
  2202. IN BOOLEAN DoInit
  2203. )
  2204. /*++
  2205. Routine Description:
  2206. This worker routine allocates a new handle table entry for the specified
  2207. handle table.
  2208. Note: The caller must have already locked the handle table
  2209. Arguments:
  2210. HandleTable - Supplies the handle table being used
  2211. DoInit - If FALSE then the caller (duplicate) doesn't need the free list built
  2212. Return Value:
  2213. BOOLEAN - TRUE, Retry the fast allocation path, FALSE, We failed to allocate memory
  2214. --*/
  2215. {
  2216. ULONG i,j;
  2217. PHANDLE_TABLE_ENTRY NewLowLevel;
  2218. PHANDLE_TABLE_ENTRY *NewMidLevel;
  2219. PHANDLE_TABLE_ENTRY **NewHighLevel;
  2220. ULONG NewFree, OldFree;
  2221. ULONG OldIndex;
  2222. PVOID OldValue;
  2223. ULONG_PTR CapturedTable = HandleTable->TableCode;
  2224. ULONG TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
  2225. PAGED_CODE();
  2226. //
  2227. // Initializing NewLowLevel is not needed for
  2228. // correctness but without it the compiler cannot compile this code
  2229. // W4 to check for use of uninitialized variables.
  2230. //
  2231. NewLowLevel = NULL;
  2232. CapturedTable = CapturedTable & ~LEVEL_CODE_MASK;
  2233. if ( TableLevel == 0 ) {
  2234. //
  2235. // We have a single level. We need to ad a mid-layer
  2236. // to the process handle table
  2237. //
  2238. NewMidLevel = ExpAllocateMidLevelTable( HandleTable, DoInit, &NewLowLevel );
  2239. if (NewMidLevel == NULL) {
  2240. return FALSE;
  2241. }
  2242. //
  2243. // Since ExpAllocateMidLevelTable initialize the
  2244. // first position with a new table, we need to move it in
  2245. // the second position, and store in the first position the current one
  2246. //
  2247. NewMidLevel[1] = NewMidLevel[0];
  2248. NewMidLevel[0] = (PHANDLE_TABLE_ENTRY)CapturedTable;
  2249. //
  2250. // Encode the current level and set it to the handle table process
  2251. //
  2252. CapturedTable = ((ULONG_PTR)NewMidLevel) | 1;
  2253. OldValue = InterlockedExchangePointer( (PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable );
  2254. } else if (TableLevel == 1) {
  2255. //
  2256. // We have a 2 levels handle table
  2257. //
  2258. PHANDLE_TABLE_ENTRY *TableLevel2 = (PHANDLE_TABLE_ENTRY *)CapturedTable;
  2259. //
  2260. // Test whether the index we need to create is still in the
  2261. // range for a 2 layers table
  2262. //
  2263. i = HandleTable->NextHandleNeedingPool / (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
  2264. if (i < MIDLEVEL_COUNT) {
  2265. //
  2266. // We just need to allocate a new low-level
  2267. // table
  2268. //
  2269. NewLowLevel = ExpAllocateLowLevelTable( HandleTable, DoInit );
  2270. if (NewLowLevel == NULL) {
  2271. return FALSE;
  2272. }
  2273. //
  2274. // Set the new one to the table, at appropriate position
  2275. //
  2276. OldValue = InterlockedExchangePointer( (PVOID *) (&TableLevel2[i]), NewLowLevel );
  2277. EXASSERT (OldValue == NULL);
  2278. } else {
  2279. //
  2280. // We exhausted the 2 level domain. We need to insert a new one
  2281. //
  2282. NewHighLevel = ExpAllocateTablePagedPool( HandleTable->QuotaProcess,
  2283. HIGHLEVEL_SIZE
  2284. );
  2285. if (NewHighLevel == NULL) {
  2286. return FALSE;
  2287. }
  2288. NewMidLevel = ExpAllocateMidLevelTable( HandleTable, DoInit, &NewLowLevel );
  2289. if (NewMidLevel == NULL) {
  2290. ExpFreeTablePagedPool( HandleTable->QuotaProcess,
  2291. NewHighLevel,
  2292. HIGHLEVEL_SIZE
  2293. );
  2294. return FALSE;
  2295. }
  2296. //
  2297. // Initialize the first index with the previous mid-level layer
  2298. //
  2299. NewHighLevel[0] = (PHANDLE_TABLE_ENTRY*)CapturedTable;
  2300. NewHighLevel[1] = NewMidLevel;
  2301. //
  2302. // Encode the level into the table pointer
  2303. //
  2304. CapturedTable = ((ULONG_PTR)NewHighLevel) | 2;
  2305. //
  2306. // Change the handle table pointer with this one
  2307. //
  2308. OldValue = InterlockedExchangePointer( (PVOID *)&HandleTable->TableCode, (PVOID)CapturedTable );
  2309. }
  2310. } else if (TableLevel == 2) {
  2311. //
  2312. // we have already a table with 3 levels
  2313. //
  2314. ULONG RemainingIndex;
  2315. PHANDLE_TABLE_ENTRY **TableLevel3 = (PHANDLE_TABLE_ENTRY **)CapturedTable;
  2316. i = HandleTable->NextHandleNeedingPool / (MIDLEVEL_THRESHOLD * HANDLE_VALUE_INC);
  2317. //
  2318. // Check whether we exhausted all possible indexes.
  2319. //
  2320. if (i >= HIGHLEVEL_COUNT) {
  2321. return FALSE;
  2322. }
  2323. if (TableLevel3[i] == NULL) {
  2324. //
  2325. // The new available handle points to a free mid-level entry
  2326. // We need then to allocate a new one and save it in that position
  2327. //
  2328. NewMidLevel = ExpAllocateMidLevelTable( HandleTable, DoInit, &NewLowLevel );
  2329. if (NewMidLevel == NULL) {
  2330. return FALSE;
  2331. }
  2332. OldValue = InterlockedExchangePointer( (PVOID *) &(TableLevel3[i]), NewMidLevel );
  2333. EXASSERT (OldValue == NULL);
  2334. } else {
  2335. //
  2336. // We have already a mid-level table. We just need to add a new low-level one
  2337. // at the end
  2338. //
  2339. RemainingIndex = (HandleTable->NextHandleNeedingPool / HANDLE_VALUE_INC) -
  2340. i * MIDLEVEL_THRESHOLD;
  2341. j = RemainingIndex / LOWLEVEL_COUNT;
  2342. NewLowLevel = ExpAllocateLowLevelTable( HandleTable, DoInit );
  2343. if (NewLowLevel == NULL) {
  2344. return FALSE;
  2345. }
  2346. OldValue = InterlockedExchangePointer( (PVOID *)(&TableLevel3[i][j]) , NewLowLevel );
  2347. EXASSERT (OldValue == NULL);
  2348. }
  2349. }
  2350. //
  2351. // This must be done after the table pointers so that new created handles
  2352. // are valid before being freed.
  2353. //
  2354. OldIndex = InterlockedExchangeAdd ((PLONG) &HandleTable->NextHandleNeedingPool,
  2355. LOWLEVEL_COUNT * HANDLE_VALUE_INC);
  2356. if (DoInit) {
  2357. //
  2358. // Generate a new sequence number since this is a push
  2359. //
  2360. OldIndex += HANDLE_VALUE_INC + GetNextSeq();
  2361. //
  2362. // Now free the handles. These are all ready to be accepted by the lookup logic now.
  2363. //
  2364. while (1) {
  2365. OldFree = HandleTable->FirstFree;
  2366. NewLowLevel[LOWLEVEL_COUNT - 1].NextFreeTableEntry = OldFree;
  2367. //
  2368. // These are new entries that have never existed before. We can't have an A-B-A problem
  2369. // with these so we don't need to take any locks
  2370. //
  2371. NewFree = InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
  2372. OldIndex,
  2373. OldFree);
  2374. if (NewFree == OldFree) {
  2375. break;
  2376. }
  2377. }
  2378. }
  2379. return TRUE;
  2380. }
  2381. VOID
  2382. ExSetHandleTableStrictFIFO (
  2383. IN PHANDLE_TABLE HandleTable
  2384. )
  2385. /*++
  2386. Routine Description:
  2387. This routine marks a handle table so that handle allocation is done in
  2388. a strict FIFO order.
  2389. Arguments:
  2390. HandleTable - Supplies the handle table being changed to FIFO
  2391. Return Value:
  2392. None.
  2393. --*/
  2394. {
  2395. HandleTable->StrictFIFO = TRUE;
  2396. }
  2397. //
  2398. // Local Support Routine
  2399. //
  2400. //
  2401. // The following is a global variable only present in the checked builds
  2402. // to help catch apps that reuse handle values after they're closed.
  2403. //
  2404. #if DBG
  2405. BOOLEAN ExReuseHandles = 1;
  2406. #endif //DBG
  2407. VOID
  2408. ExpFreeHandleTableEntry (
  2409. IN PHANDLE_TABLE HandleTable,
  2410. IN EXHANDLE Handle,
  2411. IN PHANDLE_TABLE_ENTRY HandleTableEntry
  2412. )
  2413. /*++
  2414. Routine Description:
  2415. This worker routine returns the specified handle table entry to the free
  2416. list for the handle table.
  2417. Note: The caller must have already locked the handle table
  2418. Arguments:
  2419. HandleTable - Supplies the parent handle table being modified
  2420. Handle - Supplies the handle of the entry being freed
  2421. HandleTableEntry - Supplies the table entry being freed
  2422. Return Value:
  2423. None.
  2424. --*/
  2425. {
  2426. PHANDLE_TABLE_ENTRY_INFO EntryInfo;
  2427. ULONG OldFree, NewFree, *Free;
  2428. PKTHREAD CurrentThread;
  2429. ULONG Idx;
  2430. ULONG SeqInc;
  2431. PAGED_CODE();
  2432. EXASSERT (HandleTableEntry->Object == NULL);
  2433. EXASSERT (HandleTableEntry == ExpLookupHandleTableEntry (HandleTable, Handle));
  2434. //
  2435. // Clear the AuditMask flags if these are present into the table
  2436. //
  2437. EntryInfo = ExGetHandleInfo(HandleTable, Handle.GenericHandleOverlay, TRUE);
  2438. if (EntryInfo) {
  2439. EntryInfo->AuditMask = 0;
  2440. }
  2441. //
  2442. // A free is simply a push onto the free table entry stack, or in the
  2443. // debug case we'll sometimes just float the entry to catch apps who
  2444. // reuse a recycled handle value.
  2445. //
  2446. InterlockedDecrement (&HandleTable->HandleCount);
  2447. CurrentThread = KeGetCurrentThread ();
  2448. NewFree = (ULONG) Handle.Value & ~(HANDLE_VALUE_INC - 1);
  2449. #if DBG
  2450. if (ExReuseHandles) {
  2451. #endif //DBG
  2452. if (!HandleTable->StrictFIFO) {
  2453. //
  2454. // We are pushing potentialy old entries onto the free list.
  2455. // Prevent the A-B-A problem by shifting to an alternate list
  2456. // read this element has the list head out of the loop.
  2457. //
  2458. Idx = (NewFree>>2) % HANDLE_TABLE_LOCKS;
  2459. if (ExTryAcquireReleasePushLockExclusive (&HandleTable->HandleTableLock[Idx])) {
  2460. SeqInc = GetNextSeq();
  2461. Free = &HandleTable->FirstFree;
  2462. } else {
  2463. SeqInc = 0;
  2464. Free = &HandleTable->LastFree;
  2465. }
  2466. } else {
  2467. SeqInc = 0;
  2468. Free = &HandleTable->LastFree;
  2469. }
  2470. while (1) {
  2471. OldFree = *Free;
  2472. HandleTableEntry->NextFreeTableEntry = OldFree;
  2473. if ((ULONG)InterlockedCompareExchange ((PLONG)Free,
  2474. NewFree + SeqInc,
  2475. OldFree) == OldFree) {
  2476. EXASSERT ((OldFree & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool);
  2477. break;
  2478. }
  2479. }
  2480. #if DBG
  2481. } else {
  2482. HandleTableEntry->NextFreeTableEntry = 0;
  2483. }
  2484. #endif //DBG
  2485. return;
  2486. }
  2487. //
  2488. // Local Support Routine
  2489. //
  2490. PHANDLE_TABLE_ENTRY
  2491. ExpLookupHandleTableEntry (
  2492. IN PHANDLE_TABLE HandleTable,
  2493. IN EXHANDLE tHandle
  2494. )
  2495. /*++
  2496. Routine Description:
  2497. This routine looks up and returns the table entry for the
  2498. specified handle value.
  2499. Arguments:
  2500. HandleTable - Supplies the handle table being queried
  2501. tHandle - Supplies the handle value being queried
  2502. Return Value:
  2503. Returns a pointer to the corresponding table entry for the input
  2504. handle. Or NULL if the handle value is invalid (i.e., too large
  2505. for the tables current allocation.
  2506. --*/
  2507. {
  2508. ULONG_PTR i,j,k;
  2509. ULONG_PTR CapturedTable;
  2510. ULONG TableLevel;
  2511. PHANDLE_TABLE_ENTRY Entry = NULL;
  2512. EXHANDLE Handle;
  2513. PUCHAR TableLevel1;
  2514. PUCHAR TableLevel2;
  2515. PUCHAR TableLevel3;
  2516. ULONG_PTR MaxHandle;
  2517. PAGED_CODE();
  2518. //
  2519. // Extract the handle index
  2520. //
  2521. Handle = tHandle;
  2522. Handle.TagBits = 0;
  2523. MaxHandle = *(volatile ULONG *) &HandleTable->NextHandleNeedingPool;
  2524. //
  2525. // See if this can be a valid handle given the table levels.
  2526. //
  2527. if (Handle.Value >= MaxHandle) {
  2528. return NULL;
  2529. }
  2530. //
  2531. // Now fetch the table address and level bits. We must preserve the
  2532. // ordering here.
  2533. //
  2534. CapturedTable = *(volatile ULONG_PTR *) &HandleTable->TableCode;
  2535. //
  2536. // we need to capture the current table. This routine is lock free
  2537. // so another thread may change the table at HandleTable->TableCode
  2538. //
  2539. TableLevel = (ULONG)(CapturedTable & LEVEL_CODE_MASK);
  2540. CapturedTable = CapturedTable - TableLevel;
  2541. //
  2542. // The lookup code depends on number of levels we have
  2543. //
  2544. switch (TableLevel) {
  2545. case 0:
  2546. //
  2547. // We have a simple index into the array, for a single level
  2548. // handle table
  2549. //
  2550. TableLevel1 = (PUCHAR) CapturedTable;
  2551. //
  2552. // The index for this level is already scaled by a factor of 4. Take advantage of this
  2553. //
  2554. Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[Handle.Value *
  2555. (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
  2556. break;
  2557. case 1:
  2558. //
  2559. // we have a 2 level handle table. We need to get the upper index
  2560. // and lower index into the array
  2561. //
  2562. TableLevel2 = (PUCHAR) CapturedTable;
  2563. i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
  2564. Handle.Value -= i;
  2565. j = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));
  2566. TableLevel1 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
  2567. Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
  2568. break;
  2569. case 2:
  2570. //
  2571. // We have here a three level handle table.
  2572. //
  2573. TableLevel3 = (PUCHAR) CapturedTable;
  2574. i = Handle.Value % (LOWLEVEL_COUNT * HANDLE_VALUE_INC);
  2575. Handle.Value -= i;
  2576. k = Handle.Value / ((LOWLEVEL_COUNT * HANDLE_VALUE_INC) / sizeof (PHANDLE_TABLE_ENTRY));
  2577. j = k % (MIDLEVEL_COUNT * sizeof (PHANDLE_TABLE_ENTRY));
  2578. k -= j;
  2579. k /= MIDLEVEL_COUNT;
  2580. TableLevel2 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel3[k];
  2581. TableLevel1 = (PUCHAR) *(PHANDLE_TABLE_ENTRY *) &TableLevel2[j];
  2582. Entry = (PHANDLE_TABLE_ENTRY) &TableLevel1[i * (sizeof (HANDLE_TABLE_ENTRY) / HANDLE_VALUE_INC)];
  2583. break;
  2584. default :
  2585. _assume (0);
  2586. }
  2587. return Entry;
  2588. }
  2589. NTKERNELAPI
  2590. NTSTATUS
  2591. ExSetHandleInfo (
  2592. IN PHANDLE_TABLE HandleTable,
  2593. IN HANDLE Handle,
  2594. IN PHANDLE_TABLE_ENTRY_INFO EntryInfo,
  2595. IN BOOLEAN EntryLocked
  2596. )
  2597. /*++
  2598. Routine Description:
  2599. The routine set the entry info for the specified handle table
  2600. Note: the handle entry must be locked when this function is called
  2601. Arguments:
  2602. HandleTable - Supplies the handle table being queried
  2603. Handle - Supplies the handle value being queried
  2604. Return Value:
  2605. --*/
  2606. {
  2607. PKTHREAD CurrentThread;
  2608. PHANDLE_TABLE_ENTRY InfoStructure;
  2609. EXHANDLE ExHandle;
  2610. NTSTATUS Status;
  2611. PHANDLE_TABLE_ENTRY TableEntry;
  2612. PHANDLE_TABLE_ENTRY_INFO InfoTable;
  2613. Status = STATUS_UNSUCCESSFUL;
  2614. TableEntry = NULL;
  2615. CurrentThread = NULL;
  2616. ExHandle.GenericHandleOverlay = Handle;
  2617. ExHandle.Index &= ~(LOWLEVEL_COUNT - 1);
  2618. if (!EntryLocked) {
  2619. CurrentThread = KeGetCurrentThread ();
  2620. KeEnterCriticalRegionThread (CurrentThread);
  2621. TableEntry = ExMapHandleToPointer(HandleTable, Handle);
  2622. if (TableEntry == NULL) {
  2623. KeLeaveCriticalRegionThread (CurrentThread);
  2624. return STATUS_UNSUCCESSFUL;
  2625. }
  2626. }
  2627. //
  2628. // The info structure is at the first position in each low-level table
  2629. //
  2630. InfoStructure = ExpLookupHandleTableEntry( HandleTable,
  2631. ExHandle
  2632. );
  2633. if (InfoStructure == NULL || InfoStructure->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE) {
  2634. if ( TableEntry ) {
  2635. ExUnlockHandleTableEntry( HandleTable, TableEntry );
  2636. KeLeaveCriticalRegionThread (CurrentThread);
  2637. }
  2638. return STATUS_UNSUCCESSFUL;
  2639. }
  2640. //
  2641. // Check whether we need to allocate a new table
  2642. //
  2643. InfoTable = InfoStructure->InfoTable;
  2644. if (InfoTable == NULL) {
  2645. //
  2646. // Nobody allocated the Infotable so far.
  2647. // We'll do it right now
  2648. //
  2649. InfoTable = ExpAllocateTablePagedPool (HandleTable->QuotaProcess,
  2650. LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO));
  2651. if (InfoTable) {
  2652. //
  2653. // Update the number of pages for extra info. If somebody beat us to it then free the
  2654. // new table
  2655. //
  2656. if (InterlockedCompareExchangePointer (&InfoStructure->InfoTable,
  2657. InfoTable,
  2658. NULL) == NULL) {
  2659. InterlockedIncrement(&HandleTable->ExtraInfoPages);
  2660. } else {
  2661. ExpFreeTablePagedPool (HandleTable->QuotaProcess,
  2662. InfoTable,
  2663. LOWLEVEL_COUNT * sizeof(HANDLE_TABLE_ENTRY_INFO));
  2664. InfoTable = InfoStructure->InfoTable;
  2665. }
  2666. }
  2667. }
  2668. if (InfoTable != NULL) {
  2669. //
  2670. // Calculate the index and copy the structure
  2671. //
  2672. ExHandle.GenericHandleOverlay = Handle;
  2673. InfoTable[ExHandle.Index % LOWLEVEL_COUNT] = *EntryInfo;
  2674. Status = STATUS_SUCCESS;
  2675. }
  2676. if ( TableEntry ) {
  2677. ExUnlockHandleTableEntry( HandleTable, TableEntry );
  2678. KeLeaveCriticalRegionThread (CurrentThread);
  2679. }
  2680. return Status;
  2681. }
  2682. NTKERNELAPI
  2683. PHANDLE_TABLE_ENTRY_INFO
  2684. ExpGetHandleInfo (
  2685. IN PHANDLE_TABLE HandleTable,
  2686. IN HANDLE Handle,
  2687. IN BOOLEAN EntryLocked
  2688. )
  2689. /*++
  2690. Routine Description:
  2691. The routine reads the entry info for the specified handle table
  2692. Note: the handle entry must be locked when this function is called
  2693. Arguments:
  2694. HandleTable - Supplies the handle table being queried
  2695. Handle - Supplies the handle value being queried
  2696. Return Value:
  2697. --*/
  2698. {
  2699. PHANDLE_TABLE_ENTRY InfoStructure;
  2700. EXHANDLE ExHandle;
  2701. PHANDLE_TABLE_ENTRY TableEntry = NULL;
  2702. ExHandle.GenericHandleOverlay = Handle;
  2703. ExHandle.Index &= ~(LOWLEVEL_COUNT - 1);
  2704. if (!EntryLocked) {
  2705. TableEntry = ExMapHandleToPointer(HandleTable, Handle);
  2706. if (TableEntry == NULL) {
  2707. return NULL;
  2708. }
  2709. }
  2710. //
  2711. // The info structure is at the first position in each low-level table
  2712. //
  2713. InfoStructure = ExpLookupHandleTableEntry( HandleTable,
  2714. ExHandle
  2715. );
  2716. if (InfoStructure == NULL || InfoStructure->NextFreeTableEntry != EX_ADDITIONAL_INFO_SIGNATURE ||
  2717. InfoStructure->InfoTable == NULL) {
  2718. if ( TableEntry ) {
  2719. ExUnlockHandleTableEntry( HandleTable, TableEntry );
  2720. }
  2721. return NULL;
  2722. }
  2723. //
  2724. // Return a pointer to the info structure
  2725. //
  2726. ExHandle.GenericHandleOverlay = Handle;
  2727. return &(InfoStructure->InfoTable[ExHandle.Index % LOWLEVEL_COUNT]);
  2728. }
  2729. #if DBG
  2730. ULONG g_ulExpUpdateDebugInfoDebugLevel = 0;
  2731. #endif
  2732. void ExpUpdateDebugInfo(
  2733. PHANDLE_TABLE HandleTable,
  2734. PETHREAD CurrentThread,
  2735. HANDLE Handle,
  2736. ULONG Type)
  2737. {
  2738. BOOLEAN LockAcquired = FALSE;
  2739. PHANDLE_TRACE_DEBUG_INFO DebugInfo;
  2740. DebugInfo = ExReferenceHandleDebugInfo (HandleTable);
  2741. if (DebugInfo == NULL) {
  2742. return;
  2743. }
  2744. #if DBG
  2745. if (g_ulExpUpdateDebugInfoDebugLevel > 10)
  2746. {
  2747. DbgPrint ("ExpUpdateDebugInfo() BitMaskFlags=0x%x, CurrentStackIndex=%d, Handle=0x%p, Type=%d \n",
  2748. DebugInfo->BitMaskFlags,
  2749. DebugInfo->CurrentStackIndex,
  2750. Handle,
  2751. Type);
  2752. }
  2753. #endif
  2754. if (DebugInfo->BitMaskFlags & (HANDLE_TRACE_DEBUG_INFO_CLEAN_DEBUG_INFO | HANDLE_TRACE_DEBUG_INFO_COMPACT_CLOSE_HANDLE)) {
  2755. //
  2756. // we wish to presenve lock-free behavior in the non-compation path
  2757. // so we lock only in this path
  2758. //
  2759. ExAcquireFastMutex(&DebugInfo->CloseCompactionLock);
  2760. LockAcquired = TRUE;
  2761. }
  2762. if (DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_CLEAN_DEBUG_INFO) {
  2763. //
  2764. // clean debug info, but not the fast mutex!
  2765. //
  2766. ASSERT(LockAcquired);
  2767. DebugInfo->BitMaskFlags &= ~HANDLE_TRACE_DEBUG_INFO_CLEAN_DEBUG_INFO;
  2768. DebugInfo->BitMaskFlags &= ~HANDLE_TRACE_DEBUG_INFO_WAS_WRAPPED_AROUND;
  2769. DebugInfo->BitMaskFlags |= HANDLE_TRACE_DEBUG_INFO_WAS_SOMETIME_CLEANED;
  2770. DebugInfo->CurrentStackIndex = 0;
  2771. RtlZeroMemory (DebugInfo->TraceDb,
  2772. sizeof (*DebugInfo) +
  2773. DebugInfo->TableSize * sizeof (DebugInfo->TraceDb[0]) -
  2774. sizeof (DebugInfo->TraceDb));
  2775. }
  2776. if (
  2777. (DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_COMPACT_CLOSE_HANDLE) &&
  2778. (Type == HANDLE_TRACE_DB_CLOSE)
  2779. ){
  2780. //
  2781. // i am asuming that either:
  2782. // 1) this flag was set from the beginning, so there are no close items.
  2783. // 2) this flag was set via KD, in which case HANDLE_TRACE_DEBUG_INFO_CLEAN_DEBUG_INFO
  2784. // must have been set also, so again there are no close items
  2785. //
  2786. ULONG uiMaxNumOfItemsInTraceDb;
  2787. ULONG uiNextItem;
  2788. ASSERT(LockAcquired);
  2789. //
  2790. // look for the matching open item, remove them from the list, and compact the list
  2791. //
  2792. uiMaxNumOfItemsInTraceDb = (DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_WAS_WRAPPED_AROUND) ? DebugInfo->TableSize : DebugInfo->CurrentStackIndex ;
  2793. for (uiNextItem = 1; uiNextItem <= uiMaxNumOfItemsInTraceDb; uiNextItem++) {
  2794. //
  2795. // if HANDLE_TRACE_DEBUG_INFO_COMPACT_CLOSE_HANDLE is on
  2796. // there could be no HANDLE_TRACE_DB_CLOSE items
  2797. // This ASSERT can fire if the HANDLE_TRACE_DEBUG_INFO_COMPACT_CLOSE_HANDLE flag
  2798. // is set dynamically, so another thread was not using the locks to add
  2799. // items to the list, and could have added a HANDLE_TRACE_DB_CLOSE item.
  2800. //
  2801. ASSERT(DebugInfo->TraceDb[uiNextItem%DebugInfo->TableSize].Type != HANDLE_TRACE_DB_CLOSE);
  2802. if (
  2803. (DebugInfo->TraceDb[uiNextItem%DebugInfo->TableSize].Type == HANDLE_TRACE_DB_OPEN) &&
  2804. (DebugInfo->TraceDb[uiNextItem%DebugInfo->TableSize].Handle == Handle)
  2805. ) {
  2806. //
  2807. // found the matching open, compact the list
  2808. //
  2809. ULONG IndexToMoveBack;
  2810. DebugInfo->CurrentStackIndex--;
  2811. IndexToMoveBack = DebugInfo->CurrentStackIndex % DebugInfo->TableSize;
  2812. if (0 != IndexToMoveBack)
  2813. {
  2814. DebugInfo->TraceDb[uiNextItem%DebugInfo->TableSize] = DebugInfo->TraceDb[IndexToMoveBack];
  2815. }
  2816. else
  2817. {
  2818. //
  2819. // the list is already empty
  2820. //
  2821. }
  2822. break;
  2823. }
  2824. }
  2825. if (!(uiNextItem <= uiMaxNumOfItemsInTraceDb)) {
  2826. //
  2827. // a matching open was not found.
  2828. // this must mean that we wrapped around, or cleaned the list sometime after
  2829. // it was created
  2830. // or that a duplicated handle was created before handle tracking was initiated
  2831. // so we cannot ASSERT for that
  2832. //
  2833. /*
  2834. ASSERT(
  2835. (DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_WAS_SOMETIME_CLEANED) ||
  2836. (DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_WAS_WRAPPED_AROUND)
  2837. );
  2838. */
  2839. //
  2840. // just ignore this HANDLE_TRACE_DB_CLOSE
  2841. //
  2842. }
  2843. }
  2844. else
  2845. {
  2846. PHANDLE_TRACE_DB_ENTRY DebugEntry;
  2847. ULONG Index = ((ULONG) InterlockedIncrement ((PLONG)&DebugInfo->CurrentStackIndex))
  2848. % DebugInfo->TableSize;
  2849. ASSERT((Type != HANDLE_TRACE_DB_CLOSE) || (!(DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_COMPACT_CLOSE_HANDLE)));
  2850. if (0 == Index) {
  2851. //
  2852. // this is a wraparound of the db, mark it as such, and if there's
  2853. // a debugger attached break into it
  2854. //
  2855. DebugInfo->BitMaskFlags |= HANDLE_TRACE_DEBUG_INFO_WAS_WRAPPED_AROUND;
  2856. if(DebugInfo->BitMaskFlags & HANDLE_TRACE_DEBUG_INFO_BREAK_ON_WRAP_AROUND)
  2857. {
  2858. __try {
  2859. DbgBreakPoint();
  2860. }
  2861. __except(1) {
  2862. NOTHING;
  2863. }
  2864. }
  2865. }
  2866. DebugEntry = &DebugInfo->TraceDb[Index];
  2867. DebugEntry->ClientId = CurrentThread->Cid;
  2868. DebugEntry->Handle = Handle;
  2869. DebugEntry->Type = Type;
  2870. Index = RtlWalkFrameChain (DebugEntry->StackTrace, HANDLE_TRACE_DB_STACK_SIZE, 0);
  2871. RtlWalkFrameChain (&DebugEntry->StackTrace[Index], HANDLE_TRACE_DB_STACK_SIZE - Index, 1);
  2872. }
  2873. if (LockAcquired)
  2874. {
  2875. ExReleaseFastMutex(&DebugInfo->CloseCompactionLock);
  2876. }
  2877. ExDereferenceHandleDebugInfo (HandleTable, DebugInfo);
  2878. }
  2879. VOID
  2880. ExHandleTest (
  2881. )
  2882. {
  2883. PHANDLE_TABLE HandleTable;
  2884. ULONG i, j, k;
  2885. #define MAX_ALLOCS 20
  2886. PHANDLE_TABLE_ENTRY HandleEntryArray[MAX_ALLOCS];
  2887. EXHANDLE Handle[MAX_ALLOCS];
  2888. LARGE_INTEGER CurrentTime;
  2889. HandleTable = PsGetCurrentProcess ()->ObjectTable;
  2890. HandleTable->StrictFIFO = 0;
  2891. k = 0;
  2892. for (i = 0; i < 100000; i++) {
  2893. KeQuerySystemTime (&CurrentTime);
  2894. for (j = 0; j < MAX_ALLOCS; j++) {
  2895. //
  2896. // Clears Handle.Index and Handle.TagBits
  2897. //
  2898. Handle[j].GenericHandleOverlay = NULL;
  2899. //
  2900. // Allocate a new handle table entry, and get the handle value
  2901. //
  2902. HandleEntryArray[j] = ExpAllocateHandleTableEntry (HandleTable,
  2903. &Handle[j]);
  2904. if (HandleEntryArray[j] != NULL) {
  2905. HandleEntryArray[j]->NextFreeTableEntry = 0x88888888;
  2906. }
  2907. }
  2908. for (j = MAX_ALLOCS; j > 0; j--) {
  2909. k = k + CurrentTime.LowPart;
  2910. k = k % j;
  2911. CurrentTime.QuadPart >>= 3;
  2912. if (HandleEntryArray[k] != NULL) {
  2913. //
  2914. // Free the entry
  2915. //
  2916. ExpFreeHandleTableEntry (HandleTable,
  2917. Handle[k],
  2918. HandleEntryArray[k]);
  2919. HandleEntryArray[k] = HandleEntryArray[j-1];
  2920. HandleEntryArray[j-1] = NULL;
  2921. Handle[k] = Handle[j-1];
  2922. Handle[j-1].GenericHandleOverlay = NULL;
  2923. }
  2924. }
  2925. }
  2926. }