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.

6836 lines
175 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. heapdll.c
  5. Abstract:
  6. This module implements the user mode only portions of the heap allocator.
  7. Author:
  8. Steve Wood (stevewo) 20-Sep-1994
  9. Revision History:
  10. --*/
  11. #include <ntos.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include "ntrtlp.h"
  15. #include "heap.h"
  16. #include "heappriv.h"
  17. #include "NtdllTrc.h"
  18. #include "wmiumkm.h"
  19. #ifdef NTHEAP_ENABLED
  20. #include "heapp.h"
  21. #endif // NTHEAP_ENABLED
  22. //
  23. // This structure is used by RtlUsageHeap to keep track of heap usage
  24. // between calls. This package typecasts an extra reserved buffer passed
  25. // in by the user to hold this information
  26. //
  27. typedef struct _RTL_HEAP_USAGE_INTERNAL {
  28. PVOID Base;
  29. SIZE_T ReservedSize;
  30. SIZE_T CommittedSize;
  31. PRTL_HEAP_USAGE_ENTRY FreeList;
  32. PRTL_HEAP_USAGE_ENTRY LargeEntriesSentinal;
  33. ULONG Reserved;
  34. } RTL_HEAP_USAGE_INTERNAL, *PRTL_HEAP_USAGE_INTERNAL;
  35. //
  36. // Note that the following variables are specific to each process
  37. //
  38. //
  39. // This is a lock used to protect access the this processes heap list
  40. //
  41. HEAP_LOCK RtlpProcessHeapsListLock;
  42. //
  43. // This is a specific list of heaps initialized and used by the process
  44. //
  45. #define RTLP_STATIC_HEAP_LIST_SIZE 16
  46. PHEAP RtlpProcessHeapsListBuffer[ RTLP_STATIC_HEAP_LIST_SIZE ];
  47. //
  48. // This variable stores a pointer to the heap used to storage global heap
  49. // tags
  50. //
  51. PHEAP RtlpGlobalTagHeap = NULL;
  52. //
  53. // This varible is used by the process as work space to build up names for
  54. // pseudo tags
  55. //
  56. static WCHAR RtlpPseudoTagNameBuffer[ 24 ];
  57. ULONG RtlpLFHInitialized = 0;
  58. BOOLEAN
  59. RtlpGrowBlockInPlace (
  60. IN PHEAP Heap,
  61. IN ULONG Flags,
  62. IN PHEAP_ENTRY BusyBlock,
  63. IN SIZE_T Size,
  64. IN SIZE_T AllocationIndex
  65. );
  66. PVOID
  67. RtlDebugReAllocateHeap (
  68. IN PVOID HeapHandle,
  69. IN ULONG Flags,
  70. IN PVOID BaseAddress,
  71. IN SIZE_T Size
  72. );
  73. BOOLEAN
  74. RtlDebugGetUserInfoHeap (
  75. IN PVOID HeapHandle,
  76. IN ULONG Flags,
  77. IN PVOID BaseAddress,
  78. OUT PVOID *UserValue OPTIONAL,
  79. OUT PULONG UserFlags OPTIONAL
  80. );
  81. BOOLEAN
  82. RtlDebugSetUserValueHeap (
  83. IN PVOID HeapHandle,
  84. IN ULONG Flags,
  85. IN PVOID BaseAddress,
  86. IN PVOID UserValue
  87. );
  88. BOOLEAN
  89. RtlDebugSetUserFlagsHeap (
  90. IN PVOID HeapHandle,
  91. IN ULONG Flags,
  92. IN PVOID BaseAddress,
  93. IN ULONG UserFlagsReset,
  94. IN ULONG UserFlagsSet
  95. );
  96. SIZE_T
  97. RtlDebugCompactHeap (
  98. IN PVOID HeapHandle,
  99. IN ULONG Flags
  100. );
  101. NTSTATUS
  102. RtlDebugCreateTagHeap (
  103. IN PVOID HeapHandle,
  104. IN ULONG Flags,
  105. IN PWSTR TagPrefix OPTIONAL,
  106. IN PWSTR TagNames
  107. );
  108. PWSTR
  109. RtlDebugQueryTagHeap (
  110. IN PVOID HeapHandle,
  111. IN ULONG Flags,
  112. IN USHORT TagIndex,
  113. IN BOOLEAN ResetCounters,
  114. OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
  115. );
  116. NTSTATUS
  117. RtlDebugUsageHeap (
  118. IN PVOID HeapHandle,
  119. IN ULONG Flags,
  120. IN OUT PRTL_HEAP_USAGE Usage
  121. );
  122. BOOLEAN
  123. RtlDebugWalkHeap (
  124. IN PVOID HeapHandle,
  125. IN OUT PRTL_HEAP_WALK_ENTRY Entry
  126. );
  127. PHEAP_TAG_ENTRY
  128. RtlpAllocateTags (
  129. PHEAP Heap,
  130. ULONG NumberOfTags
  131. );
  132. PRTL_HEAP_USAGE_ENTRY
  133. RtlpFreeHeapUsageEntry (
  134. PRTL_HEAP_USAGE_INTERNAL Buffer,
  135. PRTL_HEAP_USAGE_ENTRY p
  136. );
  137. NTSTATUS
  138. RtlpAllocateHeapUsageEntry (
  139. PRTL_HEAP_USAGE_INTERNAL Buffer,
  140. PRTL_HEAP_USAGE_ENTRY *pp
  141. );
  142. //
  143. // Declared in ntrtl.h
  144. //
  145. NTSTATUS
  146. RtlInitializeHeapManager(
  147. VOID
  148. )
  149. /*++
  150. Routine Description:
  151. This routine is used to initialize the heap manager for the current process
  152. Arguments:
  153. None.
  154. Return Value:
  155. None.
  156. --*/
  157. {
  158. PPEB Peb = NtCurrentPeb();
  159. #if DBG
  160. //
  161. // Sanity check the sizes of the header entry structures
  162. //
  163. if (sizeof( HEAP_ENTRY ) != sizeof( HEAP_ENTRY_EXTRA )) {
  164. HeapDebugPrint(( "Heap header and extra header sizes disagree\n" ));
  165. HeapDebugBreak( NULL );
  166. }
  167. if (sizeof( HEAP_ENTRY ) != CHECK_HEAP_TAIL_SIZE) {
  168. HeapDebugPrint(( "Heap header and tail fill sizes disagree\n" ));
  169. HeapDebugBreak( NULL );
  170. }
  171. if (sizeof( HEAP_FREE_ENTRY ) != (2 * sizeof( HEAP_ENTRY ))) {
  172. HeapDebugPrint(( "Heap header and free header sizes disagree\n" ));
  173. HeapDebugBreak( NULL );
  174. }
  175. #endif // DBG
  176. //
  177. // Initialize the heap specific structures in the current peb
  178. //
  179. Peb->NumberOfHeaps = 0;
  180. Peb->MaximumNumberOfHeaps = RTLP_STATIC_HEAP_LIST_SIZE;
  181. Peb->ProcessHeaps = RtlpProcessHeapsListBuffer;
  182. #ifdef NTHEAP_ENABLED
  183. {
  184. (VOID) RtlInitializeNtHeapManager();
  185. }
  186. #endif // NTHEAP_ENABLED
  187. //
  188. // Initialize the lock and return to our caller
  189. //
  190. return RtlInitializeLockRoutine( &RtlpProcessHeapsListLock.Lock );
  191. }
  192. //
  193. // Declared in ntrtl.h
  194. //
  195. VOID
  196. RtlProtectHeap (
  197. IN PVOID HeapHandle,
  198. IN BOOLEAN MakeReadOnly
  199. )
  200. /*++
  201. Routine Description:
  202. This routine will change the protection on all the pages in a heap
  203. to be either readonly or readwrite
  204. Arguments:
  205. HeapHandle - Supplies a pointer to the heap being altered
  206. MakeReadOnly - Specifies if the heap is to be made readonly or
  207. readwrite
  208. Return Value:
  209. None.
  210. --*/
  211. {
  212. PHEAP Heap;
  213. UCHAR SegmentIndex;
  214. PHEAP_SEGMENT Segment;
  215. MEMORY_BASIC_INFORMATION VaInfo;
  216. NTSTATUS Status;
  217. PVOID Address;
  218. PVOID ProtectAddress;
  219. SIZE_T Size;
  220. ULONG OldProtect;
  221. ULONG NewProtect;
  222. Heap = (PHEAP)HeapHandle;
  223. //
  224. // For every valid segment in the heap we will zoom through all its
  225. // regions and for those that are committed we'll change it protection
  226. //
  227. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  228. Segment = Heap->Segments[ SegmentIndex ];
  229. if ( Segment ) {
  230. //
  231. // Starting from the first address for the segment and going to
  232. // the last address in the segment we'll step through by regions
  233. //
  234. Address = Segment->BaseAddress;
  235. while ((ULONG_PTR)Address < (ULONG_PTR)(Segment->LastValidEntry)) {
  236. //
  237. // Query the current region to get its state and size
  238. //
  239. Status = ZwQueryVirtualMemory( NtCurrentProcess(),
  240. Address,
  241. MemoryBasicInformation,
  242. &VaInfo,
  243. sizeof(VaInfo),
  244. NULL );
  245. if (!NT_SUCCESS( Status )) {
  246. HeapDebugPrint(( "VirtualQuery Failed 0x%08x %x\n", Address, Status ));
  247. return;
  248. }
  249. //
  250. // If we found a commited block then set its protection
  251. //
  252. if (VaInfo.State == MEM_COMMIT) {
  253. Size = VaInfo.RegionSize;
  254. ProtectAddress = Address;
  255. if (MakeReadOnly) {
  256. NewProtect = PAGE_READONLY;
  257. } else {
  258. NewProtect = PAGE_READWRITE;
  259. }
  260. Status = ZwProtectVirtualMemory( NtCurrentProcess(),
  261. &ProtectAddress,
  262. &Size,
  263. NewProtect,
  264. &OldProtect );
  265. if (!NT_SUCCESS( Status )) {
  266. HeapDebugPrint(( "VirtualProtect Failed 0x%08x %x\n", Address, Status ));
  267. return;
  268. }
  269. }
  270. //
  271. // Now calculate the address of the next region in the segment
  272. //
  273. Address = (PVOID)((PCHAR)Address + VaInfo.RegionSize);
  274. }
  275. }
  276. }
  277. //
  278. // And return to our caller
  279. //
  280. return;
  281. }
  282. //
  283. // Declared in nturtl.h
  284. //
  285. BOOLEAN
  286. RtlLockHeap (
  287. IN PVOID HeapHandle
  288. )
  289. /*++
  290. Routine Description:
  291. This routine is used by lock access to a specific heap structure
  292. Arguments:
  293. HeapHandle - Supplies a pointer to the heap being locked
  294. Return Value:
  295. BOOLEAN - TRUE if the heap is now locked and FALSE otherwise (i.e.,
  296. the heap is ill-formed). TRUE is returned even if the heap is
  297. not lockable.
  298. --*/
  299. {
  300. PHEAP Heap = (PHEAP)HeapHandle;
  301. RTL_PAGED_CODE();
  302. //
  303. // Check for the heap protected by guard pages
  304. //
  305. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  306. RtlpDebugPageHeapLock( HeapHandle ));
  307. //
  308. // Validate that HeapAddress points to a HEAP structure.
  309. //
  310. if (!RtlpCheckHeapSignature( Heap, "RtlLockHeap" )) {
  311. return FALSE;
  312. }
  313. //
  314. // Lock the heap. And disable the lookaside list by incrementing
  315. // its lock count.
  316. //
  317. if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
  318. RtlAcquireLockRoutine( Heap->LockVariable );
  319. RtlpLockFrontHeap(Heap);
  320. }
  321. #ifndef NTOS_KERNEL_RUNTIME
  322. if( IsHeapLogging( HeapHandle ) ) {
  323. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  324. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  325. USHORT ReqSize = sizeof(NTDLL_EVENT_COMMON) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  326. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  327. if(pEventHeader && pThreadLocalData) {
  328. PNTDLL_EVENT_COMMON pHeapEvent = (PNTDLL_EVENT_COMMON)( (SIZE_T)pEventHeader
  329. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  330. pEventHeader->Packet.Size = (USHORT) ReqSize;
  331. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_LOCK;
  332. pHeapEvent->Handle = (PVOID)HeapHandle;
  333. ReleaseBufferLocation(pThreadLocalData);
  334. }
  335. }
  336. #endif // NTOS_KERNEL_RUNTIME
  337. return TRUE;
  338. }
  339. //
  340. // Declared in nturtl.h
  341. //
  342. BOOLEAN
  343. RtlUnlockHeap (
  344. IN PVOID HeapHandle
  345. )
  346. /*++
  347. Routine Description:
  348. This routine is used to unlock access to a specific heap structure
  349. Arguments:
  350. HeapHandle - Supplies a pointer to the heep being unlocked
  351. Return Value:
  352. BOOLEAN - TRUE if the heap is now unlocked and FALSE otherwise (i.e.,
  353. the heap is ill-formed). TRUE is also returned if the heap was
  354. never locked to begin with because it is not seralizable.
  355. --*/
  356. {
  357. PHEAP Heap = (PHEAP)HeapHandle;
  358. RTL_PAGED_CODE();
  359. //
  360. // Check for the heap protected by guard pages
  361. //
  362. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  363. RtlpDebugPageHeapUnlock( HeapHandle ));
  364. //
  365. // Validate that HeapAddress points to a HEAP structure.
  366. //
  367. if (!RtlpCheckHeapSignature( Heap, "RtlUnlockHeap" )) {
  368. return FALSE;
  369. }
  370. //
  371. // Unlock the heap. And enable the lookaside logic by decrementing
  372. // its lock count
  373. //
  374. if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
  375. RtlpUnlockFrontHeap(Heap);
  376. RtlReleaseLockRoutine( Heap->LockVariable );
  377. }
  378. #ifndef NTOS_KERNEL_RUNTIME
  379. if( IsHeapLogging( HeapHandle ) ) {
  380. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  381. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  382. USHORT ReqSize = sizeof(NTDLL_EVENT_COMMON) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  383. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  384. if(pEventHeader && pThreadLocalData) {
  385. PNTDLL_EVENT_COMMON pHeapEvent = (PNTDLL_EVENT_COMMON)( (SIZE_T)pEventHeader
  386. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  387. pEventHeader->Packet.Size = (USHORT) ReqSize;
  388. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_UNLOCK;
  389. pHeapEvent->Handle = (PVOID)HeapHandle;
  390. ReleaseBufferLocation(pThreadLocalData);
  391. }
  392. }
  393. #endif // NTOS_KERNEL_RUNTIME
  394. return TRUE;
  395. }
  396. //
  397. // Declared in nturtl.h
  398. //
  399. PVOID
  400. RtlReAllocateHeap (
  401. IN PVOID HeapHandle,
  402. IN ULONG Flags,
  403. IN PVOID BaseAddress,
  404. IN SIZE_T Size
  405. )
  406. /*++
  407. Routine Description:
  408. This routine will resize a user specified heap block. The new size
  409. can either be smaller or larger than the current block size.
  410. Arguments:
  411. HeapHandle - Supplies a pointer to the heap being modified
  412. Flags - Supplies a set of heap flags to augment those already
  413. enforced by the heap
  414. BaseAddress - Supplies the current address of a block allocated
  415. from heap. We will try and resize this block at its current
  416. address, but it could possibly move if this heap structure
  417. allows for relocation
  418. Size - Supplies the size, in bytes, for the newly resized heap
  419. block
  420. Return Value:
  421. PVOID - A pointer to the resized block. If the block had to move
  422. then this address will not be equal to the input base address
  423. --*/
  424. {
  425. PHEAP Heap = (PHEAP)HeapHandle;
  426. SIZE_T AllocationSize;
  427. PHEAP_ENTRY BusyBlock, NewBusyBlock;
  428. PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
  429. SIZE_T FreeSize;
  430. BOOLEAN LockAcquired = FALSE;
  431. PVOID NewBaseAddress;
  432. PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
  433. SIZE_T OldSize;
  434. SIZE_T AllocationIndex;
  435. SIZE_T OldAllocationIndex;
  436. UCHAR FreeFlags;
  437. NTSTATUS Status;
  438. PVOID DeCommitAddress;
  439. SIZE_T DeCommitSize;
  440. EXCEPTION_RECORD ExceptionRecord;
  441. PVOID TraceBaseAddress = NULL;
  442. //
  443. // If there isn't an address to relocate the heap at then our work is done
  444. //
  445. if (BaseAddress == NULL) {
  446. SET_LAST_STATUS( STATUS_SUCCESS );
  447. return NULL;
  448. }
  449. #ifdef NTHEAP_ENABLED
  450. {
  451. if (Heap->Flags & NTHEAP_ENABLED_FLAG) {
  452. return RtlReAllocateNtHeap( HeapHandle, Flags, BaseAddress, Size );
  453. }
  454. }
  455. #endif // NTHEAP_ENABLED
  456. if (RtlpGetLowFragHeap(Heap)) {
  457. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  458. if (BusyBlock->SegmentIndex == HEAP_LFH_INDEX) {
  459. //
  460. // Refuse realloc in place only for LFH blocks
  461. //
  462. if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) {
  463. return NULL;
  464. }
  465. OldSize = RtlSizeHeap(Heap, 0, BaseAddress);
  466. Flags &= ~HEAP_TAG_MASK;
  467. NewBaseAddress = RtlAllocateHeap(Heap, Flags, (Size ? Size : 1));
  468. if (NewBaseAddress) {
  469. //
  470. // Copy over the user's data area to the new block
  471. //
  472. RtlMoveMemory( NewBaseAddress, BaseAddress, Size < OldSize ? Size : OldSize );
  473. //
  474. // Check if we grew the block and we should zero
  475. // the remaining part.
  476. //
  477. if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) {
  478. RtlZeroMemory( (PCHAR)NewBaseAddress + OldSize,
  479. Size - OldSize );
  480. }
  481. RtlFreeHeap(Heap, Flags, BaseAddress);
  482. }
  483. #ifndef NTOS_KERNEL_RUNTIME
  484. if( IsHeapLogging( HeapHandle ) && NewBaseAddress != NULL ) {
  485. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  486. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  487. USHORT ReqSize = sizeof(PHEAP_EVENT_REALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  488. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  489. if(pEventHeader && pThreadLocalData) {
  490. PHEAP_EVENT_REALLOC pHeapEvent = (PHEAP_EVENT_REALLOC)((SIZE_T)pEventHeader
  491. +(SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  492. pEventHeader->Packet.Size = (USHORT) ReqSize;
  493. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_REALLOC;
  494. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  495. pHeapEvent->NewAddress = (PVOID)NewBaseAddress;
  496. pHeapEvent->OldAddress = (PVOID)BaseAddress;
  497. pHeapEvent->OldSize = OldSize;
  498. pHeapEvent->NewSize = Size;
  499. pHeapEvent->Source = MEMORY_FROM_LOWFRAG;
  500. ReleaseBufferLocation(pThreadLocalData);
  501. }
  502. }
  503. #endif //NTOS_KERNEL_RUNTIME
  504. return NewBaseAddress;
  505. }
  506. }
  507. //
  508. // Augment the heap flags
  509. //
  510. Flags |= Heap->ForceFlags;
  511. //
  512. // Check if we should simply call the debug version of heap to do the work
  513. //
  514. if (DEBUG_HEAP( Flags)) {
  515. return RtlDebugReAllocateHeap( HeapHandle, Flags, BaseAddress, Size );
  516. }
  517. //
  518. // Make sure we didn't get a negative heap size
  519. //
  520. if (Size > MAXINT_PTR) {
  521. SET_LAST_STATUS( STATUS_NO_MEMORY );
  522. return NULL;
  523. }
  524. //
  525. // Round the requested size up to the allocation granularity. Note
  526. // that if the request is for 0 bytes, we still allocate memory, because
  527. // we add in an extra byte to protect ourselves from errors.
  528. //
  529. AllocationSize = ((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask;
  530. if ((Flags & HEAP_NEED_EXTRA_FLAGS) ||
  531. (Heap->PseudoTagEntries != NULL) ||
  532. ((((PHEAP_ENTRY)BaseAddress)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT)) {
  533. AllocationSize += sizeof( HEAP_ENTRY_EXTRA );
  534. }
  535. try {
  536. //
  537. // Lock the heap
  538. //
  539. if (!(Flags & HEAP_NO_SERIALIZE)) {
  540. RtlAcquireLockRoutine( Heap->LockVariable );
  541. LockAcquired = TRUE;
  542. //
  543. // Because it is now zero the following statement will set the no
  544. // serialize bit
  545. //
  546. Flags ^= HEAP_NO_SERIALIZE;
  547. }
  548. try {
  549. //
  550. // Compute the heap block address for user specified block
  551. //
  552. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  553. //
  554. // Check if the block is not in use then it is an error
  555. //
  556. if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
  557. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  558. //
  559. // Bail if not a busy block.
  560. //
  561. leave;
  562. //
  563. // We need the current (i.e., old) size and allocation of the
  564. // block. Check if the block is a big allocation. The size
  565. // field of a big block is really the unused by count
  566. //
  567. } else if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  568. OldSize = RtlpGetSizeOfBigBlock( BusyBlock );
  569. OldAllocationIndex = (OldSize + BusyBlock->Size) >> HEAP_GRANULARITY_SHIFT;
  570. //
  571. // We'll need to adjust the new allocation size to account
  572. // for the big block header and then round it up to a page
  573. //
  574. AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  575. AllocationSize = ROUND_UP_TO_POWER2( AllocationSize, PAGE_SIZE );
  576. //
  577. // Otherwise the block is in use and is a small allocation
  578. //
  579. } else {
  580. OldAllocationIndex = BusyBlock->Size;
  581. OldSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) -
  582. RtlpGetUnusedBytes(Heap, BusyBlock);
  583. }
  584. //
  585. // Compute the new allocation index
  586. //
  587. AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
  588. //
  589. // At this point we have the old size and index, and the new size
  590. // and index
  591. //
  592. // See if new size less than or equal to the current size.
  593. //
  594. if (AllocationIndex <= OldAllocationIndex) {
  595. //
  596. // If the new allocation index is only one less then the current
  597. // index then make the sizes equal
  598. //
  599. if (AllocationIndex + 1 == OldAllocationIndex) {
  600. AllocationIndex += 1;
  601. AllocationSize += sizeof( HEAP_ENTRY );
  602. }
  603. //
  604. // Calculate new residual (unused) amount
  605. //
  606. if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  607. //
  608. // In a big block the size is really the unused byte count
  609. //
  610. BusyBlock->Size = (USHORT)(AllocationSize - Size);
  611. } else if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  612. //
  613. // The extra stuff struct goes after the data. So compute
  614. // the old and new extra stuff location and copy the data
  615. //
  616. OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
  617. NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
  618. *NewExtraStuff = *OldExtraStuff;
  619. //
  620. // If we're doing heap tagging then update the tag entry
  621. //
  622. if (IS_HEAP_TAGGING_ENABLED()) {
  623. NewExtraStuff->TagIndex =
  624. RtlpUpdateTagEntry( Heap,
  625. NewExtraStuff->TagIndex,
  626. OldAllocationIndex,
  627. AllocationIndex,
  628. ReAllocationAction );
  629. }
  630. RtlpSetUnusedBytes(Heap, BusyBlock, (AllocationSize - Size));
  631. } else {
  632. //
  633. // If we're doing heap tagging then update the tag entry
  634. //
  635. if (IS_HEAP_TAGGING_ENABLED()) {
  636. BusyBlock->SmallTagIndex = (UCHAR)
  637. RtlpUpdateTagEntry( Heap,
  638. BusyBlock->SmallTagIndex,
  639. BusyBlock->Size,
  640. AllocationIndex,
  641. ReAllocationAction );
  642. }
  643. RtlpSetUnusedBytes(Heap, BusyBlock, (AllocationSize - Size));
  644. }
  645. //
  646. // Check if the block is getting bigger, then fill in the extra
  647. // space.
  648. //
  649. // This happens even if the allocation index is less than or
  650. // equal to the old allocation index, because the old allocation
  651. // index contains unused bytes as well.
  652. //
  653. if (Size > OldSize) {
  654. //
  655. // See if we should zero the extra space
  656. //
  657. if (Flags & HEAP_ZERO_MEMORY) {
  658. RtlZeroMemory( (PCHAR)BaseAddress + OldSize,
  659. Size - OldSize );
  660. //
  661. // Otherwise see if we should fill the extra space
  662. //
  663. } else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
  664. SIZE_T PartialBytes, ExtraSize;
  665. PartialBytes = OldSize & (sizeof( ULONG ) - 1);
  666. if (PartialBytes) {
  667. PartialBytes = 4 - PartialBytes;
  668. }
  669. if (Size > (OldSize + PartialBytes)) {
  670. ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof( ULONG ) - 1);
  671. if (ExtraSize != 0) {
  672. RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
  673. ExtraSize,
  674. ALLOC_HEAP_FILL );
  675. }
  676. }
  677. }
  678. }
  679. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
  680. RtlFillMemory( (PCHAR)(BusyBlock + 1) + Size,
  681. CHECK_HEAP_TAIL_SIZE,
  682. CHECK_HEAP_TAIL_FILL );
  683. }
  684. //
  685. // If amount of change is greater than the size of a free block,
  686. // then need to free the extra space. Otherwise, nothing else to
  687. // do.
  688. //
  689. if (AllocationIndex != OldAllocationIndex) {
  690. FreeFlags = BusyBlock->Flags & ~HEAP_ENTRY_BUSY;
  691. if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  692. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  693. VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  694. if (IS_HEAP_TAGGING_ENABLED()) {
  695. VirtualAllocBlock->ExtraStuff.TagIndex =
  696. RtlpUpdateTagEntry( Heap,
  697. VirtualAllocBlock->ExtraStuff.TagIndex,
  698. OldAllocationIndex,
  699. AllocationIndex,
  700. VirtualReAllocationAction );
  701. }
  702. DeCommitAddress = (PCHAR)VirtualAllocBlock + AllocationSize;
  703. DeCommitSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) -
  704. AllocationSize;
  705. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  706. (PVOID *)&DeCommitAddress,
  707. &DeCommitSize,
  708. MEM_DECOMMIT );
  709. if (!NT_SUCCESS( Status )) {
  710. HeapDebugPrint(( "Unable to release memory at %p for %p bytes - Status == %x\n",
  711. DeCommitAddress, DeCommitSize, Status ));
  712. HeapDebugBreak( NULL );
  713. } else {
  714. VirtualAllocBlock->CommitSize -= DeCommitSize;
  715. }
  716. } else {
  717. //
  718. // Otherwise, shrink size of this block to new size, and make extra
  719. // space at end free.
  720. //
  721. SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
  722. SplitBlock->Flags = FreeFlags;
  723. SplitBlock->PreviousSize = (USHORT)AllocationIndex;
  724. SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
  725. FreeSize = BusyBlock->Size - AllocationIndex;
  726. BusyBlock->Size = (USHORT)AllocationIndex;
  727. BusyBlock->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
  728. //
  729. // If the following block is uncommitted then we only need to
  730. // add this new entry to its free list
  731. //
  732. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  733. PHEAP_SEGMENT Segment;
  734. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  735. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  736. SplitBlock->Size = (USHORT)FreeSize;
  737. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  738. Heap->TotalFreeSize += FreeSize;
  739. } else {
  740. //
  741. // Otherwise get the next block and check if it is busy. If it
  742. // is in use then add this new entry to its free list
  743. //
  744. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  745. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
  746. SplitBlock->Size = (USHORT)FreeSize;
  747. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  748. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  749. Heap->TotalFreeSize += FreeSize;
  750. } else {
  751. //
  752. // Otherwise the next block is not in use so we
  753. // should be able to merge with it. Remove the
  754. // second free block and if the combined size is
  755. // still okay then merge the two blocks and add
  756. // the single block back in. Otherwise call a
  757. // routine that will actually break it apart
  758. // before insertion.
  759. //
  760. SplitBlock->Flags = SplitBlock2->Flags;
  761. RtlpRemoveFreeBlock( Heap, SplitBlock2 );
  762. Heap->TotalFreeSize -= SplitBlock2->Size;
  763. FreeSize += SplitBlock2->Size;
  764. if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
  765. SplitBlock->Size = (USHORT)FreeSize;
  766. if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  767. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  768. } else {
  769. PHEAP_SEGMENT Segment;
  770. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  771. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  772. }
  773. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  774. Heap->TotalFreeSize += FreeSize;
  775. } else {
  776. RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
  777. }
  778. }
  779. }
  780. }
  781. }
  782. } else {
  783. //
  784. // At this point the new size is greater than the current size
  785. //
  786. // If the block is a big allocation or we're not able to grow
  787. // the block in place then we have a lot of work to do
  788. //
  789. if ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
  790. !RtlpGrowBlockInPlace( Heap, Flags, BusyBlock, Size, AllocationIndex )) {
  791. //
  792. // We're growing the block. Allocate a new block with the bigger
  793. // size, copy the contents of the old block to the new block and then
  794. // free the old block. Return the address of the new block.
  795. //
  796. if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) {
  797. #if DBG
  798. // HeapDebugPrint(( "Failing ReAlloc because cant do it inplace.\n" ));
  799. #endif
  800. BaseAddress = NULL;
  801. } else {
  802. //
  803. // Clear the tag bits from the flags
  804. //
  805. Flags &= ~HEAP_TAG_MASK;
  806. //
  807. // If there is an extra struct present then get the tag
  808. // index from the extra stuff and augment the flags with
  809. // the tag index.
  810. //
  811. if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  812. Flags &= ~HEAP_SETTABLE_USER_FLAGS;
  813. Flags |= HEAP_SETTABLE_USER_VALUE |
  814. ((BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
  815. OldExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
  816. try {
  817. if ((OldExtraStuff->TagIndex != 0) &&
  818. !(OldExtraStuff->TagIndex & HEAP_PSEUDO_TAG_FLAG)) {
  819. Flags |= OldExtraStuff->TagIndex << HEAP_TAG_SHIFT;
  820. }
  821. } except (EXCEPTION_EXECUTE_HANDLER) {
  822. BusyBlock->Flags &= ~HEAP_ENTRY_EXTRA_PRESENT;
  823. }
  824. } else if (BusyBlock->SmallTagIndex != 0) {
  825. //
  826. // There is not an extra stuff struct, but block
  827. // does have a small tag index so now add this small
  828. // tag to the flags
  829. //
  830. Flags |= BusyBlock->SmallTagIndex << HEAP_TAG_SHIFT;
  831. }
  832. //
  833. // Allocate from the heap space for the reallocation
  834. //
  835. NewBaseAddress = RtlAllocateHeap( HeapHandle,
  836. Flags & ~HEAP_ZERO_MEMORY,
  837. Size );
  838. if (NewBaseAddress != NULL) {
  839. //
  840. // We were able to get the allocation so now back up
  841. // to the heap block and if the block has an extra
  842. // stuff struct then copy over the extra stuff
  843. //
  844. NewBusyBlock = (PHEAP_ENTRY)NewBaseAddress - 1;
  845. if (NewBusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  846. NewExtraStuff = RtlpGetExtraStuffPointer( NewBusyBlock );
  847. if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  848. OldExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
  849. NewExtraStuff->Settable = OldExtraStuff->Settable;
  850. } else {
  851. RtlZeroMemory( NewExtraStuff, sizeof( *NewExtraStuff ));
  852. }
  853. }
  854. //
  855. // Copy over the user's data area to the new block
  856. //
  857. RtlCopyMemory( NewBaseAddress, BaseAddress, Size < OldSize ? Size : OldSize );
  858. //
  859. // Check if we grew the block and we should zero
  860. // the remaining part.
  861. //
  862. if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) {
  863. RtlZeroMemory( (PCHAR)NewBaseAddress + OldSize,
  864. Size - OldSize );
  865. }
  866. //
  867. // Release the old block
  868. //
  869. RtlFreeHeap( HeapHandle,
  870. Flags,
  871. BaseAddress );
  872. }
  873. TraceBaseAddress = BaseAddress;
  874. BaseAddress = NewBaseAddress;
  875. }
  876. }
  877. }
  878. if ((BaseAddress == NULL) && (Flags & HEAP_GENERATE_EXCEPTIONS)) {
  879. //
  880. // Construct an exception record.
  881. //
  882. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  883. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  884. ExceptionRecord.NumberParameters = 1;
  885. ExceptionRecord.ExceptionFlags = 0;
  886. ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
  887. RtlRaiseException( &ExceptionRecord );
  888. }
  889. } except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
  890. EXCEPTION_EXECUTE_HANDLER ) {
  891. SET_LAST_STATUS( GetExceptionCode() );
  892. BaseAddress = NULL;
  893. }
  894. } finally {
  895. //
  896. // Unlock the heap
  897. //
  898. if (LockAcquired) {
  899. RtlReleaseLockRoutine( Heap->LockVariable );
  900. }
  901. }
  902. #ifndef NTOS_KERNEL_RUNTIME
  903. if( IsHeapLogging( HeapHandle ) && BaseAddress ) {
  904. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  905. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  906. USHORT ReqSize = sizeof(HEAP_EVENT_REALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  907. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  908. if(pEventHeader && pThreadLocalData) {
  909. PHEAP_EVENT_REALLOC pHeapEvent=(PHEAP_EVENT_REALLOC)( (SIZE_T)pEventHeader
  910. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  911. pEventHeader->Packet.Size = (USHORT) ReqSize;
  912. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_REALLOC;
  913. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  914. pHeapEvent->NewAddress = (PVOID)BaseAddress;
  915. if(TraceBaseAddress){
  916. pHeapEvent->OldAddress = (PVOID)TraceBaseAddress;
  917. } else {
  918. pHeapEvent->OldAddress = (PVOID)BaseAddress;
  919. }
  920. pHeapEvent->OldSize = OldSize;
  921. pHeapEvent->NewSize = Size;
  922. pHeapEvent->Source = MEMORY_FROM_MAINPATH;
  923. ReleaseBufferLocation(pThreadLocalData);
  924. }
  925. }
  926. #endif //NTOS_KERNEL_RUNTIME
  927. //
  928. // And return to our caller
  929. //
  930. return BaseAddress;
  931. }
  932. //
  933. // Declared in nturtl.h
  934. //
  935. BOOLEAN
  936. RtlGetUserInfoHeap (
  937. IN PVOID HeapHandle,
  938. IN ULONG Flags,
  939. IN PVOID BaseAddress,
  940. OUT PVOID *UserValue OPTIONAL,
  941. OUT PULONG UserFlags OPTIONAL
  942. )
  943. /*++
  944. Routine Description:
  945. This routine returns to the user the set of user flags
  946. and user values for the specified heap entry. The user value
  947. is set via a set call and the user flags is part of the
  948. user settable flags used when communicating with the heap package
  949. and can also be set via a set call
  950. Arguments:
  951. HeapHandle - Supplies a pointer to the heap being queried
  952. Flags - Supplies a set of flags to agument those already in the heap
  953. BaseAddress - Supplies a pointer to the users heap entry being
  954. queried
  955. UserValue - Optionally supplies a pointer to recieve the heap entry
  956. value
  957. UserFlasg - Optionally supplies a pointer to recieve the heap flags
  958. Return Value:
  959. BOOLEAN - TRUE if the query is successful and FALSE otherwise
  960. --*/
  961. {
  962. PHEAP Heap = (PHEAP)HeapHandle;
  963. PHEAP_ENTRY BusyBlock;
  964. PHEAP_ENTRY_EXTRA ExtraStuff;
  965. BOOLEAN LockAcquired = FALSE;
  966. BOOLEAN Result;
  967. //
  968. // Build up a set of real flags to use in this operation
  969. //
  970. Flags |= Heap->ForceFlags;
  971. //
  972. // Check if we should be going the debug route
  973. //
  974. if (DEBUG_HEAP( Flags )) {
  975. return RtlDebugGetUserInfoHeap( HeapHandle, Flags, BaseAddress, UserValue, UserFlags );
  976. }
  977. Result = FALSE;
  978. try {
  979. try {
  980. //
  981. // Lock the heap
  982. //
  983. if (!(Flags & HEAP_NO_SERIALIZE)) {
  984. RtlAcquireLockRoutine( Heap->LockVariable );
  985. LockAcquired = TRUE;
  986. }
  987. //
  988. // Backup the pointer to the heap entry
  989. //
  990. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  991. //
  992. // If the entry is not in use then it is an error
  993. //
  994. if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
  995. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  996. } else {
  997. //
  998. // The heap entry is in use so now check if there is
  999. // any extra information present
  1000. //
  1001. if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  1002. //
  1003. // Get a pointer to the extra information and if the
  1004. // user asked for user values then that field from the
  1005. // extra stuff
  1006. //
  1007. ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
  1008. if (ARGUMENT_PRESENT( UserValue )) {
  1009. *UserValue = (PVOID)ExtraStuff->Settable;
  1010. }
  1011. }
  1012. //
  1013. // If the user asked for user flags then return the flags
  1014. // from the heap entry that are user setable
  1015. //
  1016. if (ARGUMENT_PRESENT( UserFlags )) {
  1017. *UserFlags = (BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
  1018. }
  1019. //
  1020. // Now that the assignments are done we can say that
  1021. // we were successful
  1022. //
  1023. Result = TRUE;
  1024. }
  1025. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1026. SET_LAST_STATUS( GetExceptionCode() );
  1027. Result = FALSE;
  1028. }
  1029. } finally {
  1030. //
  1031. // Unlock the heap
  1032. //
  1033. if (LockAcquired) {
  1034. RtlReleaseLockRoutine( Heap->LockVariable );
  1035. }
  1036. }
  1037. //
  1038. // And return to our caller
  1039. //
  1040. return Result;
  1041. }
  1042. //
  1043. // Declared in nturtl.h
  1044. //
  1045. BOOLEAN
  1046. RtlSetUserValueHeap (
  1047. IN PVOID HeapHandle,
  1048. IN ULONG Flags,
  1049. IN PVOID BaseAddress,
  1050. IN PVOID UserValue
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. This routine is used to set the user settable value for a heap entry
  1055. Arguments:
  1056. HeapHandle - Supplies a pointer to the heap being modified
  1057. Flags - Supplies a set of flags needed to augment those already enforced
  1058. by the heap
  1059. BaseAddress - Supplies a pointer to the heap entry allocation being
  1060. modified
  1061. UserValue - Supplies the value to store in the extra stuff space of
  1062. the heap entry
  1063. Return Value:
  1064. BOOLEAN - TRUE if the setting worked, and FALSE otherwise. It could be
  1065. FALSE if the base address is invalid, or if there is not room for
  1066. the extra stuff
  1067. --*/
  1068. {
  1069. PHEAP Heap = (PHEAP)HeapHandle;
  1070. PHEAP_ENTRY BusyBlock;
  1071. PHEAP_ENTRY_EXTRA ExtraStuff;
  1072. BOOLEAN LockAcquired = FALSE;
  1073. BOOLEAN Result;
  1074. //
  1075. // Augment the set of flags
  1076. //
  1077. Flags |= Heap->ForceFlags;
  1078. //
  1079. // Check to see if we should be going the debug route
  1080. //
  1081. if (DEBUG_HEAP( Flags )) {
  1082. return RtlDebugSetUserValueHeap( HeapHandle, Flags, BaseAddress, UserValue );
  1083. }
  1084. Result = FALSE;
  1085. try {
  1086. //
  1087. // Lock the heap
  1088. //
  1089. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1090. RtlAcquireLockRoutine( Heap->LockVariable );
  1091. LockAcquired = TRUE;
  1092. }
  1093. //
  1094. // Get a pointer to the owning heap entry
  1095. //
  1096. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  1097. //
  1098. // If the entry is not in use then its is an error
  1099. //
  1100. if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
  1101. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  1102. //
  1103. // Otherwise we only can set the value if the entry has space
  1104. // for the extra stuff
  1105. //
  1106. } else if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  1107. ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
  1108. ExtraStuff->Settable = (ULONG_PTR)UserValue;
  1109. Result = TRUE;
  1110. }
  1111. } finally {
  1112. //
  1113. // Unlock the heap
  1114. //
  1115. if (LockAcquired) {
  1116. RtlReleaseLockRoutine( Heap->LockVariable );
  1117. }
  1118. }
  1119. //
  1120. // And return to our caller
  1121. //
  1122. return Result;
  1123. }
  1124. //
  1125. // Declared in nturtl.h
  1126. //
  1127. BOOLEAN
  1128. RtlSetUserFlagsHeap (
  1129. IN PVOID HeapHandle,
  1130. IN ULONG Flags,
  1131. IN PVOID BaseAddress,
  1132. IN ULONG UserFlagsReset,
  1133. IN ULONG UserFlagsSet
  1134. )
  1135. /*++
  1136. Routine Description:
  1137. HeapHandle - Supplies a pointer to the heap being modified
  1138. Flags - Supplies a set of flags needed to augment those already enforced
  1139. by the heap
  1140. BaseAddress - Supplies a pointer to the heap entry allocation being
  1141. modified
  1142. UserFlagsReset - Supplies a mask of flags that the user wants cleared
  1143. UserFlagsSet- Supplies a mask of flags that the user wants set
  1144. Return Value:
  1145. BOOLEAN - TRUE if the operation is a success and FALSE otherwise
  1146. --*/
  1147. {
  1148. PHEAP Heap = (PHEAP)HeapHandle;
  1149. PHEAP_ENTRY BusyBlock;
  1150. BOOLEAN LockAcquired = FALSE;
  1151. BOOLEAN Result = FALSE;
  1152. //
  1153. // Augment the set of flags
  1154. //
  1155. Flags |= Heap->ForceFlags;
  1156. //
  1157. // Check to see if we should be going the debug route
  1158. //
  1159. if (DEBUG_HEAP( Flags )) {
  1160. return RtlDebugSetUserFlagsHeap( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet );
  1161. }
  1162. try {
  1163. //
  1164. // Lock the heap
  1165. //
  1166. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1167. RtlAcquireLockRoutine( Heap->LockVariable );
  1168. LockAcquired = TRUE;
  1169. }
  1170. try {
  1171. //
  1172. // Get a pointer to the owning heap entry
  1173. //
  1174. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  1175. //
  1176. // If the entry is not in use then it is an error
  1177. //
  1178. if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
  1179. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  1180. } else {
  1181. //
  1182. // Otherwise modify the flags in the block
  1183. //
  1184. // This is terrible error prone if the user passes in
  1185. // flags that aren't 0x20 0x40 or 0x80 only.
  1186. //
  1187. BusyBlock->Flags &= ~(UserFlagsReset >> 4);
  1188. BusyBlock->Flags |= (UserFlagsSet >> 4);
  1189. Result = TRUE;
  1190. }
  1191. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1192. SET_LAST_STATUS( GetExceptionCode() );
  1193. Result = FALSE;
  1194. }
  1195. } finally {
  1196. //
  1197. // Unlock the heap
  1198. //
  1199. if (LockAcquired) {
  1200. RtlReleaseLockRoutine( Heap->LockVariable );
  1201. }
  1202. }
  1203. return Result;
  1204. }
  1205. //
  1206. // Declared in nturtl.h
  1207. //
  1208. ULONG
  1209. RtlCreateTagHeap (
  1210. IN PVOID HeapHandle,
  1211. IN ULONG Flags,
  1212. IN PWSTR TagPrefix OPTIONAL,
  1213. IN PWSTR TagNames
  1214. )
  1215. /*++
  1216. Routine Description:
  1217. This routine create a tag heap for either the specified heap or
  1218. for the global tag heap.
  1219. Arguments:
  1220. HeapHandle - Optionally supplies a pointer to the heap that we
  1221. want modified. If null then the global tag heap is used
  1222. Flags - Supplies a list of flags to augment the flags already
  1223. enforced by the heap
  1224. TagPrefix - Optionally supplies a null terminated wchar string
  1225. of a prefix to add to each tag
  1226. TagNames - Supplies a list of tag names separated by null and terminated
  1227. by a double null. If the first name in the list start with
  1228. a "!" then it is interpreted as the heap name. The syntax
  1229. for the tag name is
  1230. [!<heapname> nul ] {<tagname> nul}* nul
  1231. Return Value:
  1232. ULONG - returns the index of the last tag create shifted to the high
  1233. order word.
  1234. --*/
  1235. {
  1236. PHEAP Heap = (PHEAP)HeapHandle;
  1237. BOOLEAN LockAcquired = FALSE;
  1238. ULONG TagIndex;
  1239. ULONG NumberOfTags, MaxTagNameLength, TagPrefixLength;
  1240. PWSTR s, s1, HeapName;
  1241. PHEAP_TAG_ENTRY TagEntry;
  1242. ULONG Result;
  1243. //
  1244. // Check if tagging is disable and so this call is a noop
  1245. //
  1246. if (!IS_HEAP_TAGGING_ENABLED()) {
  1247. return 0;
  1248. }
  1249. //
  1250. // If the processes global tag heap has not been created yet then
  1251. // allocate a global tag heap
  1252. //
  1253. if (RtlpGlobalTagHeap == NULL) {
  1254. RtlpGlobalTagHeap = RtlAllocateHeap( RtlProcessHeap( ), HEAP_ZERO_MEMORY, sizeof( HEAP ));
  1255. if (RtlpGlobalTagHeap == NULL) {
  1256. return 0;
  1257. }
  1258. }
  1259. try {
  1260. //
  1261. // If the user passed in a heap then we'll use the lock from that
  1262. // heap to synchronize our work. Otherwise we're unsynchronized
  1263. //
  1264. if (Heap != NULL) {
  1265. //
  1266. // Tagging is not part of the guard page heap package
  1267. //
  1268. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, 0 );
  1269. //
  1270. // Check if we should be calling the debug version of the heap package
  1271. //
  1272. if (DEBUG_HEAP( Flags )) {
  1273. Result = RtlDebugCreateTagHeap( HeapHandle, Flags, TagPrefix, TagNames );
  1274. leave;
  1275. }
  1276. //
  1277. // Augment the flags and lock the specified heap
  1278. //
  1279. Flags |= Heap->ForceFlags;
  1280. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1281. RtlAcquireLockRoutine( Heap->LockVariable );
  1282. LockAcquired = TRUE;
  1283. }
  1284. }
  1285. //
  1286. // We start off with zero tags
  1287. //
  1288. TagIndex = 0;
  1289. NumberOfTags = 0;
  1290. //
  1291. // With tag names that start with "!" we assume what follows
  1292. // is a heap name.
  1293. //
  1294. if (*TagNames == L'!') {
  1295. HeapName = TagNames + 1;
  1296. //
  1297. // Move up to the following tag name after the heap name
  1298. // separated by a null
  1299. //
  1300. while (*TagNames++) { NOTHING; }
  1301. } else {
  1302. HeapName = NULL;
  1303. }
  1304. //
  1305. // Gobble up each tag name keeping count of how many we find
  1306. //
  1307. s = TagNames;
  1308. while (*s) {
  1309. while (*s++) { NOTHING; }
  1310. NumberOfTags += 1;
  1311. }
  1312. //
  1313. // Now we will only continue on if we were supplied tag names
  1314. //
  1315. if (NumberOfTags > 0) {
  1316. //
  1317. // Allocate heap entries for the number of tags we need and
  1318. // only proceed if this allocation succeeded. The following
  1319. // call also makes room for the heap name as tag index 0. Note
  1320. // that is heap is null then we assume we're using the global
  1321. // tag heap
  1322. //
  1323. TagEntry = RtlpAllocateTags( Heap, NumberOfTags );
  1324. if (TagEntry != NULL) {
  1325. MaxTagNameLength = (sizeof( TagEntry->TagName ) / sizeof( WCHAR )) - 1;
  1326. TagIndex = TagEntry->TagIndex;
  1327. //
  1328. // If the first tag index is zero then we'll make this tag entry
  1329. // the heap name.
  1330. //
  1331. if (TagIndex == 0) {
  1332. if (HeapName != NULL ) {
  1333. //
  1334. // Copy over the heap name and pad it out with nulls up
  1335. // to the end of the name buffer
  1336. //
  1337. wcsncpy( TagEntry->TagName, HeapName, MaxTagNameLength );
  1338. }
  1339. //
  1340. // Whether we add a heap name or not we'll move on to the
  1341. // next tag entry and index
  1342. //
  1343. TagEntry += 1;
  1344. TagIndex = TagEntry->TagIndex;
  1345. //
  1346. // This isn't the first index for a specified heap, but see if
  1347. // it is the first index for the global heap. If so then put
  1348. // name of the global tags into the 0 index
  1349. //
  1350. } else if (TagIndex == HEAP_GLOBAL_TAG) {
  1351. wcsncpy( TagEntry->TagName, L"GlobalTags", MaxTagNameLength );
  1352. TagEntry += 1;
  1353. TagIndex = TagEntry->TagIndex;
  1354. }
  1355. //
  1356. // Now we've taken case of the 0 index we'll go on to the rest of
  1357. // the tags. If there is tag prefix and it is not zero length
  1358. // then we'll use this tag prefix provided that is leaves us at
  1359. // least 4 characters for the tag name itself. Otherwise we'll
  1360. // ignore the tag prefix (by setting the variable to null).
  1361. //
  1362. if ((ARGUMENT_PRESENT( TagPrefix )) &&
  1363. (TagPrefixLength = wcslen( TagPrefix ))) {
  1364. if (TagPrefixLength >= MaxTagNameLength-4) {
  1365. TagPrefix = NULL;
  1366. } else {
  1367. MaxTagNameLength -= TagPrefixLength;
  1368. }
  1369. } else {
  1370. TagPrefix = NULL;
  1371. }
  1372. //
  1373. // For every tag name (note that this varable has already been
  1374. // advanced beyond the heap name) we'll put it in a tag entry
  1375. // by copying in the prefix and then appending on the tag itself
  1376. //
  1377. // s points to the current users supplied tag name
  1378. // s1 points to the tag name buffer in the current tag entry
  1379. //
  1380. s = TagNames;
  1381. while (*s) {
  1382. s1 = TagEntry->TagName;
  1383. //
  1384. // Copy in the optional tag prefix and update s1
  1385. //
  1386. if (ARGUMENT_PRESENT( TagPrefix )) {
  1387. wcscpy( s1, TagPrefix );
  1388. s1 += TagPrefixLength;
  1389. }
  1390. //
  1391. // Copy over the remaining tag name padding it with nulls
  1392. // up to the end of the name buffer
  1393. //
  1394. wcsncpy( s1, s, MaxTagNameLength );
  1395. //
  1396. // Skip to the next tag name
  1397. //
  1398. while (*s++) { NOTHING; }
  1399. //
  1400. // Skip to the next tag entry
  1401. //
  1402. TagEntry += 1;
  1403. }
  1404. }
  1405. }
  1406. Result = TagIndex << HEAP_TAG_SHIFT;
  1407. } finally {
  1408. //
  1409. // Unlock the heap
  1410. //
  1411. if (LockAcquired) {
  1412. RtlReleaseLockRoutine( Heap->LockVariable );
  1413. }
  1414. }
  1415. //
  1416. // And return to our caller. The answer we return is the last tag index
  1417. // stored in the high word of a ulong result
  1418. //
  1419. return Result;
  1420. }
  1421. //
  1422. // Declared in nturtl.h
  1423. //
  1424. PWSTR
  1425. RtlQueryTagHeap (
  1426. IN PVOID HeapHandle,
  1427. IN ULONG Flags,
  1428. IN USHORT TagIndex,
  1429. IN BOOLEAN ResetCounters,
  1430. OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
  1431. )
  1432. /*++
  1433. Routine Description:
  1434. This routine returns the name and optional statistics for a given
  1435. tag index.
  1436. Arguments:
  1437. Note that some of the code looks like it can handle the
  1438. global tag heap but other places look rather wrong
  1439. HeapHandle - Specifies the heap being queried. If null then the
  1440. global tag heap is used.
  1441. Flags - Supplies a set flags to augment those enforced by the
  1442. heap
  1443. TagIndex - Specifies the tag index that we want to query
  1444. ResetCounter - Specifies if this routine should reset the counter
  1445. for the tag after the query
  1446. TagInfo - Optionally supplies storage where the output tag information
  1447. should be stored
  1448. Return Value:
  1449. PWSTR - Returns a pointer to the tag name or NULL if the index
  1450. doesn't exist
  1451. --*/
  1452. {
  1453. PHEAP Heap = (PHEAP)HeapHandle;
  1454. BOOLEAN LockAcquired = FALSE;
  1455. PHEAP_TAG_ENTRY TagEntry;
  1456. PWSTR Result;
  1457. //
  1458. // Tagging is not part of the guard page heap package
  1459. //
  1460. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, NULL );
  1461. //
  1462. // Check if tagging is disabled
  1463. //
  1464. if (!IS_HEAP_TAGGING_ENABLED()) {
  1465. return NULL;
  1466. }
  1467. try {
  1468. //
  1469. // Check if the caller has given us a heap to query
  1470. //
  1471. Result = NULL;
  1472. if (Heap == NULL) {
  1473. leave;
  1474. }
  1475. //
  1476. // Check if we should be using the debug version of the
  1477. // heap package
  1478. //
  1479. if (DEBUG_HEAP( Flags )) {
  1480. Result = RtlDebugQueryTagHeap( HeapHandle, Flags, TagIndex, ResetCounters, TagInfo );
  1481. leave;
  1482. }
  1483. //
  1484. // Lock the heap
  1485. //
  1486. Flags |= Heap->ForceFlags;
  1487. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1488. RtlAcquireLockRoutine( Heap->LockVariable );
  1489. LockAcquired = TRUE;
  1490. }
  1491. //
  1492. // Check that the specified tag index is valid and that the
  1493. // this heap does actually have some tag entries
  1494. //
  1495. if ((TagIndex < Heap->NextAvailableTagIndex) &&
  1496. (Heap->TagEntries != NULL)) {
  1497. //
  1498. // Stride over to the specific tag entry and if the caller gave us
  1499. // an output buffer then fill in the details
  1500. //
  1501. TagEntry = Heap->TagEntries + TagIndex;
  1502. if (ARGUMENT_PRESENT( TagInfo )) {
  1503. TagInfo->NumberOfAllocations = TagEntry->Allocs;
  1504. TagInfo->NumberOfFrees = TagEntry->Frees;
  1505. TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
  1506. }
  1507. //
  1508. // Check if we should reset the counters
  1509. //
  1510. if (ResetCounters) {
  1511. TagEntry->Allocs = 0;
  1512. TagEntry->Frees = 0;
  1513. TagEntry->Size = 0;
  1514. }
  1515. //
  1516. // Point to the tag name
  1517. //
  1518. Result = &TagEntry->TagName[ 0 ];
  1519. //
  1520. // If the tag index has the psuedo tag bit set then recalulate the
  1521. // tag index and if this heap has pseudo tags than that is what
  1522. // we'll return
  1523. //
  1524. } else if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
  1525. //
  1526. // Clear the bit
  1527. //
  1528. TagIndex ^= HEAP_PSEUDO_TAG_FLAG;
  1529. if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) &&
  1530. (Heap->PseudoTagEntries != NULL)) {
  1531. //
  1532. // Stride over to the specific pseudo tag entry and if the
  1533. // caller gave us an output buffer then fill in the details
  1534. //
  1535. TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
  1536. if (ARGUMENT_PRESENT( TagInfo )) {
  1537. TagInfo->NumberOfAllocations = TagEntry->Allocs;
  1538. TagInfo->NumberOfFrees = TagEntry->Frees;
  1539. TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
  1540. }
  1541. //
  1542. // Check if we should reset the counters
  1543. //
  1544. if (ResetCounters) {
  1545. TagEntry->Allocs = 0;
  1546. TagEntry->Frees = 0;
  1547. TagEntry->Size = 0;
  1548. }
  1549. //
  1550. // Pseudo tags do not have names
  1551. //
  1552. Result = L"";
  1553. }
  1554. }
  1555. } finally {
  1556. //
  1557. // Unlock the heap
  1558. //
  1559. if (LockAcquired) {
  1560. RtlReleaseLockRoutine( Heap->LockVariable );
  1561. }
  1562. }
  1563. //
  1564. // And return the tag name to our caller
  1565. //
  1566. return Result;
  1567. }
  1568. //
  1569. // Declared in nturtl.h
  1570. //
  1571. NTSTATUS
  1572. RtlExtendHeap (
  1573. IN PVOID HeapHandle,
  1574. IN ULONG Flags,
  1575. IN PVOID Base,
  1576. IN SIZE_T Size
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. This routine grows the specified heap by adding a new segment to its
  1581. storage. The memory for the segment is supplied by the caller.
  1582. Arguments:
  1583. HeapHandle - Supplies a pointer to the heap being modified
  1584. Flags - Supplies a set of flags used to augment those already
  1585. enforced by the heap
  1586. Base - Supplies the starting address for the new segment being added
  1587. to the input heap
  1588. Size - Supplies the size, in bytes, of the new segment. Note that this
  1589. routine will actually use more memory than specified by this
  1590. variable. It will use whatever is committed and reserved provided
  1591. the amount is greater than or equal to "Size"
  1592. Return Value:
  1593. NTSTATUS - An appropriate status value
  1594. --*/
  1595. {
  1596. PHEAP Heap = (PHEAP)HeapHandle;
  1597. NTSTATUS Status;
  1598. PHEAP_SEGMENT Segment;
  1599. BOOLEAN LockAcquired = FALSE;
  1600. UCHAR SegmentIndex, EmptySegmentIndex;
  1601. SIZE_T CommitSize;
  1602. SIZE_T ReserveSize;
  1603. ULONG SegmentFlags;
  1604. PVOID CommittedBase;
  1605. PVOID UnCommittedBase;
  1606. MEMORY_BASIC_INFORMATION MemoryInformation;
  1607. //
  1608. // Check if the guard page version of heap can do the work
  1609. //
  1610. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  1611. RtlpDebugPageHeapExtend( HeapHandle, Flags, Base, Size ));
  1612. //
  1613. // See what Mm thinks about the base address we were passed in.
  1614. // The address must not be free.
  1615. //
  1616. Status = NtQueryVirtualMemory( NtCurrentProcess(),
  1617. Base,
  1618. MemoryBasicInformation,
  1619. &MemoryInformation,
  1620. sizeof( MemoryInformation ),
  1621. NULL );
  1622. if (!NT_SUCCESS( Status )) {
  1623. return Status;
  1624. }
  1625. if (MemoryInformation.State == MEM_FREE) {
  1626. return STATUS_INVALID_PARAMETER;
  1627. }
  1628. //
  1629. // If what we were passed in as a base address is not on a page boundary then
  1630. // adjust the information supplied by MM to the page boundary right after
  1631. // the input base address
  1632. //
  1633. if (MemoryInformation.BaseAddress != Base) {
  1634. MemoryInformation.BaseAddress = (PCHAR)MemoryInformation.BaseAddress + PAGE_SIZE;
  1635. MemoryInformation.RegionSize -= PAGE_SIZE;
  1636. }
  1637. try {
  1638. //
  1639. // Lock the heap
  1640. //
  1641. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1642. RtlAcquireLockRoutine( Heap->LockVariable );
  1643. LockAcquired = TRUE;
  1644. }
  1645. //
  1646. // Scan the heap's segment list for a free segment. And make sure the address
  1647. // of all the segment does not contain the input base address
  1648. //
  1649. Status = STATUS_INSUFFICIENT_RESOURCES;
  1650. EmptySegmentIndex = HEAP_MAXIMUM_SEGMENTS;
  1651. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  1652. Segment = Heap->Segments[ SegmentIndex ];
  1653. if (Segment) {
  1654. if (((ULONG_PTR)Base >= (ULONG_PTR)Segment) &&
  1655. ((ULONG_PTR)Base < (ULONG_PTR)(Segment->LastValidEntry))) {
  1656. Status = STATUS_INVALID_PARAMETER;
  1657. break;
  1658. }
  1659. } else if ((Segment == NULL) &&
  1660. (EmptySegmentIndex == HEAP_MAXIMUM_SEGMENTS)) {
  1661. EmptySegmentIndex = SegmentIndex;
  1662. Status = STATUS_SUCCESS;
  1663. }
  1664. }
  1665. //
  1666. // At this point if status is success then the empty segment index
  1667. // is available for us to use and base address doesn't overlap an
  1668. // existing segment.
  1669. //
  1670. if (NT_SUCCESS( Status )) {
  1671. //
  1672. // Indicate that this segment is user supplied
  1673. //
  1674. SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
  1675. CommittedBase = MemoryInformation.BaseAddress;
  1676. //
  1677. // If the start of the memory supplied by the use is already
  1678. // committed then check the state of the following
  1679. // uncommitted piece of memory to see if it is reserved
  1680. //
  1681. if (MemoryInformation.State == MEM_COMMIT) {
  1682. CommitSize = MemoryInformation.RegionSize;
  1683. UnCommittedBase = (PCHAR)CommittedBase + CommitSize;
  1684. Status = NtQueryVirtualMemory( NtCurrentProcess(),
  1685. UnCommittedBase,
  1686. MemoryBasicInformation,
  1687. &MemoryInformation,
  1688. sizeof( MemoryInformation ),
  1689. NULL );
  1690. ReserveSize = CommitSize;
  1691. if ((NT_SUCCESS( Status )) &&
  1692. (MemoryInformation.State == MEM_RESERVE)) {
  1693. ReserveSize += MemoryInformation.RegionSize;
  1694. }
  1695. } else {
  1696. //
  1697. // Otherwise the user hasn't committed anything in the
  1698. // the address they gave us and we know it is not free
  1699. // so it must be reserved.
  1700. //
  1701. UnCommittedBase = CommittedBase;
  1702. ReserveSize = MemoryInformation.RegionSize;
  1703. }
  1704. //
  1705. // Now if the reserved size is smaller than a page size or
  1706. // the user specified size is greater than the reserved size
  1707. // then the buffer we're given is too small to be a segment
  1708. // of heap
  1709. //
  1710. if ((ReserveSize < PAGE_SIZE) ||
  1711. (Size > ReserveSize)) {
  1712. Status = STATUS_BUFFER_TOO_SMALL;
  1713. } else {
  1714. //
  1715. // Otherwise the size is okay, now check if we need
  1716. // to do the commit of the base. If so we'll commit
  1717. // one page
  1718. if (UnCommittedBase == CommittedBase) {
  1719. CommitSize = PAGE_SIZE;
  1720. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  1721. (PVOID *)&Segment,
  1722. 0,
  1723. &CommitSize,
  1724. MEM_COMMIT,
  1725. PAGE_READWRITE );
  1726. }
  1727. }
  1728. //
  1729. // At this point the if status is good then memory is all set up
  1730. // with at least one page of committed memory to start with. So
  1731. // initialize the heap segment and we're done.
  1732. //
  1733. if (NT_SUCCESS( Status )) {
  1734. if (RtlpInitializeHeapSegment( Heap,
  1735. Segment,
  1736. EmptySegmentIndex,
  1737. 0,
  1738. Segment,
  1739. (PCHAR)Segment + CommitSize,
  1740. (PCHAR)Segment + ReserveSize )) {
  1741. Status = STATUS_NO_MEMORY;
  1742. }
  1743. }
  1744. }
  1745. } finally {
  1746. //
  1747. // Unlock the heap
  1748. //
  1749. if (LockAcquired) {
  1750. RtlReleaseLockRoutine( Heap->LockVariable );
  1751. }
  1752. }
  1753. //
  1754. // And return to our caller
  1755. //
  1756. return Status;
  1757. }
  1758. //
  1759. // Declared in nturtl.h
  1760. //
  1761. SIZE_T
  1762. NTAPI
  1763. RtlCompactHeap (
  1764. IN PVOID HeapHandle,
  1765. IN ULONG Flags
  1766. )
  1767. /*++
  1768. Routine Description:
  1769. This routine compacts the specified heap by coalescing all the free block.
  1770. It also determines the size of the largest available free block and
  1771. returns its, in bytes, back to the caller.
  1772. Arguments:
  1773. HeapHandle - Supplies a pointer to the heap being modified
  1774. Flags - Supplies a set of flags used to augment those already
  1775. enforced by the heap
  1776. Return Value:
  1777. SIZE_T - Returns the size, in bytes, of the largest free block
  1778. available in the heap
  1779. --*/
  1780. {
  1781. PHEAP Heap = (PHEAP)HeapHandle;
  1782. PHEAP_FREE_ENTRY FreeBlock;
  1783. PHEAP_SEGMENT Segment;
  1784. UCHAR SegmentIndex;
  1785. SIZE_T LargestFreeSize;
  1786. BOOLEAN LockAcquired = FALSE;
  1787. //
  1788. // Augment the heap flags
  1789. //
  1790. Flags |= Heap->ForceFlags;
  1791. //
  1792. // Check if this is a debug version of heap
  1793. //
  1794. if (DEBUG_HEAP( Flags )) {
  1795. return RtlDebugCompactHeap( HeapHandle, Flags );
  1796. }
  1797. try {
  1798. //
  1799. // Lock the heap
  1800. //
  1801. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1802. RtlAcquireLockRoutine( Heap->LockVariable );
  1803. LockAcquired = TRUE;
  1804. }
  1805. LargestFreeSize = 0;
  1806. try {
  1807. //
  1808. // Coalesce the heap into its largest free blocks possible
  1809. // and get the largest free block in the heap
  1810. //
  1811. FreeBlock = RtlpCoalesceHeap( (PHEAP)HeapHandle );
  1812. //
  1813. // If there is a free block then compute its byte size
  1814. //
  1815. if (FreeBlock != NULL) {
  1816. LargestFreeSize = FreeBlock->Size << HEAP_GRANULARITY_SHIFT;
  1817. }
  1818. //
  1819. // Scan every segment in the heap looking at its largest uncommitted
  1820. // range. Remember the largest range if its bigger than anything
  1821. // we've found so far
  1822. //
  1823. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  1824. Segment = Heap->Segments[ SegmentIndex ];
  1825. if (Segment && Segment->LargestUnCommittedRange > LargestFreeSize) {
  1826. LargestFreeSize = Segment->LargestUnCommittedRange;
  1827. }
  1828. }
  1829. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1830. SET_LAST_STATUS( GetExceptionCode() );
  1831. }
  1832. } finally {
  1833. //
  1834. // Unlock the heap
  1835. //
  1836. if (LockAcquired) {
  1837. RtlReleaseLockRoutine( Heap->LockVariable );
  1838. }
  1839. }
  1840. //
  1841. // And return the largest free size to our caller
  1842. //
  1843. return LargestFreeSize;
  1844. }
  1845. //
  1846. // Declared in nturtl.h
  1847. //
  1848. BOOLEAN
  1849. RtlValidateHeap (
  1850. PVOID HeapHandle,
  1851. IN ULONG Flags,
  1852. IN PVOID BaseAddress
  1853. )
  1854. /*++
  1855. Routine Description:
  1856. This routine verifies the structure of a heap and/or heap block
  1857. Arguments:
  1858. HeapHandle - Supplies a pointer to the heap being queried
  1859. Flags - Supplies a set of flags used to augment those already
  1860. enforced by the heap
  1861. BaseAddress - Optionally supplies a pointer to the heap block
  1862. that should be individually validated
  1863. Return Value:
  1864. BOOLEAN - TRUE if the heap/block is okay and FALSE otherwise
  1865. --*/
  1866. {
  1867. PHEAP Heap = (PHEAP)HeapHandle;
  1868. BOOLEAN LockAcquired = FALSE;
  1869. BOOLEAN Result;
  1870. try {
  1871. try {
  1872. //
  1873. // Check for the guard page version of heap
  1874. //
  1875. if ( IS_DEBUG_PAGE_HEAP_HANDLE( HeapHandle )) {
  1876. Result = RtlpDebugPageHeapValidate( HeapHandle, Flags, BaseAddress );
  1877. } else {
  1878. //
  1879. // If there is an active lookaside list then drain and remove it.
  1880. // By setting the lookaside field in the heap to null we guarantee
  1881. // that the call the free heap will not try and use the lookaside
  1882. // list logic.
  1883. //
  1884. // We'll actually capture the lookaside pointer from the heap and
  1885. // only use the captured pointer. This will take care of the
  1886. // condition where another walk or lock heap can cause us to check
  1887. // for a non null pointer and then have it become null when we read
  1888. // it again. If it is non null to start with then even if the
  1889. // user walks or locks the heap via another thread the pointer to
  1890. // still valid here so we can still try and do a lookaside list pop.
  1891. //
  1892. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  1893. if (Lookaside != NULL) {
  1894. ULONG i;
  1895. PVOID Block;
  1896. Heap->FrontEndHeap = NULL;
  1897. Heap->FrontEndHeapType = 0;
  1898. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
  1899. while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
  1900. RtlFreeHeap( HeapHandle, 0, Block );
  1901. }
  1902. }
  1903. }
  1904. Result = FALSE;
  1905. //
  1906. // Validate that HeapAddress points to a HEAP structure.
  1907. //
  1908. if (RtlpCheckHeapSignature( Heap, "RtlValidateHeap" )) {
  1909. Flags |= Heap->ForceFlags;
  1910. //
  1911. // Lock the heap
  1912. //
  1913. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1914. RtlAcquireLockRoutine( Heap->LockVariable );
  1915. LockAcquired = TRUE;
  1916. }
  1917. //
  1918. // If the user did not supply a base address then verify
  1919. // the complete heap otherwise just do a single heap
  1920. // entry
  1921. //
  1922. if (BaseAddress == NULL) {
  1923. Result = RtlpValidateHeap( Heap, TRUE );
  1924. } else {
  1925. Result = RtlpValidateHeapEntry( Heap, (PHEAP_ENTRY)BaseAddress - 1, "RtlValidateHeap" );
  1926. }
  1927. }
  1928. }
  1929. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1930. SET_LAST_STATUS( GetExceptionCode() );
  1931. Result = FALSE;
  1932. }
  1933. } finally {
  1934. //
  1935. // Unlock the heap
  1936. //
  1937. if (LockAcquired) {
  1938. RtlReleaseLockRoutine( Heap->LockVariable );
  1939. }
  1940. }
  1941. //
  1942. // And return to our caller
  1943. //
  1944. #ifndef NTOS_KERNEL_RUNTIME
  1945. if( IsHeapLogging( HeapHandle ) ) {
  1946. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1947. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1948. USHORT ReqSize = sizeof(NTDLL_EVENT_COMMON) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1949. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  1950. if(pEventHeader && pThreadLocalData) {
  1951. PNTDLL_EVENT_COMMON pHeapEvent = (PNTDLL_EVENT_COMMON)( (SIZE_T)pEventHeader
  1952. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1953. pEventHeader->Packet.Size = (USHORT) ReqSize;
  1954. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_VALIDATE;
  1955. pHeapEvent->Handle = (PVOID)HeapHandle;
  1956. ReleaseBufferLocation(pThreadLocalData);
  1957. }
  1958. }
  1959. #endif // NTOS_KERNEL_RUNTIME
  1960. return Result;
  1961. }
  1962. //
  1963. // Declared in nturtl.h
  1964. //
  1965. BOOLEAN
  1966. RtlValidateProcessHeaps (
  1967. VOID
  1968. )
  1969. /*++
  1970. Routine Description:
  1971. This routine cycles through all and validates each heap in the current
  1972. process.
  1973. Arguments:
  1974. None.
  1975. Return Value:
  1976. BOOLEAN - TRUE if all the heap verify okay and FALSE for any other
  1977. reason.
  1978. --*/
  1979. {
  1980. NTSTATUS Status;
  1981. ULONG i, NumberOfHeaps;
  1982. PVOID HeapsArray[ 512 ];
  1983. PVOID *Heaps;
  1984. SIZE_T Size;
  1985. BOOLEAN Result;
  1986. Result = TRUE;
  1987. Heaps = &HeapsArray[ 0 ];
  1988. //
  1989. // By default we can handle 512 heaps per process any more than
  1990. // that and we'll need to allocate storage to do the processing
  1991. //
  1992. // So now determine how many heaps are in the current process
  1993. //
  1994. NumberOfHeaps = RtlGetProcessHeaps( 512, Heaps );
  1995. //
  1996. // RtlGetProcessHeaps returns the number of heaps from peb.
  1997. // Can be larger than 512, as we passed for buffer
  1998. //
  1999. if (NumberOfHeaps > 512) {
  2000. //
  2001. // The number of heaps is greater than 512 so
  2002. // allocate extra memory to store the array of
  2003. // heap pointers
  2004. //
  2005. Heaps = NULL;
  2006. Size = NumberOfHeaps * sizeof( PVOID );
  2007. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  2008. (PVOID *)&Heaps,
  2009. 0,
  2010. &Size,
  2011. MEM_COMMIT,
  2012. PAGE_READWRITE );
  2013. if (!NT_SUCCESS( Status )) {
  2014. return FALSE;
  2015. }
  2016. //
  2017. // And retry getting the heaps
  2018. // Note that the number of heaps returned can be differnt now,
  2019. // because other create/destroy heap calls. We'll use the returned value.
  2020. //
  2021. NumberOfHeaps = RtlGetProcessHeaps( NumberOfHeaps, Heaps );
  2022. }
  2023. //
  2024. // Now for each heap in our heap array we'll validate
  2025. // that heap
  2026. //
  2027. for (i=0; i<NumberOfHeaps; i++) {
  2028. if (!RtlValidateHeap( Heaps[i], 0, NULL )) {
  2029. Result = FALSE;
  2030. }
  2031. }
  2032. //
  2033. // Check if we need to return the memory that we use for
  2034. // an enlarged heap array
  2035. //
  2036. if (Heaps != &HeapsArray[ 0 ]) {
  2037. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  2038. (PVOID *)&Heaps,
  2039. &Size,
  2040. MEM_RELEASE );
  2041. }
  2042. //
  2043. // And return to our caller
  2044. //
  2045. return Result;
  2046. }
  2047. //
  2048. // Declared in nturtl.h
  2049. //
  2050. ULONG
  2051. RtlGetProcessHeaps (
  2052. ULONG NumberOfHeapsToReturn,
  2053. PVOID *ProcessHeaps
  2054. )
  2055. /*++
  2056. Routine Description:
  2057. This routine determines how many individual heaps there are in the
  2058. current process and fills an array with pointers to each heap.
  2059. Arguments:
  2060. NumberOfHeapsToReturn - Indicates how many heaps the caller
  2061. is willing to accept in the second parameter
  2062. ProcessHeaps - Supplies a pointer to an array of heap pointer
  2063. to be filled in by this routine. The maximum size of this
  2064. array is specified by the first parameter
  2065. Return Value:
  2066. ULONG - Returns the smaller of the actual number of heaps in the
  2067. the process or the size of the output buffer
  2068. --*/
  2069. {
  2070. PPEB Peb = NtCurrentPeb();
  2071. ULONG NumberOfHeapsToCopy;
  2072. ULONG TotalHeaps;
  2073. RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
  2074. try {
  2075. //
  2076. // Return no more than the number of heaps currently in use
  2077. //
  2078. TotalHeaps = Peb->NumberOfHeaps;
  2079. if (TotalHeaps > NumberOfHeapsToReturn) {
  2080. NumberOfHeapsToCopy = NumberOfHeapsToReturn;
  2081. } else {
  2082. NumberOfHeapsToCopy = TotalHeaps;
  2083. }
  2084. //
  2085. // Return the heap pointers to the caller
  2086. //
  2087. RtlCopyMemory( ProcessHeaps,
  2088. Peb->ProcessHeaps,
  2089. NumberOfHeapsToCopy * sizeof( *ProcessHeaps ));
  2090. //
  2091. // Points to the PAGE_HEAP data
  2092. //
  2093. ProcessHeaps += NumberOfHeapsToCopy;
  2094. NumberOfHeapsToReturn -= NumberOfHeapsToCopy;
  2095. } finally {
  2096. RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
  2097. }
  2098. #ifdef DEBUG_PAGE_HEAP
  2099. //
  2100. // If we have debugging page heaps, go return what we can from them
  2101. //
  2102. if ( RtlpDebugPageHeap ) {
  2103. TotalHeaps +=
  2104. RtlpDebugPageHeapGetProcessHeaps( NumberOfHeapsToReturn, ProcessHeaps );
  2105. }
  2106. #endif
  2107. return TotalHeaps;
  2108. }
  2109. //
  2110. // Declared in nturtl.h
  2111. //
  2112. NTSTATUS
  2113. RtlEnumProcessHeaps (
  2114. PRTL_ENUM_HEAPS_ROUTINE EnumRoutine,
  2115. PVOID Parameter
  2116. )
  2117. /*++
  2118. Routine Description:
  2119. This routine cycles through all the heaps in a process and
  2120. invokes the specified call back routine for that heap
  2121. Arguments:
  2122. EnumRoutine - Supplies the callback to invoke for each heap
  2123. in the process
  2124. Parameter - Provides an additional parameter to pass to the
  2125. callback routine
  2126. Return Value:
  2127. NTSTATUS - returns success or the first error status returned
  2128. by the callback routine
  2129. --*/
  2130. {
  2131. PPEB Peb = NtCurrentPeb();
  2132. NTSTATUS Status;
  2133. ULONG i;
  2134. Status = STATUS_SUCCESS;
  2135. //
  2136. // Lock the heap
  2137. //
  2138. RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
  2139. try {
  2140. //
  2141. // For each heap in the process invoke the callback routine
  2142. // and if the callback returns anything other than success
  2143. // then break out and return immediately to our caller
  2144. //
  2145. for (i=0; i<Peb->NumberOfHeaps; i++) {
  2146. Status = (*EnumRoutine)( (PHEAP)(Peb->ProcessHeaps[ i ]), Parameter );
  2147. if (!NT_SUCCESS( Status )) {
  2148. break;
  2149. }
  2150. }
  2151. } finally {
  2152. //
  2153. // Unlock the heap
  2154. //
  2155. RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
  2156. }
  2157. //
  2158. // And return to our caller
  2159. //
  2160. return Status;
  2161. }
  2162. //
  2163. // Declared in nturtl.h
  2164. //
  2165. NTSTATUS
  2166. RtlUsageHeap (
  2167. IN PVOID HeapHandle,
  2168. IN ULONG Flags,
  2169. IN OUT PRTL_HEAP_USAGE Usage
  2170. )
  2171. /*++
  2172. Routine Description:
  2173. This is a rather bizzare routine. It models heap usage in that it returns
  2174. to the caller the various heap sizes, but it also return three lists. One
  2175. is a list of entries for each active allocation in the heap. The next two
  2176. are used for tracking difference between usage calls. There is a list of
  2177. what was added and a list of what was removed.
  2178. Arguments:
  2179. HeapHandle - Supplies a pointer to the heap being queried
  2180. Flags - Supplies a set of flags needed to augment those enforced
  2181. by the heap.
  2182. HEAP_USAGE_ALLOCATED_BLOCKS - Denotes that the calls wants the list
  2183. of allocated entries.
  2184. HEAP_USAGE_FREE_BUFFER - Denotes the last call to this procedure and
  2185. that any temporary storage can now be freed
  2186. Usage - Receives the current usage statistics for the heap. This variable
  2187. is also used to store state information between calls to this routine.
  2188. Return Value:
  2189. NTSTATUS - An appropriate status value. STATUS_SUCCESS if the heap has
  2190. not changed at all between calls and STATUS_MORE_ENTRIES if thep changed
  2191. between two calls.
  2192. --*/
  2193. {
  2194. NTSTATUS Status;
  2195. PHEAP Heap = (PHEAP)HeapHandle;
  2196. PRTL_HEAP_USAGE_INTERNAL Buffer;
  2197. PHEAP_SEGMENT Segment;
  2198. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
  2199. PHEAP_ENTRY CurrentBlock;
  2200. PHEAP_ENTRY_EXTRA ExtraStuff;
  2201. PLIST_ENTRY Head, Next;
  2202. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2203. SIZE_T BytesFree;
  2204. UCHAR SegmentIndex;
  2205. BOOLEAN LockAcquired = FALSE;
  2206. BOOLEAN VirtualAllocBlockSeen;
  2207. PRTL_HEAP_USAGE_ENTRY pOldEntries, pNewEntries, pNewEntry;
  2208. PRTL_HEAP_USAGE_ENTRY *ppEntries, *ppAddedEntries, *ppRemovedEntries, *pp;
  2209. PVOID DataAddress;
  2210. SIZE_T DataSize;
  2211. //
  2212. // Augment the heap flags
  2213. //
  2214. Flags |= Heap->ForceFlags;
  2215. //
  2216. // Check if we should be using the debug version of heap
  2217. //
  2218. if (DEBUG_HEAP( Flags )) {
  2219. return RtlDebugUsageHeap( HeapHandle, Flags, Usage );
  2220. }
  2221. //
  2222. // Make sure that the size of the input buffer is correct
  2223. //
  2224. if (Usage->Length != sizeof( RTL_HEAP_USAGE )) {
  2225. return STATUS_INFO_LENGTH_MISMATCH;
  2226. }
  2227. //
  2228. // Zero out the output fields
  2229. //
  2230. Usage->BytesAllocated = 0;
  2231. Usage->BytesCommitted = 0;
  2232. Usage->BytesReserved = 0;
  2233. Usage->BytesReservedMaximum = 0;
  2234. //
  2235. // Use the reserved area of the output buffer as an internal
  2236. // heap usage storage space between calls
  2237. //
  2238. Buffer = (PRTL_HEAP_USAGE_INTERNAL)&Usage->Reserved[ 0 ];
  2239. //
  2240. // Check if there is not a base buffer and we should allocate
  2241. // one then do so now
  2242. //
  2243. if ((Buffer->Base == NULL) &&
  2244. (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
  2245. Buffer->ReservedSize = 4 * 1024 * 1024;
  2246. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  2247. &Buffer->Base,
  2248. 0,
  2249. &Buffer->ReservedSize,
  2250. MEM_RESERVE,
  2251. PAGE_READWRITE );
  2252. if (!NT_SUCCESS( Status )) {
  2253. return Status;
  2254. }
  2255. Buffer->CommittedSize = 0;
  2256. Buffer->FreeList = NULL;
  2257. Buffer->LargeEntriesSentinal = NULL;
  2258. //
  2259. // Otherwise check if there already is a base buffer
  2260. // and we should free it now
  2261. //
  2262. } else if ((Buffer->Base != NULL) &&
  2263. (Flags & HEAP_USAGE_FREE_BUFFER)) {
  2264. Buffer->ReservedSize = 0;
  2265. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  2266. &Buffer->Base,
  2267. &Buffer->ReservedSize,
  2268. MEM_RELEASE );
  2269. if (!NT_SUCCESS( Status )) {
  2270. return Status;
  2271. }
  2272. RtlZeroMemory( Buffer, sizeof( *Buffer ) );
  2273. }
  2274. try {
  2275. //
  2276. // Lock the heap
  2277. //
  2278. if (!(Flags & HEAP_NO_SERIALIZE)) {
  2279. RtlAcquireLockRoutine( Heap->LockVariable );
  2280. LockAcquired = TRUE;
  2281. }
  2282. //
  2283. // Scan through the heap segments and for every in-use segment
  2284. // we add it to the amount of committed and reserved bytes
  2285. // If the segment is not in use and the heap is growable then
  2286. // we just add it to the reserved maximum
  2287. //
  2288. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  2289. Segment = Heap->Segments[ SegmentIndex ];
  2290. if (Segment) {
  2291. Usage->BytesCommitted += (Segment->NumberOfPages -
  2292. Segment->NumberOfUnCommittedPages) * PAGE_SIZE;
  2293. Usage->BytesReserved += Segment->NumberOfPages * PAGE_SIZE;
  2294. } else if (Heap->Flags & HEAP_GROWABLE) {
  2295. Usage->BytesReservedMaximum += Heap->SegmentReserve;
  2296. }
  2297. }
  2298. Usage->BytesReservedMaximum += Usage->BytesReserved;
  2299. Usage->BytesAllocated = Usage->BytesCommitted - (Heap->TotalFreeSize << HEAP_GRANULARITY_SHIFT);
  2300. //
  2301. // Scan through the big allocations and add those amounts to the
  2302. // usage statistics
  2303. //
  2304. Head = &Heap->VirtualAllocdBlocks;
  2305. Next = Head->Flink;
  2306. while (Head != Next) {
  2307. VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
  2308. Usage->BytesAllocated += VirtualAllocBlock->CommitSize;
  2309. Usage->BytesCommitted += VirtualAllocBlock->CommitSize;
  2310. Next = Next->Flink;
  2311. }
  2312. Status = STATUS_SUCCESS;
  2313. //
  2314. // Now check if we have a base buffer and we are suppose to account
  2315. // for allocated blocks
  2316. //
  2317. if ((Buffer->Base != NULL) &&
  2318. (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
  2319. //
  2320. // Setup a pointer to the old entries, added entries, and removed
  2321. // entries in the usage struct. Also drain the added entries
  2322. // and removed entries list
  2323. //
  2324. pOldEntries = Usage->Entries;
  2325. ppEntries = &Usage->Entries;
  2326. *ppEntries = NULL;
  2327. ppAddedEntries = &Usage->AddedEntries;
  2328. while (*ppAddedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppAddedEntries )) { NOTHING; }
  2329. ppRemovedEntries = &Usage->RemovedEntries;
  2330. while (*ppRemovedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppRemovedEntries )) { NOTHING; }
  2331. //
  2332. // The way the code works is that ppEntries, ppAddedEntries, and
  2333. // ppRemovedEntries point to the tail of their respective lists. If
  2334. // the list is empty then they point to the head.
  2335. //
  2336. //
  2337. // Process every segment in the heap
  2338. //
  2339. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  2340. Segment = Heap->Segments[ SegmentIndex ];
  2341. //
  2342. // Only deal with segments that are in use
  2343. //
  2344. if (Segment) {
  2345. //
  2346. // The current block is really the first block in current
  2347. // segment. We need to special case the computation to
  2348. // account for the first heap segment.
  2349. //
  2350. if (Segment->BaseAddress == Heap) {
  2351. CurrentBlock = &Heap->Entry;
  2352. } else {
  2353. CurrentBlock = &Segment->Entry;
  2354. }
  2355. //
  2356. // Now for every busy block in the segment we'll check if
  2357. // we need to allocate a heap usage entry and put it in the
  2358. // the entries list
  2359. //
  2360. while (CurrentBlock < Segment->LastValidEntry) {
  2361. if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
  2362. //
  2363. // Compute the users data address and size
  2364. //
  2365. DataAddress = (CurrentBlock+1);
  2366. DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
  2367. RtlpGetUnusedBytes(Heap, CurrentBlock);
  2368. keepLookingAtOldEntries:
  2369. //
  2370. // The first time through this routine will have
  2371. // both of these variables null so we'll start off
  2372. // by looking at new entries.
  2373. //
  2374. if (pOldEntries == Buffer->LargeEntriesSentinal) {
  2375. goto keepLookingAtNewEntries;
  2376. }
  2377. //
  2378. // Check if this entry hasn't changed.
  2379. //
  2380. // If the old entry is equal to this data block
  2381. // then move the old entry back to the entries
  2382. // list and go on to the next block.
  2383. //
  2384. if ((pOldEntries->Address == DataAddress) &&
  2385. (pOldEntries->Size == DataSize)) {
  2386. //
  2387. // Same block, keep in entries list
  2388. //
  2389. *ppEntries = pOldEntries;
  2390. pOldEntries = pOldEntries->Next;
  2391. ppEntries = &(*ppEntries)->Next;
  2392. *ppEntries = NULL;
  2393. //
  2394. // Check if an entry was removed
  2395. //
  2396. // If this entry is beyond the old entry then move
  2397. // the old entry to the removed entry list and keep
  2398. // looking at the old entry list without advancing
  2399. // the current data block
  2400. //
  2401. } else if (pOldEntries->Address <= DataAddress) {
  2402. *ppRemovedEntries = pOldEntries;
  2403. pOldEntries = pOldEntries->Next;
  2404. ppRemovedEntries = &(*ppRemovedEntries)->Next;
  2405. *ppRemovedEntries = NULL;
  2406. goto keepLookingAtOldEntries;
  2407. //
  2408. // Otherwise the we want to process the current data block
  2409. //
  2410. } else {
  2411. keepLookingAtNewEntries:
  2412. //
  2413. // Allocate a new heap usage entry
  2414. //
  2415. pNewEntry = NULL;
  2416. Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
  2417. if (!NT_SUCCESS( Status )) {
  2418. break;
  2419. }
  2420. //
  2421. // And fill in the new entry
  2422. //
  2423. pNewEntry->Address = DataAddress;
  2424. pNewEntry->Size = DataSize;
  2425. //
  2426. // If there is an extra stuff struct then fill it in
  2427. // with the stack backtrace, and appropriate tag index
  2428. //
  2429. if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  2430. ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock );
  2431. #if i386
  2432. pNewEntry->AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
  2433. #endif // i386
  2434. if (!IS_HEAP_TAGGING_ENABLED()) {
  2435. pNewEntry->TagIndex = 0;
  2436. } else {
  2437. pNewEntry->TagIndex = ExtraStuff->TagIndex;
  2438. }
  2439. } else {
  2440. //
  2441. // Otherwise there is no extra stuff so there is
  2442. // no backtrace and the tag is from the small index
  2443. //
  2444. #if i386
  2445. pNewEntry->AllocatorBackTraceIndex = 0;
  2446. #endif // i386
  2447. if (!IS_HEAP_TAGGING_ENABLED()) {
  2448. pNewEntry->TagIndex = 0;
  2449. } else {
  2450. pNewEntry->TagIndex = CurrentBlock->SmallTagIndex;
  2451. }
  2452. }
  2453. //
  2454. // Allocate another new heap usage entry as part of the added
  2455. // entry list
  2456. //
  2457. Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
  2458. if (!NT_SUCCESS( Status )) {
  2459. break;
  2460. }
  2461. //
  2462. // Copy over the contents of the new entry to the added entry
  2463. //
  2464. **ppAddedEntries = *pNewEntry;
  2465. //
  2466. // Advance the added entry pointer to the next slot
  2467. //
  2468. ppAddedEntries = &((*ppAddedEntries)->Next);
  2469. *ppAddedEntries = NULL;
  2470. pNewEntry->Next = NULL;
  2471. //
  2472. // Add the new entry to the entries list
  2473. //
  2474. *ppEntries = pNewEntry;
  2475. ppEntries = &pNewEntry->Next;
  2476. }
  2477. }
  2478. //
  2479. // Now advance to the next block in the segment
  2480. //
  2481. // If the next block doesn't exist then zoom through the
  2482. // uncommitted ranges in the segment until we find a
  2483. // match and can recompute the next real block
  2484. //
  2485. if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  2486. CurrentBlock += CurrentBlock->Size;
  2487. if (CurrentBlock < Segment->LastValidEntry) {
  2488. UnCommittedRange = Segment->UnCommittedRanges;
  2489. while ((UnCommittedRange != NULL) &&
  2490. (UnCommittedRange->Address != (ULONG_PTR)CurrentBlock)) {
  2491. UnCommittedRange = UnCommittedRange->Next;
  2492. }
  2493. if (UnCommittedRange == NULL) {
  2494. CurrentBlock = Segment->LastValidEntry;
  2495. } else {
  2496. CurrentBlock = (PHEAP_ENTRY)(UnCommittedRange->Address +
  2497. UnCommittedRange->Size);
  2498. }
  2499. }
  2500. } else {
  2501. //
  2502. // Otherwise the next block exists and so point
  2503. // directly at it
  2504. //
  2505. CurrentBlock += CurrentBlock->Size;
  2506. }
  2507. }
  2508. }
  2509. }
  2510. //
  2511. // At this point we've scanned through every segment in the heap
  2512. //
  2513. // The first time through we now have two lists one of entries and
  2514. // another of added entries. In each case Usage->Entries, and
  2515. // Usage->AddedEntries points to the start of the list and ppEntries,
  2516. // and ppAddedEntries points to the tail of the list. The first
  2517. // time through we has seem to have a one-to-one correspondence
  2518. // between Entries and AddedEntries, but the AddedEntries records
  2519. // do not contain anything useful
  2520. //
  2521. if (NT_SUCCESS( Status )) {
  2522. //
  2523. // Now we'll examine each big allocation, and for each big allocation
  2524. // we'll make a heap usage entry
  2525. //
  2526. Head = &Heap->VirtualAllocdBlocks;
  2527. Next = Head->Flink;
  2528. VirtualAllocBlockSeen = FALSE;
  2529. while (Head != Next) {
  2530. VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
  2531. //
  2532. // Allocate a new heap usage entry
  2533. //
  2534. pNewEntry = NULL;
  2535. Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
  2536. if (!NT_SUCCESS( Status )) {
  2537. break;
  2538. }
  2539. VirtualAllocBlockSeen = TRUE;
  2540. //
  2541. // Fill in the new heap usage entry
  2542. //
  2543. pNewEntry->Address = (VirtualAllocBlock + 1);
  2544. pNewEntry->Size = VirtualAllocBlock->CommitSize - VirtualAllocBlock->BusyBlock.Size;
  2545. #if i386
  2546. pNewEntry->AllocatorBackTraceIndex = VirtualAllocBlock->ExtraStuff.AllocatorBackTraceIndex;
  2547. #endif // i386
  2548. if (!IS_HEAP_TAGGING_ENABLED()) {
  2549. pNewEntry->TagIndex = 0;
  2550. } else {
  2551. pNewEntry->TagIndex = VirtualAllocBlock->ExtraStuff.TagIndex;
  2552. }
  2553. //
  2554. // Search the heap usage entries list until we find the address
  2555. // that right after the new entry address and then insert
  2556. // this new entry. This will keep the entries list sorted in
  2557. // assending addresses
  2558. //
  2559. //
  2560. // The first time through this function ppEntries will point
  2561. // to the tail and so *pp should actually start off as null,
  2562. // which means that the big allocation simply get tacked on
  2563. // the end of the entries list. We do not augment the
  2564. // AddedEntries list for these big allocations.
  2565. //
  2566. pp = ppEntries;
  2567. while (*pp) {
  2568. if ((*pp)->Address >= pNewEntry->Address) {
  2569. break;
  2570. }
  2571. pp = &(*pp)->Next;
  2572. }
  2573. pNewEntry->Next = *pp;
  2574. *pp = pNewEntry;
  2575. //
  2576. // Get the next big allocation block
  2577. //
  2578. Next = Next->Flink;
  2579. }
  2580. //
  2581. // At this point we've scanned through the heap segments and the
  2582. // big allocations.
  2583. //
  2584. // The first time through this procedure we have built two lists
  2585. // the Entries and the AddedEntries
  2586. //
  2587. if (NT_SUCCESS( Status )) {
  2588. pOldEntries = Buffer->LargeEntriesSentinal;
  2589. Buffer->LargeEntriesSentinal = *ppEntries;
  2590. //
  2591. // Now we'll process the previous large entries sentinal list
  2592. //
  2593. // This path is not taken the first time through this procedure
  2594. //
  2595. while (pOldEntries != NULL) {
  2596. //
  2597. // If we have new entries and the entry is equal to the
  2598. // entry in the previous large sentinal list then
  2599. // we move one down on the new list and remove the previous
  2600. // sentinal entry
  2601. //
  2602. if ((*ppEntries != NULL) &&
  2603. (pOldEntries->Address == (*ppEntries)->Address) &&
  2604. (pOldEntries->Size == (*ppEntries)->Size)) {
  2605. ppEntries = &(*ppEntries)->Next;
  2606. pOldEntries = RtlpFreeHeapUsageEntry( Buffer, pOldEntries );
  2607. //
  2608. // If we do now have any new entries or the previous
  2609. // sentinal entry is comes before this new entry then
  2610. // we'll add the sentinal entry to the remove list
  2611. //
  2612. } else if ((*ppEntries == NULL) ||
  2613. (pOldEntries->Address < (*ppEntries)->Address)) {
  2614. *ppRemovedEntries = pOldEntries;
  2615. pOldEntries = pOldEntries->Next;
  2616. ppRemovedEntries = &(*ppRemovedEntries)->Next;
  2617. *ppRemovedEntries = NULL;
  2618. //
  2619. // Otherwise the old sentinal entry is put on the added
  2620. // entries list
  2621. //
  2622. } else {
  2623. *ppAddedEntries = pOldEntries;
  2624. pOldEntries = pOldEntries->Next;
  2625. **ppAddedEntries = **ppEntries;
  2626. ppAddedEntries = &(*ppAddedEntries)->Next;
  2627. *ppAddedEntries = NULL;
  2628. }
  2629. }
  2630. //
  2631. // This path is not taken the first time through this procedure
  2632. //
  2633. while (pNewEntry = *ppEntries) {
  2634. Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
  2635. if (!NT_SUCCESS( Status )) {
  2636. break;
  2637. }
  2638. **ppAddedEntries = *pNewEntry;
  2639. ppAddedEntries = &(*ppAddedEntries)->Next;
  2640. *ppAddedEntries = NULL;
  2641. ppEntries = &pNewEntry->Next;
  2642. }
  2643. //
  2644. // Tell the user that something has changed between the
  2645. // previous call and this one
  2646. //
  2647. if ((Usage->AddedEntries != NULL) || (Usage->RemovedEntries != NULL)) {
  2648. Status = STATUS_MORE_ENTRIES;
  2649. }
  2650. }
  2651. }
  2652. }
  2653. } finally {
  2654. //
  2655. // Unlock the heap
  2656. //
  2657. if (LockAcquired) {
  2658. RtlReleaseLockRoutine( Heap->LockVariable );
  2659. }
  2660. }
  2661. //
  2662. // And return to our caller
  2663. //
  2664. return Status;
  2665. }
  2666. //
  2667. // Declared in nturtl.h
  2668. //
  2669. NTSTATUS
  2670. RtlWalkHeap (
  2671. IN PVOID HeapHandle,
  2672. IN OUT PRTL_HEAP_WALK_ENTRY Entry
  2673. )
  2674. /*++
  2675. Routine Description:
  2676. This routine is used to enumerate all the entries within a heap. For each
  2677. call it returns a new information in entry.
  2678. Arguments:
  2679. HeapHandle - Supplies a pointer to the heap being queried
  2680. Entry - Supplies storage for the entry information. If the DataAddress field
  2681. is null then the enumeration starts over from the beginning otherwise it
  2682. resumes from where it left off
  2683. Return Value:
  2684. NTSTATUS - An appropriate status value
  2685. --*/
  2686. {
  2687. NTSTATUS Status;
  2688. PHEAP Heap = (PHEAP)HeapHandle;
  2689. PHEAP_SEGMENT Segment;
  2690. UCHAR SegmentIndex;
  2691. PHEAP_ENTRY CurrentBlock;
  2692. PHEAP_ENTRY_EXTRA ExtraStuff;
  2693. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
  2694. PLIST_ENTRY Next, Head;
  2695. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2696. //
  2697. // Check if we should be using the guard page verion of heap
  2698. //
  2699. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  2700. RtlpDebugPageHeapWalk( HeapHandle, Entry ));
  2701. //
  2702. // If this is the debug version of heap then validate the heap
  2703. // before we go on
  2704. //
  2705. if (DEBUG_HEAP( Heap->Flags )) {
  2706. if (!RtlDebugWalkHeap( HeapHandle, Entry )) {
  2707. return STATUS_INVALID_PARAMETER;
  2708. }
  2709. }
  2710. Status = STATUS_SUCCESS;
  2711. //
  2712. // If there is an active lookaside list then drain and remove it.
  2713. // By setting the lookaside field in the heap to null we guarantee
  2714. // that the call the free heap will not try and use the lookaside
  2715. // list logic.
  2716. //
  2717. // We'll actually capture the lookaside pointer from the heap and
  2718. // only use the captured pointer. This will take care of the
  2719. // condition where another walk or lock heap can cause us to check
  2720. // for a non null pointer and then have it become null when we read
  2721. // it again. If it is non null to start with then even if the
  2722. // user walks or locks the heap via another thread the pointer to
  2723. // still valid here so we can still try and do a lookaside list pop.
  2724. //
  2725. {
  2726. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  2727. if (Lookaside != NULL) {
  2728. ULONG i;
  2729. PVOID Block;
  2730. Heap->FrontEndHeap = NULL;
  2731. Heap->FrontEndHeapType = 0;
  2732. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
  2733. while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
  2734. RtlFreeHeap( HeapHandle, 0, Block );
  2735. }
  2736. }
  2737. }
  2738. }
  2739. //
  2740. // Check if this is the first time we've been called to walk the heap
  2741. //
  2742. if (Entry->DataAddress == NULL) {
  2743. //
  2744. // Start with the first segement in the heap
  2745. //
  2746. SegmentIndex = 0;
  2747. nextSegment:
  2748. CurrentBlock = NULL;
  2749. //
  2750. // Now find the next in use segment for the heap
  2751. //
  2752. Segment = NULL;
  2753. while ((SegmentIndex < HEAP_MAXIMUM_SEGMENTS) &&
  2754. ((Segment = Heap->Segments[ SegmentIndex ]) == NULL)) {
  2755. SegmentIndex += 1;
  2756. }
  2757. //
  2758. // If there are no more valid segments then we'll try the big
  2759. // allocation
  2760. //
  2761. if (Segment == NULL) {
  2762. Head = &Heap->VirtualAllocdBlocks;
  2763. Next = Head->Flink;
  2764. if (Next == Head) {
  2765. Status = STATUS_NO_MORE_ENTRIES;
  2766. } else {
  2767. VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
  2768. CurrentBlock = &VirtualAllocBlock->BusyBlock;
  2769. }
  2770. //
  2771. // Otherwise we'll grab information about the segment. Note that
  2772. // the current block is still null so when we fall out of this
  2773. // block we'll return directly to our caller with this segment
  2774. // information
  2775. //
  2776. } else {
  2777. Entry->DataAddress = Segment;
  2778. Entry->DataSize = 0;
  2779. Entry->OverheadBytes = sizeof( *Segment );
  2780. Entry->Flags = RTL_HEAP_SEGMENT;
  2781. Entry->SegmentIndex = SegmentIndex;
  2782. Entry->Segment.CommittedSize = (Segment->NumberOfPages -
  2783. Segment->NumberOfUnCommittedPages) * PAGE_SIZE;
  2784. Entry->Segment.UnCommittedSize = Segment->NumberOfUnCommittedPages * PAGE_SIZE;
  2785. Entry->Segment.FirstEntry = (Segment->FirstEntry->Flags & HEAP_ENTRY_BUSY) ?
  2786. ((PHEAP_ENTRY)Segment->FirstEntry + 1) :
  2787. (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Segment->FirstEntry + 1);
  2788. Entry->Segment.LastEntry = Segment->LastValidEntry;
  2789. }
  2790. //
  2791. // This is not the first time through. Check if last time we gave back
  2792. // an heap segement or an uncommitted range
  2793. //
  2794. } else if (Entry->Flags & (RTL_HEAP_SEGMENT | RTL_HEAP_UNCOMMITTED_RANGE)) {
  2795. //
  2796. // Check that the segment index is still valid
  2797. //
  2798. if ((SegmentIndex = Entry->SegmentIndex) >= HEAP_MAXIMUM_SEGMENTS) {
  2799. Status = STATUS_INVALID_ADDRESS;
  2800. CurrentBlock = NULL;
  2801. } else {
  2802. //
  2803. // Check that the segment is still in use
  2804. //
  2805. Segment = Heap->Segments[ SegmentIndex ];
  2806. if (Segment == NULL) {
  2807. Status = STATUS_INVALID_ADDRESS;
  2808. CurrentBlock = NULL;
  2809. //
  2810. // The segment is still in use if what we returned last time
  2811. // as the segment header then this time we'll return the
  2812. // segments first entry
  2813. //
  2814. } else if (Entry->Flags & RTL_HEAP_SEGMENT) {
  2815. CurrentBlock = (PHEAP_ENTRY)Segment->FirstEntry;
  2816. //
  2817. // Otherwise what we returned last time as an uncommitted
  2818. // range so now we need to get the next block
  2819. //
  2820. } else {
  2821. CurrentBlock = (PHEAP_ENTRY)((PCHAR)Entry->DataAddress + Entry->DataSize);
  2822. //
  2823. // Check if we are beyond this segment and need to get the
  2824. // next one
  2825. //
  2826. if (CurrentBlock >= Segment->LastValidEntry) {
  2827. SegmentIndex += 1;
  2828. goto nextSegment;
  2829. }
  2830. }
  2831. }
  2832. //
  2833. // Otherwise this is not the first time through and last time we gave back a
  2834. // valid heap entry
  2835. //
  2836. } else {
  2837. //
  2838. // Check if the last entry we gave back was in use
  2839. //
  2840. if (Entry->Flags & HEAP_ENTRY_BUSY) {
  2841. //
  2842. // Get the last entry we returned
  2843. //
  2844. CurrentBlock = ((PHEAP_ENTRY)Entry->DataAddress - 1);
  2845. //
  2846. // If the last entry was for a big allocation then
  2847. // get the next big block if there is one otherwise
  2848. // say there are no more entries
  2849. //
  2850. if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  2851. Head = &Heap->VirtualAllocdBlocks;
  2852. VirtualAllocBlock = CONTAINING_RECORD( CurrentBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  2853. Next = VirtualAllocBlock->Entry.Flink;
  2854. if (Next == Head) {
  2855. Status = STATUS_NO_MORE_ENTRIES;
  2856. } else {
  2857. VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
  2858. CurrentBlock = &VirtualAllocBlock->BusyBlock;
  2859. }
  2860. //
  2861. // Our previous result is a busy normal block
  2862. //
  2863. } else {
  2864. if ( CurrentBlock->SegmentIndex == HEAP_LFH_INDEX ) {
  2865. //
  2866. // Get the block size from the sub-segment. N.B the block size there
  2867. // is in heap units as well.
  2868. //
  2869. PHEAP_ENTRY NextBlock = CurrentBlock + ((PHEAP_SUBSEGMENT)CurrentBlock->SubSegment)->BlockSize;
  2870. if (NextBlock->SegmentIndex == HEAP_LFH_INDEX) {
  2871. CurrentBlock = NextBlock;
  2872. goto SETCRTBLOCK;
  2873. }
  2874. //
  2875. // We finished the metablock. We need to jump back to the metablock header
  2876. //
  2877. CurrentBlock = ((PHEAP_ENTRY)((PHEAP_SUBSEGMENT)CurrentBlock->SubSegment)->UserBlocks) - 1;
  2878. }
  2879. //
  2880. // Get the segment and make sure it is still valid and in use
  2881. //
  2882. Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
  2883. if (Segment == NULL) {
  2884. Status = STATUS_INVALID_ADDRESS;
  2885. CurrentBlock = NULL;
  2886. //
  2887. // The segment is still in use, check if what we returned
  2888. // previously was a last entry
  2889. //
  2890. } else if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  2891. findUncommittedRange:
  2892. //
  2893. // We are at a last entry so now if the segment is done
  2894. // then go get another segment
  2895. //
  2896. CurrentBlock += CurrentBlock->Size;
  2897. if (CurrentBlock >= Segment->LastValidEntry) {
  2898. SegmentIndex += 1;
  2899. goto nextSegment;
  2900. }
  2901. //
  2902. // Otherwise we will find the uncommitted range entry that
  2903. // immediately follows this last entry
  2904. //
  2905. pp = &Segment->UnCommittedRanges;
  2906. while ((UnCommittedRange = *pp) && UnCommittedRange->Address != (ULONG_PTR)CurrentBlock ) {
  2907. pp = &UnCommittedRange->Next;
  2908. }
  2909. if (UnCommittedRange == NULL) {
  2910. Status = STATUS_INVALID_PARAMETER;
  2911. } else {
  2912. //
  2913. // Now fill in the entry to denote that uncommitted
  2914. // range information
  2915. //
  2916. Entry->DataAddress = (PVOID)UnCommittedRange->Address;
  2917. Entry->DataSize = UnCommittedRange->Size;
  2918. Entry->OverheadBytes = 0;
  2919. Entry->SegmentIndex = SegmentIndex;
  2920. Entry->Flags = RTL_HEAP_UNCOMMITTED_RANGE;
  2921. }
  2922. //
  2923. // Null out the current block because we've just filled in
  2924. // the entry
  2925. //
  2926. CurrentBlock = NULL;
  2927. } else {
  2928. //
  2929. // Otherwise the entry has a following entry so now
  2930. // advance to the next entry
  2931. //
  2932. CurrentBlock += CurrentBlock->Size;
  2933. }
  2934. }
  2935. //
  2936. // Otherwise the previous entry we returned is not in use
  2937. //
  2938. } else {
  2939. //
  2940. // Get the last entry we returned
  2941. //
  2942. CurrentBlock = (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Entry->DataAddress - 1);
  2943. if ( CurrentBlock->SegmentIndex == HEAP_LFH_INDEX ) {
  2944. //
  2945. //
  2946. // We finished the metablock. We need to jump back to the metablock header
  2947. // we get the block size from the sub-segment. N.B the block size there
  2948. // is in heap units as well.
  2949. //
  2950. PHEAP_ENTRY NextBlock = CurrentBlock + ((PHEAP_SUBSEGMENT)CurrentBlock->SubSegment)->BlockSize;
  2951. if (NextBlock->SegmentIndex == HEAP_LFH_INDEX) {
  2952. CurrentBlock = NextBlock;
  2953. goto SETCRTBLOCK;
  2954. }
  2955. CurrentBlock = ((PHEAP_ENTRY)((PHEAP_SUBSEGMENT)CurrentBlock->SubSegment)->UserBlocks) - 1;
  2956. }
  2957. //
  2958. // Get the segment and make sure it it still valid and in use
  2959. //
  2960. Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
  2961. if (Segment == NULL) {
  2962. Status = STATUS_INVALID_ADDRESS;
  2963. CurrentBlock = NULL;
  2964. //
  2965. // If the block is the last entry then go find the next uncommitted
  2966. // range or segment
  2967. //
  2968. } else if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  2969. goto findUncommittedRange;
  2970. //
  2971. // Otherwise we'll just move on to the next entry
  2972. //
  2973. } else {
  2974. CurrentBlock += CurrentBlock->Size;
  2975. }
  2976. }
  2977. }
  2978. SETCRTBLOCK:
  2979. //
  2980. // At this point if current block is not null then we've found another
  2981. // entry to return. We could also have found a segment or uncommitted
  2982. // range but those are handled separately above and keep current block
  2983. // null
  2984. //
  2985. if (CurrentBlock != NULL) {
  2986. if (RtlpGetLowFragHeap(Heap)
  2987. &&
  2988. (CurrentBlock->Size > ((sizeof(HEAP_USERDATA_HEADER) + sizeof(HEAP_ENTRY)) >> HEAP_GRANULARITY_SHIFT))
  2989. &&
  2990. (((PHEAP_USERDATA_HEADER)(CurrentBlock + 1))->Signature == HEAP_LFH_USER_SIGNATURE)) {
  2991. CurrentBlock = (PHEAP_ENTRY)((ULONG_PTR)CurrentBlock + sizeof(HEAP_USERDATA_HEADER) + sizeof(HEAP_ENTRY));
  2992. }
  2993. //
  2994. // Check if the block is in use
  2995. //
  2996. if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
  2997. //
  2998. // Fill in the entry field for this block
  2999. //
  3000. Entry->DataAddress = (CurrentBlock+1);
  3001. if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  3002. Entry->DataSize = RtlpGetSizeOfBigBlock( CurrentBlock );
  3003. Entry->OverheadBytes = (UCHAR)( sizeof( *VirtualAllocBlock ) + CurrentBlock->Size);
  3004. Entry->SegmentIndex = HEAP_MAXIMUM_SEGMENTS;
  3005. Entry->Flags = RTL_HEAP_BUSY | HEAP_ENTRY_VIRTUAL_ALLOC;
  3006. } else {
  3007. Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
  3008. RtlpGetUnusedBytes(Heap, CurrentBlock);
  3009. //
  3010. // OverheadBytes can't hold bore than 256 values. We copy then
  3011. // the UnusedBytes value, even if the block actually has more than this
  3012. //
  3013. Entry->OverheadBytes = CurrentBlock->UnusedBytes;
  3014. Entry->SegmentIndex = CurrentBlock->SegmentIndex;
  3015. Entry->Flags = RTL_HEAP_BUSY;
  3016. }
  3017. if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  3018. ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock );
  3019. Entry->Block.Settable = ExtraStuff->Settable;
  3020. #if i386
  3021. Entry->Block.AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
  3022. #endif // i386
  3023. if (!IS_HEAP_TAGGING_ENABLED()) {
  3024. Entry->Block.TagIndex = 0;
  3025. } else {
  3026. Entry->Block.TagIndex = ExtraStuff->TagIndex;
  3027. }
  3028. Entry->Flags |= RTL_HEAP_SETTABLE_VALUE;
  3029. } else {
  3030. if (!IS_HEAP_TAGGING_ENABLED()) {
  3031. Entry->Block.TagIndex = 0;
  3032. } else {
  3033. Entry->Block.TagIndex = CurrentBlock->SmallTagIndex;
  3034. }
  3035. }
  3036. Entry->Flags |= CurrentBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS;
  3037. //
  3038. // Otherwise the block is not in use
  3039. //
  3040. } else {
  3041. Entry->DataAddress = ((PHEAP_FREE_ENTRY)CurrentBlock+1);
  3042. Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
  3043. sizeof( HEAP_FREE_ENTRY );
  3044. Entry->OverheadBytes = sizeof( HEAP_FREE_ENTRY );
  3045. Entry->SegmentIndex = CurrentBlock->SegmentIndex;
  3046. Entry->Flags = 0;
  3047. }
  3048. }
  3049. #ifndef NTOS_KERNEL_RUNTIME
  3050. if( IsHeapLogging( HeapHandle ) ) {
  3051. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  3052. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  3053. USHORT ReqSize = sizeof(NTDLL_EVENT_COMMON) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  3054. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  3055. if(pEventHeader && pThreadLocalData) {
  3056. PNTDLL_EVENT_COMMON pHeapEvent = (PNTDLL_EVENT_COMMON)( (SIZE_T)pEventHeader
  3057. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  3058. pEventHeader->Packet.Size = (USHORT) ReqSize;
  3059. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_WALK;
  3060. pHeapEvent->Handle = (PVOID)HeapHandle;
  3061. ReleaseBufferLocation(pThreadLocalData);
  3062. }
  3063. }
  3064. #endif // NTOS_KERNEL_RUNTIME
  3065. //
  3066. // And return to our caller
  3067. //
  3068. return Status;
  3069. }
  3070. //
  3071. // Declared in heappriv.h
  3072. //
  3073. BOOLEAN
  3074. RtlpCheckHeapSignature (
  3075. IN PHEAP Heap,
  3076. IN PCHAR Caller
  3077. )
  3078. /*++
  3079. Routine Description:
  3080. This routine verifies that it is being called with a properly identified
  3081. heap.
  3082. Arguments:
  3083. Heap - Supplies a pointer to the heap being checked
  3084. Caller - Supplies a string that can be used to identify the caller
  3085. Return Value:
  3086. BOOLEAN - TRUE if the heap signature is present, and FALSE otherwise
  3087. --*/
  3088. {
  3089. //
  3090. // If the heap signature matches then that is the only
  3091. // checking we do
  3092. //
  3093. if (Heap->Signature == HEAP_SIGNATURE) {
  3094. return TRUE;
  3095. } else {
  3096. //
  3097. // We have a bad heap signature. Print out some information, break
  3098. // into the debugger, and then return false
  3099. //
  3100. HeapDebugPrint(( "Invalid heap signature for heap at %x", Heap ));
  3101. if (Caller != NULL) {
  3102. DbgPrint( ", passed to %s", Caller );
  3103. }
  3104. DbgPrint( "\n" );
  3105. HeapDebugBreak( &Heap->Signature );
  3106. return FALSE;
  3107. }
  3108. }
  3109. //
  3110. // Declared in heappriv.h
  3111. //
  3112. PHEAP_FREE_ENTRY
  3113. RtlpCoalesceHeap (
  3114. IN PHEAP Heap
  3115. )
  3116. /*++
  3117. Routine Description:
  3118. This routine scans through heap and coalesces its free blocks
  3119. Arguments:
  3120. Heap - Supplies a pointer to the heap being modified
  3121. Return Value:
  3122. PHEAP_FREE_ENTRY - returns a pointer to the largest free block
  3123. in the heap
  3124. --*/
  3125. {
  3126. SIZE_T OldFreeSize;
  3127. SIZE_T FreeSize;
  3128. ULONG n;
  3129. PHEAP_FREE_ENTRY FreeBlock, LargestFreeBlock;
  3130. PLIST_ENTRY FreeListHead, Next;
  3131. RTL_PAGED_CODE();
  3132. LargestFreeBlock = NULL;
  3133. //
  3134. // For every free list in the heap, going from smallest to
  3135. // largest and skipping the zero index one we will
  3136. // scan the free list coalesceing the free blocks
  3137. //
  3138. FreeListHead = &Heap->FreeLists[ 1 ];
  3139. n = HEAP_MAXIMUM_FREELISTS;
  3140. while (n--) {
  3141. //
  3142. // Scan the individual free list
  3143. //
  3144. Next = FreeListHead->Blink;
  3145. while (FreeListHead != Next) {
  3146. //
  3147. // Get a pointer to the current free list entry, and remember its
  3148. // next and size
  3149. //
  3150. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  3151. Next = Next->Flink;
  3152. OldFreeSize = FreeSize = FreeBlock->Size;
  3153. //
  3154. // Coalesce the block
  3155. //
  3156. FreeBlock = RtlpCoalesceFreeBlocks( Heap,
  3157. FreeBlock,
  3158. &FreeSize,
  3159. TRUE );
  3160. //
  3161. // If the new free size is not equal to the old free size
  3162. // then we actually did some changes otherwise the coalesce
  3163. // calll was essentialy a noop
  3164. //
  3165. if (FreeSize != OldFreeSize) {
  3166. //
  3167. // Check if we should decommit this block because it is too
  3168. // large and it is either at the beginning or end of a
  3169. // committed run. Otherwise just insert the new sized
  3170. // block into its corresponding free list. We'll hit this
  3171. // block again when we visit larger free lists.
  3172. //
  3173. if (FreeBlock->Size >= (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT)
  3174. &&
  3175. (FreeBlock->PreviousSize == 0 ||
  3176. (FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY))) {
  3177. RtlpDeCommitFreeBlock( Heap, FreeBlock, FreeSize );
  3178. } else {
  3179. RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
  3180. }
  3181. Next = FreeListHead->Blink;
  3182. } else {
  3183. //
  3184. // Remember the largest free block we've found so far
  3185. //
  3186. if ((LargestFreeBlock == NULL) ||
  3187. (LargestFreeBlock->Size < FreeBlock->Size)) {
  3188. LargestFreeBlock = FreeBlock;
  3189. }
  3190. }
  3191. }
  3192. //
  3193. // Go to the next free list. When we hit the largest dedicated
  3194. // size free list we'll fall back to the [0] index list
  3195. //
  3196. if (n == 1) {
  3197. FreeListHead = &Heap->FreeLists[ 0 ];
  3198. } else {
  3199. FreeListHead++;
  3200. }
  3201. }
  3202. //
  3203. // And return to our caller
  3204. //
  3205. return LargestFreeBlock;
  3206. }
  3207. //
  3208. // Declared in heappriv.h
  3209. //
  3210. VOID
  3211. RtlpAddHeapToProcessList (
  3212. IN PHEAP Heap
  3213. )
  3214. /*++
  3215. Routine Description:
  3216. This routine adds the specified heap to the heap list for the
  3217. current process
  3218. Arguments:
  3219. Heap - Supplies a pointer to the heap being added
  3220. Return Value:
  3221. None.
  3222. --*/
  3223. {
  3224. PPEB Peb = NtCurrentPeb();
  3225. PHEAP *NewList;
  3226. //
  3227. // Lock the processes heap list
  3228. //
  3229. RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
  3230. try {
  3231. //
  3232. // If the processes heap list is already full then we'll
  3233. // double the size of the heap list for the process
  3234. //
  3235. if (Peb->NumberOfHeaps == Peb->MaximumNumberOfHeaps) {
  3236. //
  3237. // Double the size
  3238. //
  3239. Peb->MaximumNumberOfHeaps *= 2;
  3240. //
  3241. // Allocate space for the new list
  3242. //
  3243. NewList = RtlAllocateHeap( RtlProcessHeap(),
  3244. 0,
  3245. Peb->MaximumNumberOfHeaps * sizeof( *NewList ));
  3246. if (NewList == NULL) {
  3247. //
  3248. // We can't allocate space for the new list. Restore then
  3249. // the previous value for MaximumNumberOfHeaps
  3250. //
  3251. Peb->MaximumNumberOfHeaps = Peb->NumberOfHeaps;
  3252. leave;
  3253. }
  3254. //
  3255. // Copy over the old buffer to the new buffer
  3256. //
  3257. RtlCopyMemory( NewList,
  3258. Peb->ProcessHeaps,
  3259. Peb->NumberOfHeaps * sizeof( *NewList ));
  3260. //
  3261. // Check if we should free the previous heap list buffer
  3262. //
  3263. if (Peb->ProcessHeaps != RtlpProcessHeapsListBuffer) {
  3264. RtlFreeHeap( RtlProcessHeap(), 0, Peb->ProcessHeaps );
  3265. }
  3266. //
  3267. // Set the new list
  3268. //
  3269. Peb->ProcessHeaps = NewList;
  3270. }
  3271. //
  3272. // Add the input heap to the next free heap list slot, and note that
  3273. // the processes heap list index is really one beyond the actualy
  3274. // index used to get the processes heap
  3275. //
  3276. Peb->ProcessHeaps[ Peb->NumberOfHeaps++ ] = Heap;
  3277. Heap->ProcessHeapsListIndex = (USHORT)Peb->NumberOfHeaps;
  3278. } finally {
  3279. //
  3280. // Unlock the processes heap list
  3281. //
  3282. RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
  3283. }
  3284. //
  3285. // And return to our caller
  3286. //
  3287. return;
  3288. }
  3289. //
  3290. // Delcared in heappriv.h
  3291. //
  3292. VOID
  3293. RtlpRemoveHeapFromProcessList (
  3294. IN PHEAP Heap
  3295. )
  3296. /*++
  3297. Routine Description:
  3298. This routine removes the specified heap to the heap list for the
  3299. current process
  3300. Arguments:
  3301. Heap - Supplies a pointer to the heap being removed
  3302. Return Value:
  3303. None.
  3304. --*/
  3305. {
  3306. PPEB Peb = NtCurrentPeb();
  3307. PHEAP *p, *p1;
  3308. ULONG n;
  3309. //
  3310. // Lock the current processes heap list lock
  3311. //
  3312. RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
  3313. try {
  3314. //
  3315. // We only want to the the work if the current process actually has some
  3316. // heaps, the index stored in the heap is within the range for active
  3317. // heaps. Note that the heaps stored index is bias by one.
  3318. //
  3319. if ((Peb->NumberOfHeaps != 0) &&
  3320. (Heap->ProcessHeapsListIndex != 0) &&
  3321. (Heap->ProcessHeapsListIndex <= Peb->NumberOfHeaps)) {
  3322. //
  3323. // Establish a pointer into the array of process heaps at the
  3324. // current heap location and one beyond
  3325. //
  3326. p = (PHEAP *)&Peb->ProcessHeaps[ Heap->ProcessHeapsListIndex - 1 ];
  3327. p1 = p + 1;
  3328. //
  3329. // Calculate the number of heaps that exist beyond the current
  3330. // heap in the array including the current heap location
  3331. //
  3332. n = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
  3333. //
  3334. // For every heap beyond the current one that we are removing
  3335. // we'll move that heap down to the previous index.
  3336. //
  3337. while (--n) {
  3338. //
  3339. // Copy the heap process array entry of the next entry to
  3340. // the current entry, and move p1 to the next next entry
  3341. //
  3342. *p = *p1++;
  3343. //
  3344. // This is simply a debugging call
  3345. //
  3346. RtlpUpdateHeapListIndex( (*p)->ProcessHeapsListIndex,
  3347. (USHORT)((*p)->ProcessHeapsListIndex - 1));
  3348. //
  3349. // Assign the moved heap its new heap index
  3350. //
  3351. #if 0
  3352. if (RtlpDebugPageHeap) {
  3353. PVOID ProtectAddress;
  3354. ULONG OldProtect;
  3355. ULONG NewProtect;
  3356. NTSTATUS Status;
  3357. SIZE_T Size;
  3358. ProtectAddress = *p;
  3359. Size = PAGE_SIZE;
  3360. NewProtect = PAGE_READWRITE;
  3361. Status = ZwProtectVirtualMemory( NtCurrentProcess(),
  3362. &ProtectAddress,
  3363. &Size,
  3364. NewProtect,
  3365. &OldProtect );
  3366. if (! NT_SUCCESS(Status)) {
  3367. DbgPrint ("Page heap: Failing to change protection in heap destroy (%x) \n",
  3368. Status);
  3369. }
  3370. (*p)->ProcessHeapsListIndex -= 1;
  3371. ProtectAddress = *p;
  3372. Size = PAGE_SIZE;
  3373. NewProtect = OldProtect;
  3374. Status = ZwProtectVirtualMemory( NtCurrentProcess(),
  3375. &ProtectAddress,
  3376. &Size,
  3377. NewProtect,
  3378. &OldProtect );
  3379. }
  3380. else {
  3381. (*p)->ProcessHeapsListIndex -= 1;
  3382. }
  3383. #else
  3384. (*p)->ProcessHeapsListIndex -= 1;
  3385. #endif
  3386. //
  3387. // Move on to the next heap entry
  3388. //
  3389. p += 1;
  3390. }
  3391. //
  3392. // Zero out the last process heap pointer, update the count, and
  3393. // make the heap we just removed realize it has been removed by
  3394. // zeroing out its process heap list index
  3395. //
  3396. Peb->ProcessHeaps[ --Peb->NumberOfHeaps ] = NULL;
  3397. Heap->ProcessHeapsListIndex = 0;
  3398. }
  3399. } finally {
  3400. //
  3401. // Unlock the current processes heap list lock
  3402. //
  3403. RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
  3404. }
  3405. return;
  3406. }
  3407. //
  3408. // Local Support routine
  3409. //
  3410. BOOLEAN
  3411. RtlpGrowBlockInPlace (
  3412. IN PHEAP Heap,
  3413. IN ULONG Flags,
  3414. IN PHEAP_ENTRY BusyBlock,
  3415. IN SIZE_T Size,
  3416. IN SIZE_T AllocationIndex
  3417. )
  3418. /*++
  3419. Routine Description:
  3420. This routine will try and grow a heap allocation block at its current
  3421. location
  3422. Arguments:
  3423. Heap - Supplies a pointer to the heap being modified
  3424. Flags - Supplies a set of flags to augment those already enforced by
  3425. the heap
  3426. BusyBlock - Supplies a pointer to the block being resized
  3427. Size - Supplies the size, in bytes, needed by the resized block
  3428. AllocationIndex - Supplies the allocation index for the resized block
  3429. Note that the size variable has not been rounded up to the next
  3430. granular block size, but that allocation index has.
  3431. Return Value:
  3432. BOOLEAN - TRUE if the block has been resized and FALSE otherwise
  3433. --*/
  3434. {
  3435. SIZE_T FreeSize;
  3436. SIZE_T OldSize;
  3437. UCHAR EntryFlags, FreeFlags;
  3438. PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
  3439. PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
  3440. //
  3441. // Check if the allocation index is too large for even the nondedicated
  3442. // free list (i.e., too large for list [0])
  3443. //
  3444. if (AllocationIndex > Heap->VirtualMemoryThreshold) {
  3445. return FALSE;
  3446. }
  3447. //
  3448. // Get the flags for the current block and a pointer to the next
  3449. // block following the current block
  3450. //
  3451. EntryFlags = BusyBlock->Flags;
  3452. FreeBlock = (PHEAP_FREE_ENTRY)(BusyBlock + BusyBlock->Size);
  3453. //
  3454. // If the current block is the last entry before an uncommitted range
  3455. // we'll try and extend the uncommitted range to fit our new allocation
  3456. //
  3457. if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
  3458. //
  3459. // Calculate how must more we need beyond the current block
  3460. // size
  3461. //
  3462. FreeSize = (AllocationIndex - BusyBlock->Size) << HEAP_GRANULARITY_SHIFT;
  3463. FreeSize = ROUND_UP_TO_POWER2( FreeSize, PAGE_SIZE );
  3464. //
  3465. // Try and commit memory at the desired location
  3466. //
  3467. FreeBlock = RtlpFindAndCommitPages( Heap,
  3468. Heap->Segments[ BusyBlock->SegmentIndex ],
  3469. &FreeSize,
  3470. (PHEAP_ENTRY)FreeBlock );
  3471. //
  3472. // Check if the commit succeeded
  3473. //
  3474. if (FreeBlock == NULL) {
  3475. return FALSE;
  3476. }
  3477. //
  3478. // New coalesce this newly committed space with whatever is free
  3479. // around it
  3480. //
  3481. FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
  3482. FreeBlock = RtlpCoalesceFreeBlocks( Heap, FreeBlock, &FreeSize, FALSE );
  3483. FreeFlags = FreeBlock->Flags;
  3484. //
  3485. // If the newly allocated space plus the current block size is still
  3486. // not big enough for our resize effort then put this newly
  3487. // allocated block into the appropriate free list and tell our caller
  3488. // that a resize wasn't possible
  3489. //
  3490. if ((FreeSize + BusyBlock->Size) < AllocationIndex) {
  3491. RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
  3492. Heap->TotalFreeSize += FreeSize;
  3493. if (DEBUG_HEAP(Flags)) {
  3494. RtlpValidateHeapHeaders( Heap, TRUE );
  3495. }
  3496. return FALSE;
  3497. }
  3498. //
  3499. // We were able to generate enough space for the resize effort, so
  3500. // now free size will be the index for the current block plus the
  3501. // new free space
  3502. //
  3503. FreeSize += BusyBlock->Size;
  3504. } else {
  3505. //
  3506. // The following block is present so grab its flags and see if
  3507. // it is free or busy. If busy then we cannot grow the current
  3508. // block
  3509. //
  3510. FreeFlags = FreeBlock->Flags;
  3511. if (FreeFlags & HEAP_ENTRY_BUSY) {
  3512. return FALSE;
  3513. }
  3514. //
  3515. // Compute the index if we combine current block with its following
  3516. // free block and check if it is big enough
  3517. //
  3518. FreeSize = BusyBlock->Size + FreeBlock->Size;
  3519. if (FreeSize < AllocationIndex) {
  3520. return FALSE;
  3521. }
  3522. //
  3523. // The two blocks together are big enough so now remove the free
  3524. // block from its free list, and update the heap's total free size
  3525. //
  3526. RtlpRemoveFreeBlock( Heap, FreeBlock );
  3527. Heap->TotalFreeSize -= FreeBlock->Size;
  3528. }
  3529. //
  3530. // At this point we have a busy block followed by a free block that
  3531. // together have enough space for the resize. The free block has been
  3532. // removed from its list and free size is the index of the two combined
  3533. // blocks.
  3534. //
  3535. // Calculate the number of bytes in use in the old block
  3536. //
  3537. OldSize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) - RtlpGetUnusedBytes(Heap, BusyBlock);
  3538. //
  3539. // Calculate the index for whatever excess we'll have when we combine
  3540. // the two blocks
  3541. //
  3542. FreeSize -= AllocationIndex;
  3543. //
  3544. // If the excess is not too much then put it back in our allocation
  3545. // (i.e., we don't want small free pieces left over)
  3546. //
  3547. if (FreeSize <= 2) {
  3548. AllocationIndex += FreeSize;
  3549. FreeSize = 0;
  3550. }
  3551. //
  3552. // If the busy block has an extra stuff struct present then copy over the
  3553. // extra stuff
  3554. //
  3555. if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) {
  3556. OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
  3557. NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
  3558. *NewExtraStuff = *OldExtraStuff;
  3559. //
  3560. // If heap tagging is enabled then update the heap tag from the extra
  3561. // stuff struct
  3562. //
  3563. if (IS_HEAP_TAGGING_ENABLED()) {
  3564. NewExtraStuff->TagIndex =
  3565. RtlpUpdateTagEntry( Heap,
  3566. NewExtraStuff->TagIndex,
  3567. BusyBlock->Size,
  3568. AllocationIndex,
  3569. ReAllocationAction );
  3570. }
  3571. //
  3572. // Otherwise extra stuff is not in use so see if heap tagging is enabled
  3573. // and if so then update small tag index
  3574. //
  3575. } else if (IS_HEAP_TAGGING_ENABLED()) {
  3576. BusyBlock->SmallTagIndex = (UCHAR)
  3577. RtlpUpdateTagEntry( Heap,
  3578. BusyBlock->SmallTagIndex,
  3579. BusyBlock->Size,
  3580. AllocationIndex,
  3581. ReAllocationAction );
  3582. }
  3583. //
  3584. // Check if we will have any free space to give back.
  3585. //
  3586. if (FreeSize == 0) {
  3587. //
  3588. // No following free space so update the flags, size and byte counts
  3589. // for the resized block. If the free block was a last entry
  3590. // then the busy block must also now be a last entry.
  3591. //
  3592. BusyBlock->Flags |= FreeFlags & HEAP_ENTRY_LAST_ENTRY;
  3593. BusyBlock->Size = (USHORT)AllocationIndex;
  3594. RtlpSetUnusedBytes(Heap, BusyBlock, ((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size));
  3595. //
  3596. // Update the previous size field of the following block if it exists
  3597. //
  3598. if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
  3599. (BusyBlock + BusyBlock->Size)->PreviousSize = BusyBlock->Size;
  3600. } else {
  3601. PHEAP_SEGMENT Segment;
  3602. Segment = Heap->Segments[BusyBlock->SegmentIndex];
  3603. Segment->LastEntryInSegment = BusyBlock;
  3604. }
  3605. //
  3606. // Otherwise there is some free space to return to the heap
  3607. //
  3608. } else {
  3609. //
  3610. // Update the size and byte counts for the resized block.
  3611. //
  3612. BusyBlock->Size = (USHORT)AllocationIndex;
  3613. RtlpSetUnusedBytes(Heap, BusyBlock, ((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size));
  3614. //
  3615. // Determine where the new free block starts and fill in its fields
  3616. //
  3617. SplitBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)BusyBlock + AllocationIndex);
  3618. SplitBlock->PreviousSize = (USHORT)AllocationIndex;
  3619. SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
  3620. //
  3621. // If this new free block will be the last entry then update its
  3622. // flags and size and put it into the appropriate free list
  3623. //
  3624. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  3625. PHEAP_SEGMENT Segment;
  3626. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  3627. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  3628. SplitBlock->Flags = FreeFlags;
  3629. SplitBlock->Size = (USHORT)FreeSize;
  3630. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  3631. Heap->TotalFreeSize += FreeSize;
  3632. //
  3633. // The free block is followed by another valid block
  3634. //
  3635. } else {
  3636. //
  3637. // Point to the block following our new free block
  3638. //
  3639. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  3640. //
  3641. // If the block following the new free block is busy then
  3642. // update the flags and size for the new free block, update
  3643. // the following blocks previous size, and put the free block
  3644. // into the appropriate free list
  3645. //
  3646. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
  3647. SplitBlock->Flags = FreeFlags & (~HEAP_ENTRY_LAST_ENTRY);
  3648. SplitBlock->Size = (USHORT)FreeSize;
  3649. ((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
  3650. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  3651. Heap->TotalFreeSize += FreeSize;
  3652. //
  3653. // Otherwise the following block is also free so we can combine
  3654. // these two blocks
  3655. //
  3656. } else {
  3657. //
  3658. // Remember the new free flags from the following block
  3659. //
  3660. FreeFlags = SplitBlock2->Flags;
  3661. //
  3662. // Remove the following block from its free list
  3663. //
  3664. RtlpRemoveFreeBlock( Heap, SplitBlock2 );
  3665. Heap->TotalFreeSize -= SplitBlock2->Size;
  3666. //
  3667. // Calculate the size of the new combined free block
  3668. //
  3669. FreeSize += SplitBlock2->Size;
  3670. //
  3671. // Give the new the its new flags
  3672. //
  3673. SplitBlock->Flags = FreeFlags;
  3674. //
  3675. // If the combited block is not too large for the dedicated
  3676. // free lists then that where we'll put it
  3677. //
  3678. if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
  3679. SplitBlock->Size = (USHORT)FreeSize;
  3680. //
  3681. // If present update the previous size for the following block
  3682. //
  3683. if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
  3684. ((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
  3685. } else {
  3686. PHEAP_SEGMENT Segment;
  3687. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  3688. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  3689. }
  3690. //
  3691. // Insert the new combined free block into the free list
  3692. //
  3693. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  3694. Heap->TotalFreeSize += FreeSize;
  3695. } else {
  3696. //
  3697. // Otherwise the new free block is too large to go into
  3698. // a dedicated free list so put it in the general free list
  3699. // which might involve breaking it apart.
  3700. //
  3701. RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
  3702. }
  3703. }
  3704. }
  3705. }
  3706. //
  3707. // At this point the block has been resized and any extra space has been
  3708. // returned to the free list
  3709. //
  3710. // Check if we should zero out the new space
  3711. //
  3712. if (Flags & HEAP_ZERO_MEMORY) {
  3713. //
  3714. // Because of the unused bytes, the OldSize can be lower than Size
  3715. // We'll fill the remaining chunck with 0.
  3716. //
  3717. if (Size > OldSize) {
  3718. RtlZeroMemory( (PCHAR)(BusyBlock + 1) + OldSize,
  3719. Size - OldSize );
  3720. }
  3721. //
  3722. // Check if we should be filling in heap after it as
  3723. // been freed, and if so then fill in the newly allocated
  3724. // space beyond the old bytes.
  3725. //
  3726. } else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
  3727. SIZE_T PartialBytes, ExtraSize;
  3728. PartialBytes = OldSize & (sizeof( ULONG ) - 1);
  3729. if (PartialBytes) {
  3730. PartialBytes = 4 - PartialBytes;
  3731. }
  3732. if (Size > (OldSize + PartialBytes)) {
  3733. ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof( ULONG ) - 1);
  3734. if (ExtraSize != 0) {
  3735. RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
  3736. ExtraSize,
  3737. ALLOC_HEAP_FILL );
  3738. }
  3739. }
  3740. }
  3741. //
  3742. // If we are going tailing checking then fill in the space right beyond
  3743. // the new allocation
  3744. //
  3745. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
  3746. RtlFillMemory( (PCHAR)(BusyBlock + 1) + Size,
  3747. CHECK_HEAP_TAIL_SIZE,
  3748. CHECK_HEAP_TAIL_FILL );
  3749. }
  3750. //
  3751. // Give the resized block any user settable flags send in by the
  3752. // caller
  3753. //
  3754. BusyBlock->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
  3755. BusyBlock->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
  3756. //
  3757. // And return to our caller
  3758. //
  3759. return TRUE;
  3760. }
  3761. //
  3762. // Local support routine
  3763. //
  3764. PHEAP_TAG_ENTRY
  3765. RtlpAllocateTags (
  3766. PHEAP Heap,
  3767. ULONG NumberOfTags
  3768. )
  3769. /*++
  3770. Routine Description:
  3771. This routine is used to allocate space for additional tags within
  3772. a heap
  3773. Arguments:
  3774. Heap - Supplies a pointer to the heap being modified. If not specified
  3775. then the processes global tag heap is used
  3776. NumberOfTags - Supplies the number of tags that we want stored in the
  3777. heap. This is the number to grow the tag list by.
  3778. Return Value:
  3779. PHEAP_TAG_ENTRY - Returns a pointer to the next available tag entry in the
  3780. heap
  3781. --*/
  3782. {
  3783. NTSTATUS Status;
  3784. ULONG TagIndex;
  3785. SIZE_T ReserveSize;
  3786. SIZE_T CommitSize;
  3787. PHEAP_TAG_ENTRY TagEntry;
  3788. USHORT CreatorBackTraceIndex;
  3789. USHORT MaximumTagIndex;
  3790. USHORT TagIndexFlag;
  3791. //
  3792. // Check if the process has a global tag heap. If not then there is
  3793. // nothing for us to do
  3794. //
  3795. if (RtlpGlobalTagHeap == NULL) {
  3796. return NULL;
  3797. }
  3798. //
  3799. // If the user didn't give us a heap then use the processes global
  3800. // tag heap
  3801. //
  3802. if (Heap == NULL) {
  3803. RtlpGlobalTagHeap->Signature = HEAP_SIGNATURE;
  3804. RtlpGlobalTagHeap->Flags = HEAP_NO_SERIALIZE;
  3805. TagIndexFlag = HEAP_GLOBAL_TAG;
  3806. Heap = RtlpGlobalTagHeap;
  3807. } else {
  3808. TagIndexFlag = 0;
  3809. }
  3810. //
  3811. // Grab the stack backtrace if possible and if we should
  3812. //
  3813. CreatorBackTraceIndex = 0;
  3814. if (Heap->Flags & HEAP_CAPTURE_STACK_BACKTRACES) {
  3815. CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
  3816. }
  3817. //
  3818. // If the heap does not already have tag entries then we'll
  3819. // reserve space for them
  3820. //
  3821. if (Heap->TagEntries == NULL) {
  3822. MaximumTagIndex = HEAP_MAXIMUM_TAG & ~HEAP_GLOBAL_TAG;
  3823. ReserveSize = MaximumTagIndex * sizeof( HEAP_TAG_ENTRY );
  3824. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  3825. &Heap->TagEntries,
  3826. 0,
  3827. &ReserveSize,
  3828. MEM_RESERVE,
  3829. PAGE_READWRITE );
  3830. if (!NT_SUCCESS( Status )) {
  3831. return NULL;
  3832. }
  3833. Heap->MaximumTagIndex = MaximumTagIndex;
  3834. Heap->NextAvailableTagIndex = 0;
  3835. //
  3836. // Add one for zero tag, as that is always reserved for heap name
  3837. //
  3838. NumberOfTags += 1;
  3839. }
  3840. //
  3841. // At this point we have a space reserved for tag entries. If the number
  3842. // of tags that we need to grow is too large then tell the user we can't
  3843. // do it.
  3844. //
  3845. if (NumberOfTags > (ULONG)(Heap->MaximumTagIndex - Heap->NextAvailableTagIndex)) {
  3846. return NULL;
  3847. }
  3848. //
  3849. // Get a pointer to the next available tag entry, and for
  3850. // every tag entry that we want to grow by we'll commit
  3851. // the page containing the tag entry. We only need to do
  3852. // this for every page just once. We'll determine this
  3853. // by seeing when the tag entry crosses a page boundary
  3854. //
  3855. TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
  3856. for (TagIndex = Heap->NextAvailableTagIndex;
  3857. TagIndex < Heap->NextAvailableTagIndex + NumberOfTags;
  3858. TagIndex++ ) {
  3859. if (((((ULONG_PTR)TagEntry + sizeof(*TagEntry)) & (PAGE_SIZE-1)) <=
  3860. sizeof(*TagEntry))) {
  3861. CommitSize = PAGE_SIZE;
  3862. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  3863. &TagEntry,
  3864. 0,
  3865. &CommitSize,
  3866. MEM_COMMIT,
  3867. PAGE_READWRITE );
  3868. if (!NT_SUCCESS( Status )) {
  3869. return NULL;
  3870. }
  3871. }
  3872. //
  3873. // Bias the tag index if this is the global tag heap
  3874. //
  3875. TagEntry->TagIndex = (USHORT)TagIndex | TagIndexFlag;
  3876. //
  3877. // Set the stack back trace
  3878. //
  3879. TagEntry->CreatorBackTraceIndex = CreatorBackTraceIndex;
  3880. //
  3881. // Move on to the next tag entry
  3882. //
  3883. TagEntry += 1;
  3884. }
  3885. //
  3886. // At this point we've build the new tag list so now pop off the next
  3887. // available tag entry
  3888. //
  3889. TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
  3890. Heap->NextAvailableTagIndex += (USHORT)NumberOfTags;
  3891. //
  3892. // And return to our caller
  3893. //
  3894. return TagEntry;
  3895. }
  3896. //
  3897. // Declared in heappriv.h
  3898. //
  3899. PWSTR
  3900. RtlpGetTagName (
  3901. PHEAP Heap,
  3902. USHORT TagIndex
  3903. )
  3904. /*++
  3905. Routine Description:
  3906. This routine returns the name of the tag denoted by the heap, tagindex
  3907. tuple.
  3908. This routine is only called by heapdbg when doing a debug print to
  3909. generate a tag name for printing
  3910. Arguments:
  3911. Heap - Supplies the tag being queried
  3912. TagIndex - Supplies the index for the tag being queried
  3913. Return Value:
  3914. PWSTR - returns the name of the indicated tag
  3915. --*/
  3916. {
  3917. //
  3918. // If the processes global tag heap has not been initialized then
  3919. // not tag has a name
  3920. //
  3921. if (RtlpGlobalTagHeap == NULL) {
  3922. return NULL;
  3923. }
  3924. //
  3925. // We only deal with non zero tag indices
  3926. //
  3927. if (TagIndex != 0) {
  3928. //
  3929. // If the tag index is for a pseudo tag then we clear the
  3930. // the psuedo bit and generate a pseudo tag name
  3931. //
  3932. if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
  3933. TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
  3934. //
  3935. // Check that the tag index is valid and that the heap
  3936. // has some psuedo tag entries
  3937. //
  3938. if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) &&
  3939. (Heap->PseudoTagEntries != NULL)) {
  3940. //
  3941. // A pseudo tag index of zero denote objects
  3942. //
  3943. if (TagIndex == 0) {
  3944. swprintf( RtlpPseudoTagNameBuffer, L"Objects>%4u",
  3945. HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT );
  3946. //
  3947. // A psuedo tag index less than the free list maximum
  3948. // denotes the dedicated free list
  3949. //
  3950. } else if (TagIndex < HEAP_MAXIMUM_FREELISTS) {
  3951. swprintf( RtlpPseudoTagNameBuffer, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT );
  3952. //
  3953. // Otherwise the pseudo tag is for the big allocations
  3954. //
  3955. } else {
  3956. swprintf( RtlpPseudoTagNameBuffer, L"VirtualAlloc" );
  3957. }
  3958. return RtlpPseudoTagNameBuffer;
  3959. }
  3960. //
  3961. // Otherwise if the tag index is for a global tag then we pull
  3962. // the name off of the global heap. Provided the index is valid
  3963. // and the heap does have some tag entries
  3964. //
  3965. } else if (TagIndex & HEAP_GLOBAL_TAG) {
  3966. TagIndex &= ~HEAP_GLOBAL_TAG;
  3967. if ((TagIndex < RtlpGlobalTagHeap->NextAvailableTagIndex) &&
  3968. (RtlpGlobalTagHeap->TagEntries != NULL)) {
  3969. return RtlpGlobalTagHeap->TagEntries[ TagIndex ].TagName;
  3970. }
  3971. //
  3972. // Otherwise we'll pull the name off of the input heap
  3973. // provided the index is valid and the heap does have some
  3974. // tag entries
  3975. //
  3976. } else if ((TagIndex < Heap->NextAvailableTagIndex) &&
  3977. (Heap->TagEntries != NULL)) {
  3978. return Heap->TagEntries[ TagIndex ].TagName;
  3979. }
  3980. }
  3981. return NULL;
  3982. }
  3983. //
  3984. // Declared in heappriv.h
  3985. //
  3986. USHORT
  3987. RtlpUpdateTagEntry (
  3988. PHEAP Heap,
  3989. USHORT TagIndex,
  3990. SIZE_T OldSize, // Only valid for ReAllocation and Free actions
  3991. SIZE_T NewSize, // Only valid for ReAllocation and Allocation actions
  3992. HEAP_TAG_ACTION Action
  3993. )
  3994. /*++
  3995. Routine Description:
  3996. This routine is used to modify a tag entry
  3997. Arguments:
  3998. Heap - Supplies a pointer to the heap being modified
  3999. TagIndex - Supplies the tag being modified
  4000. OldSize - Supplies the old allocation index of the block associated with the tag
  4001. NewSize - Supplies the new allocation index of the block associated with the tag
  4002. Action - Supplies the type of action being performed on the heap tag
  4003. Return Value:
  4004. USHORT - Returns a tag index for the newly updated tag
  4005. --*/
  4006. {
  4007. PHEAP_TAG_ENTRY TagEntry;
  4008. //
  4009. // If the processes tag heap does not exist then we'll return a zero index
  4010. // right away
  4011. //
  4012. if (RtlpGlobalTagHeap == NULL) {
  4013. return 0;
  4014. }
  4015. //
  4016. // If the action is greater than or equal to free action then it is
  4017. // either FreeAction, VirtualFreeAction, ReAllocationAction, or
  4018. // VirtualReAllocationAction. Which means we already should have a tag
  4019. // that is simply being modified
  4020. //
  4021. if (Action >= FreeAction) {
  4022. //
  4023. // If the tag index is zero then there is nothing for us to do
  4024. //
  4025. if (TagIndex == 0) {
  4026. return 0;
  4027. }
  4028. //
  4029. // If this is a pseudo tag then make sure the rest of the tag index
  4030. // after we remove the psuedo bit is valid and that the heap is
  4031. // actually maintaining pseudo tags
  4032. //
  4033. if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
  4034. TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
  4035. if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) &&
  4036. (Heap->PseudoTagEntries != NULL)) {
  4037. TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
  4038. TagIndex |= HEAP_PSEUDO_TAG_FLAG;
  4039. } else {
  4040. return 0;
  4041. }
  4042. //
  4043. // Otherwise if this is a global tag then make sure the tag index
  4044. // after we remove the global bit is valid and that the global tag
  4045. // heap has some tag entries
  4046. //
  4047. } else if (TagIndex & HEAP_GLOBAL_TAG) {
  4048. TagIndex &= ~HEAP_GLOBAL_TAG;
  4049. if ((TagIndex < RtlpGlobalTagHeap->NextAvailableTagIndex) &&
  4050. (RtlpGlobalTagHeap->TagEntries != NULL)) {
  4051. TagEntry = &RtlpGlobalTagHeap->TagEntries[ TagIndex ];
  4052. TagIndex |= HEAP_GLOBAL_TAG;
  4053. } else {
  4054. return 0;
  4055. }
  4056. //
  4057. // Otherwise we have a regular tag index that we need to make sure
  4058. // is a valid value and that the heap has some tag entries
  4059. //
  4060. } else if ((TagIndex < Heap->NextAvailableTagIndex) &&
  4061. (Heap->TagEntries != NULL)) {
  4062. TagEntry = &Heap->TagEntries[ TagIndex ];
  4063. } else {
  4064. return 0;
  4065. }
  4066. //
  4067. // At this point we have a tag entry and tag index. Increment the
  4068. // number of frees we've done on the tag, and decrement the size by
  4069. // the number of bytes we've just freed
  4070. //
  4071. TagEntry->Frees += 1;
  4072. TagEntry->Size -= OldSize;
  4073. //
  4074. // Now if the action is either ReAllocationAction or
  4075. // VirtualReAllocationAction. Then we get to add back in the
  4076. // new size and the allocation count
  4077. //
  4078. if (Action >= ReAllocationAction) {
  4079. //
  4080. // If the this is a pseudo tag then we tag entry goes off the
  4081. // pseudo tag list
  4082. //
  4083. if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
  4084. TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
  4085. NewSize :
  4086. (Action == VirtualReAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0));
  4087. TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
  4088. TagIndex |= HEAP_PSEUDO_TAG_FLAG;
  4089. }
  4090. TagEntry->Allocs += 1;
  4091. TagEntry->Size += NewSize;
  4092. }
  4093. //
  4094. // The action is either AllocationAction or VirtualAllocationAction
  4095. //
  4096. } else {
  4097. //
  4098. // Check if the supplied tag index is a regular tag and that it is
  4099. // valid for the tags in this heap
  4100. //
  4101. if ((TagIndex != 0) &&
  4102. (TagIndex < Heap->NextAvailableTagIndex) &&
  4103. (Heap->TagEntries != NULL)) {
  4104. TagEntry = &Heap->TagEntries[ TagIndex ];
  4105. //
  4106. // Otherwise if this is a global tag then make sure that it is a
  4107. // valid global index
  4108. //
  4109. } else if (TagIndex & HEAP_GLOBAL_TAG) {
  4110. TagIndex &= ~HEAP_GLOBAL_TAG;
  4111. Heap = RtlpGlobalTagHeap;
  4112. if ((TagIndex < Heap->NextAvailableTagIndex) &&
  4113. (Heap->TagEntries != NULL)) {
  4114. TagEntry = &Heap->TagEntries[ TagIndex ];
  4115. TagIndex |= HEAP_GLOBAL_TAG;
  4116. } else {
  4117. return 0;
  4118. }
  4119. //
  4120. // Otherwise if this is a pseudo tag then build a valid tag index
  4121. // based on the new size of the allocation
  4122. //
  4123. } else if (Heap->PseudoTagEntries != NULL) {
  4124. TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
  4125. NewSize :
  4126. (Action == VirtualAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0));
  4127. TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
  4128. TagIndex |= HEAP_PSEUDO_TAG_FLAG;
  4129. //
  4130. // Otherwise the user didn't call us with a valid tag
  4131. //
  4132. } else {
  4133. return 0;
  4134. }
  4135. //
  4136. // At this point we have a valid tag entry and tag index, so
  4137. // update the tag entry state to reflect this new allocation
  4138. //
  4139. TagEntry->Allocs += 1;
  4140. TagEntry->Size += NewSize;
  4141. }
  4142. //
  4143. // And return to our caller with the new tag index
  4144. //
  4145. return TagIndex;
  4146. }
  4147. //
  4148. // Declared in heappriv.h
  4149. //
  4150. VOID
  4151. RtlpResetTags (
  4152. PHEAP Heap
  4153. )
  4154. /*++
  4155. Routine Description:
  4156. This routine is used to reset all the tag entries in a heap
  4157. Arguments:
  4158. Heap - Supplies a pointer to the heap being modified
  4159. Return Value:
  4160. None.
  4161. --*/
  4162. {
  4163. PHEAP_TAG_ENTRY TagEntry;
  4164. PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntry;
  4165. ULONG i;
  4166. //
  4167. // We only have work to do if the heap has any allocated tag entries
  4168. //
  4169. TagEntry = Heap->TagEntries;
  4170. if (TagEntry != NULL) {
  4171. //
  4172. // For every tag entry in the heap we will zero out its counters
  4173. //
  4174. for (i=0; i<Heap->NextAvailableTagIndex; i++) {
  4175. TagEntry->Allocs = 0;
  4176. TagEntry->Frees = 0;
  4177. TagEntry->Size = 0;
  4178. //
  4179. // Advance to the next tag entry
  4180. //
  4181. TagEntry += 1;
  4182. }
  4183. }
  4184. //
  4185. // We will only reset the pseudo tags if they exist
  4186. //
  4187. PseudoTagEntry = Heap->PseudoTagEntries;
  4188. if (PseudoTagEntry != NULL) {
  4189. //
  4190. // For every pseudo tag entry in the heap we will zero out its
  4191. // counters
  4192. //
  4193. for (i=0; i<HEAP_NUMBER_OF_PSEUDO_TAG; i++) {
  4194. PseudoTagEntry->Allocs = 0;
  4195. PseudoTagEntry->Frees = 0;
  4196. PseudoTagEntry->Size = 0;
  4197. //
  4198. // Advance to the next pseudo tag entry
  4199. //
  4200. PseudoTagEntry += 1;
  4201. }
  4202. }
  4203. //
  4204. // And return to our caller
  4205. //
  4206. return;
  4207. }
  4208. //
  4209. // Declared in heappriv.h
  4210. //
  4211. VOID
  4212. RtlpDestroyTags (
  4213. PHEAP Heap
  4214. )
  4215. /*++
  4216. Routine Description:
  4217. This routine is used to completely remove all the normal tag entries
  4218. in use by a heap
  4219. Arguments:
  4220. Heap - Supplies a pointer to the heap being modified
  4221. Return Value:
  4222. None.
  4223. --*/
  4224. {
  4225. NTSTATUS Status;
  4226. SIZE_T RegionSize;
  4227. //
  4228. // We will only do the action if the heap has some tag entries
  4229. //
  4230. if (Heap->TagEntries != NULL) {
  4231. //
  4232. // Release all the memory used by the tag entries
  4233. //
  4234. RegionSize = 0;
  4235. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  4236. &Heap->TagEntries,
  4237. &RegionSize,
  4238. MEM_RELEASE );
  4239. if (NT_SUCCESS( Status )) {
  4240. Heap->TagEntries = NULL;
  4241. }
  4242. }
  4243. //
  4244. // And return to our caller
  4245. //
  4246. return;
  4247. }
  4248. //
  4249. // Local support routine
  4250. //
  4251. NTSTATUS
  4252. RtlpAllocateHeapUsageEntry (
  4253. PRTL_HEAP_USAGE_INTERNAL Buffer,
  4254. PRTL_HEAP_USAGE_ENTRY *pp
  4255. )
  4256. /*++
  4257. Routine Description:
  4258. This routine is used to allocate an new heap usage entry
  4259. from the internal heap usage buffer
  4260. Arguments:
  4261. Buffer - Supplies a pointer to the internal heap usage
  4262. buffer from which to allocate an entry
  4263. pp - Receives a pointer to the newly allocated heap
  4264. usage entry. If pp is already pointing to an existing
  4265. heap usage entry then on return we'll have this old
  4266. entry point to the new entry, but still return the new
  4267. entry.
  4268. Return Value:
  4269. NTSTATUS - An appropriate status value
  4270. --*/
  4271. {
  4272. NTSTATUS Status;
  4273. PRTL_HEAP_USAGE_ENTRY p;
  4274. PVOID CommitAddress;
  4275. SIZE_T PageSize;
  4276. //
  4277. // Check if the free list is empty and then we have to allocate more
  4278. // memory for the free list
  4279. //
  4280. if (Buffer->FreeList == NULL) {
  4281. //
  4282. // We cannot grow the buffer any larger than the reserved size
  4283. //
  4284. if (Buffer->CommittedSize >= Buffer->ReservedSize) {
  4285. return STATUS_NO_MEMORY;
  4286. }
  4287. //
  4288. // Try and add one page of committed memory to the buffer
  4289. // starting right after the currently committed space
  4290. //
  4291. PageSize = PAGE_SIZE;
  4292. CommitAddress = (PCHAR)Buffer->Base + Buffer->CommittedSize;
  4293. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  4294. &CommitAddress,
  4295. 0,
  4296. &PageSize,
  4297. MEM_COMMIT,
  4298. PAGE_READWRITE );
  4299. if (!NT_SUCCESS( Status )) {
  4300. return Status;
  4301. }
  4302. //
  4303. // Update the committed buffer size
  4304. //
  4305. Buffer->CommittedSize += PageSize;
  4306. //
  4307. // Add the newly allocated space to the free list and
  4308. // build up the free list
  4309. //
  4310. Buffer->FreeList = CommitAddress;
  4311. p = Buffer->FreeList;
  4312. while (PageSize != 0) {
  4313. p->Next = (p+1);
  4314. p += 1;
  4315. PageSize -= sizeof( *p );
  4316. }
  4317. //
  4318. // Null terminate the next pointer in the last free entry
  4319. //
  4320. p -= 1;
  4321. p->Next = NULL;
  4322. }
  4323. //
  4324. // At this point the free list contains at least one entry
  4325. // so simply pop the entry.
  4326. //
  4327. p = Buffer->FreeList;
  4328. Buffer->FreeList = p->Next;
  4329. p->Next = NULL;
  4330. //
  4331. // Now if the caller supplied an existing heap entry then
  4332. // we'll make the old heap entry point to this new entry
  4333. //
  4334. if (*pp) {
  4335. (*pp)->Next = p;
  4336. }
  4337. //
  4338. // And then return the new entry to our caller
  4339. //
  4340. *pp = p;
  4341. return STATUS_SUCCESS;
  4342. }
  4343. //
  4344. // Local support routine
  4345. //
  4346. PRTL_HEAP_USAGE_ENTRY
  4347. RtlpFreeHeapUsageEntry (
  4348. PRTL_HEAP_USAGE_INTERNAL Buffer,
  4349. PRTL_HEAP_USAGE_ENTRY p
  4350. )
  4351. /*++
  4352. Routine Description:
  4353. This routine moves a heap usage entry from its current
  4354. list onto the free list and returns a pointer to the
  4355. next heap usage entry in the list. It is like doing a pop
  4356. of the list denoted by "p"
  4357. Arguments:
  4358. Buffer - Supplies a pointer to the internal heap usage buffer
  4359. being modified
  4360. p - Supplies a pointer to the entry being moved. Okay if
  4361. it's null
  4362. Return Value:
  4363. PRTL_HEAP_USAGE_ENTRY - Returns a pointer to the next heap usage
  4364. entry
  4365. --*/
  4366. {
  4367. PRTL_HEAP_USAGE_ENTRY pTmp;
  4368. //
  4369. // Check if we have a non null heap entry and if so then add
  4370. // the entry to the front of the free list and return the next
  4371. // entry in the list
  4372. //
  4373. if (p != NULL) {
  4374. pTmp = p->Next;
  4375. p->Next = Buffer->FreeList;
  4376. Buffer->FreeList = p;
  4377. } else {
  4378. pTmp = NULL;
  4379. }
  4380. return pTmp;
  4381. }
  4382. //
  4383. // Declared in heap.h
  4384. //
  4385. BOOLEAN
  4386. RtlpHeapIsLocked (
  4387. IN PVOID HeapHandle
  4388. )
  4389. /*++
  4390. Routine Description:
  4391. This routine is used to determine if a heap is locked
  4392. Arguments:
  4393. HeapHandle - Supplies a pointer to the heap being queried
  4394. Return Value:
  4395. BOOLEAN - TRUE if the heap is locked and FALSE otherwise
  4396. --*/
  4397. {
  4398. PHEAP Heap;
  4399. //
  4400. // Check if this is guard page version of heap
  4401. //
  4402. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  4403. RtlpDebugPageHeapIsLocked( HeapHandle ));
  4404. Heap = (PHEAP)HeapHandle;
  4405. //
  4406. // The heap is locked if there is a lock variable, and it has an
  4407. // owning thread or the lockcount is not -1
  4408. //
  4409. return (( Heap->LockVariable != NULL ) &&
  4410. ( Heap->LockVariable->Lock.CriticalSection.OwningThread ||
  4411. Heap->LockVariable->Lock.CriticalSection.LockCount != -1 ));
  4412. }
  4413. //
  4414. // Low fragmentation heap activation routines
  4415. //
  4416. //
  4417. // The flags that are not compatible with the low fragmentation heap
  4418. //
  4419. #define HEAP_LFH_RESTRICTION_FLAGS (HEAP_DEBUG_FLAGS | \
  4420. HEAP_NO_SERIALIZE | \
  4421. HEAP_SETTABLE_USER_FLAGS | \
  4422. HEAP_NEED_EXTRA_FLAGS | \
  4423. HEAP_CREATE_ALIGN_16 | \
  4424. HEAP_FREE_CHECKING_ENABLED | \
  4425. HEAP_TAIL_CHECKING_ENABLED)
  4426. NTSTATUS
  4427. RtlpActivateLowFragmentationHeap(
  4428. IN PVOID HeapHandle
  4429. )
  4430. /*++
  4431. Routine Description:
  4432. This routine activates the low fragmentation heap for a given NT heap
  4433. Note that the activation is not compatible with some heap flags, so the caller should
  4434. test the status
  4435. Arguments:
  4436. HeapHandle - Supplies a pointer to the heap being activated with LFH
  4437. Return Value:
  4438. An appropriate status
  4439. --*/
  4440. {
  4441. PHEAP Heap = (PHEAP)HeapHandle;
  4442. BOOLEAN LockAcquired = FALSE;
  4443. BOOLEAN Result;
  4444. NTSTATUS Status = STATUS_SUCCESS;
  4445. PVOID LowFragmentationHeap;
  4446. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  4447. if ( RtlpLFHInitialized == 0 ) {
  4448. //
  4449. // Acquire the process lock and check again if the
  4450. // LFH manager was initialized
  4451. //
  4452. RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
  4453. if ( RtlpLFHInitialized == 0 ) {
  4454. RtlpInitializeLowFragHeapManager();
  4455. RtlpLFHInitialized = 1;
  4456. }
  4457. RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
  4458. }
  4459. try {
  4460. if ( (Heap->Flags & HEAP_LFH_RESTRICTION_FLAGS)
  4461. ||
  4462. !(Heap->Flags & HEAP_GROWABLE) ) {
  4463. Status = STATUS_UNSUCCESSFUL;
  4464. leave;
  4465. }
  4466. if (IS_HEAP_TAGGING_ENABLED()) {
  4467. Status = STATUS_UNSUCCESSFUL;
  4468. leave;
  4469. }
  4470. //
  4471. // Lock the front heap. We need to do this under the heap lock
  4472. // to prevent concurent access to these fields
  4473. //
  4474. RtlAcquireLockRoutine( Heap->LockVariable );
  4475. LockAcquired = TRUE;
  4476. //
  4477. // if we already have a low fragmentation heap created
  4478. // we're done
  4479. //
  4480. if (RtlpGetLowFragHeap(Heap)) {
  4481. Status = STATUS_UNSUCCESSFUL;
  4482. leave;
  4483. }
  4484. if (!RtlpIsFrontHeapUnlocked(Heap)) {
  4485. //
  4486. // Someone locked the front end heap probable to create another LFH
  4487. // We need to fail the call.
  4488. //
  4489. Status = STATUS_UNSUCCESSFUL;
  4490. leave;
  4491. }
  4492. RtlpLockFrontHeap(Heap);
  4493. //
  4494. // capture the lookaside
  4495. //
  4496. Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  4497. Heap->FrontEndHeap = NULL;
  4498. Heap->FrontEndHeapType = 0;
  4499. RtlReleaseLockRoutine( Heap->LockVariable );
  4500. LockAcquired = FALSE;
  4501. //
  4502. // If there is an active lookaside list then drain and remove it.
  4503. // By setting the lookaside field in the heap to null we guarantee
  4504. // that the call the free heap will not try and use the lookaside
  4505. // list logic.
  4506. //
  4507. // We'll actually capture the lookaside pointer from the heap and
  4508. // only use the captured pointer. This will take care of the
  4509. // condition where another walk or lock heap can cause us to check
  4510. // for a non null pointer and then have it become null when we read
  4511. // it again. If it is non null to start with then even if the
  4512. // user walks or locks the heap via another thread the pointer to
  4513. // still valid here so we can still try and do a lookaside list pop.
  4514. //
  4515. if (Lookaside != NULL) {
  4516. ULONG i;
  4517. PVOID Block;
  4518. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
  4519. while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
  4520. RtlFreeHeap( HeapHandle, 0, Block );
  4521. }
  4522. }
  4523. }
  4524. LowFragmentationHeap = RtlpCreateLowFragHeap(Heap);
  4525. RtlAcquireLockRoutine( Heap->LockVariable );
  4526. LockAcquired = TRUE;
  4527. if (LowFragmentationHeap) {
  4528. Heap->FrontEndHeap = LowFragmentationHeap;
  4529. Heap->FrontEndHeapType = HEAP_FRONT_LOWFRAGHEAP;
  4530. //
  4531. // Adjusts the block decommit threshold to 16K, to reduce
  4532. // the fragmentation induced by often sub-segment frees
  4533. //
  4534. Heap->DeCommitFreeBlockThreshold = HEAP_LARGEST_LFH_BLOCK >> HEAP_GRANULARITY_SHIFT;
  4535. } else {
  4536. //
  4537. // we cannot create the LFH. so we need to restore the lookaside
  4538. // if it previously existed and set the right return status
  4539. //
  4540. if (Lookaside != NULL) {
  4541. Heap->FrontEndHeap = Lookaside;
  4542. Heap->FrontEndHeapType = HEAP_FRONT_LOOKASIDE;
  4543. }
  4544. Status = STATUS_NO_MEMORY;
  4545. }
  4546. RtlpUnlockFrontHeap(Heap);
  4547. LockAcquired = FALSE;
  4548. RtlReleaseLockRoutine( Heap->LockVariable );
  4549. } finally {
  4550. if (LockAcquired) {
  4551. RtlReleaseLockRoutine( Heap->LockVariable );
  4552. }
  4553. }
  4554. return Status;
  4555. }
  4556. NTSTATUS
  4557. RtlSetHeapInformation (
  4558. IN PVOID HeapHandle,
  4559. IN HEAP_INFORMATION_CLASS HeapInformationClass,
  4560. IN PVOID HeapInformation OPTIONAL,
  4561. IN SIZE_T HeapInformationLength OPTIONAL
  4562. )
  4563. /*++
  4564. Routine Description:
  4565. This is a general purpose routine that sets a custom information to a given heap.
  4566. Arguments:
  4567. HeapHandle - Supplies a pointer to the heap that will receive the settings
  4568. HeapInformationClass - The information class being set. It could be one of these values:
  4569. HeapCompatibilityInformation - Will change the default front end heap to the
  4570. low fragmentation heap
  4571. HeapInformation - The Information buffer
  4572. HeapInformationLength - the length of the HeapInformation buffer
  4573. Return Value:
  4574. An appropriate status
  4575. --*/
  4576. {
  4577. switch (HeapInformationClass) {
  4578. case HeapCompatibilityInformation:
  4579. if (HeapInformationLength < sizeof(ULONG)) {
  4580. return STATUS_BUFFER_TOO_SMALL;
  4581. } else {
  4582. if ( (*(PULONG)HeapInformation) == HEAP_FRONT_LOWFRAGHEAP ) {
  4583. return RtlpActivateLowFragmentationHeap( HeapHandle );
  4584. } else {
  4585. return STATUS_UNSUCCESSFUL;
  4586. }
  4587. }
  4588. break;
  4589. }
  4590. return STATUS_SUCCESS;
  4591. }
  4592. NTSTATUS
  4593. RtlQueryHeapInformation (
  4594. IN PVOID HeapHandle,
  4595. IN HEAP_INFORMATION_CLASS HeapInformationClass,
  4596. OUT PVOID HeapInformation OPTIONAL,
  4597. IN SIZE_T HeapInformationLength OPTIONAL,
  4598. OUT PSIZE_T ReturnLength OPTIONAL
  4599. )
  4600. /*++
  4601. Routine Description:
  4602. This is a general purpose routine that queries a custom information from a given heap.
  4603. Arguments:
  4604. HeapHandle - Supplies a pointer to the heap to be queried
  4605. HeapInformationClass - The information class required. It could be one of these values:
  4606. HeapCompatibilityInformation - It will retrieve
  4607. 0 : for pre NT4 SP3 compatibility mode
  4608. 1 : lookasides are enabled (NT4 SP3 or greater or W2k)
  4609. 2 : Low fragmentation heap enabled
  4610. HeapInformation - The buffer where the information will be filled in
  4611. HeapInformationLength - the length of the HeapInformation buffer
  4612. ReturnLength - The actual size of the buffer needed.
  4613. Return Value:
  4614. An appropriate status
  4615. --*/
  4616. {
  4617. switch (HeapInformationClass) {
  4618. case HeapCompatibilityInformation:
  4619. if (HeapInformationLength < sizeof(ULONG)) {
  4620. if (ReturnLength) {
  4621. *ReturnLength = sizeof(ULONG);
  4622. }
  4623. return STATUS_BUFFER_TOO_SMALL;
  4624. } else {
  4625. *(PULONG)HeapInformation = (ULONG)(((PHEAP)HeapHandle)->FrontEndHeapType);
  4626. if (ReturnLength) {
  4627. *ReturnLength = sizeof(ULONG);
  4628. }
  4629. }
  4630. break;
  4631. default:
  4632. return STATUS_UNSUCCESSFUL;
  4633. }
  4634. return STATUS_SUCCESS;
  4635. }