Source code of Windows XP (NT5)
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.

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