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.

8354 lines
244 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. heap.c
  5. Abstract:
  6. This module implements a heap allocator.
  7. Author:
  8. Steve Wood (stevewo) 20-Sep-1989 (Adapted from URTL\alloc.c)
  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. #ifndef NTOS_KERNEL_RUNTIME
  23. ULONG RtlpDisableHeapLookaside = 0;
  24. LONG RtlpSequenceNumberTest = 1024;
  25. LONG RtlpLargeListDepthLimit = 128;
  26. #define HEAP_ACTIVATE_CACHE_THRESHOLD 256
  27. #define HEAP_COMPAT_DISABLE_LOOKASIDES 1
  28. #define HEAP_COMPAT_DISABLE_LARGECACHE 2
  29. #endif
  30. #define HEAP_REUSAGE_FACTOR 4
  31. #if defined(_WIN64)
  32. //
  33. // Win64 heaps require an initial commit size of at least 8192. Note that
  34. // this is NOT necessarily the size of a page.
  35. //
  36. #define MINIMUM_HEAP_COMMIT 8192
  37. #else
  38. #define MINIMUM_HEAP_COMMIT 4096
  39. #endif
  40. C_ASSERT((MINIMUM_HEAP_COMMIT % PAGE_SIZE) == 0);
  41. //
  42. // If any of these flags are set, the fast allocator punts
  43. // to the slow do-everything allocator.
  44. //
  45. #define HEAP_SLOW_FLAGS (HEAP_DEBUG_FLAGS | \
  46. HEAP_SETTABLE_USER_FLAGS | \
  47. HEAP_NEED_EXTRA_FLAGS | \
  48. HEAP_CREATE_ALIGN_16 | \
  49. HEAP_FREE_CHECKING_ENABLED | \
  50. HEAP_TAIL_CHECKING_ENABLED)
  51. #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  52. #pragma const_seg("PAGECONST")
  53. #endif
  54. const UCHAR CheckHeapFillPattern[ CHECK_HEAP_TAIL_SIZE ] = {
  55. CHECK_HEAP_TAIL_FILL,
  56. CHECK_HEAP_TAIL_FILL,
  57. CHECK_HEAP_TAIL_FILL,
  58. CHECK_HEAP_TAIL_FILL,
  59. CHECK_HEAP_TAIL_FILL,
  60. CHECK_HEAP_TAIL_FILL,
  61. CHECK_HEAP_TAIL_FILL,
  62. #ifdef _WIN64
  63. CHECK_HEAP_TAIL_FILL,
  64. CHECK_HEAP_TAIL_FILL,
  65. CHECK_HEAP_TAIL_FILL,
  66. CHECK_HEAP_TAIL_FILL,
  67. CHECK_HEAP_TAIL_FILL,
  68. CHECK_HEAP_TAIL_FILL,
  69. CHECK_HEAP_TAIL_FILL,
  70. CHECK_HEAP_TAIL_FILL,
  71. #endif
  72. CHECK_HEAP_TAIL_FILL
  73. };
  74. //
  75. // These are procedure prototypes exported by heapdbg.c
  76. //
  77. #ifndef NTOS_KERNEL_RUNTIME
  78. PVOID
  79. RtlDebugCreateHeap (
  80. IN ULONG Flags,
  81. IN PVOID HeapBase OPTIONAL,
  82. IN SIZE_T ReserveSize OPTIONAL,
  83. IN SIZE_T CommitSize OPTIONAL,
  84. IN PVOID Lock OPTIONAL,
  85. IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
  86. );
  87. BOOLEAN
  88. RtlDebugDestroyHeap (
  89. IN PVOID HeapHandle
  90. );
  91. PVOID
  92. RtlDebugAllocateHeap (
  93. IN PVOID HeapHandle,
  94. IN ULONG Flags,
  95. IN SIZE_T Size
  96. );
  97. BOOLEAN
  98. RtlDebugFreeHeap (
  99. IN PVOID HeapHandle,
  100. IN ULONG Flags,
  101. IN PVOID BaseAddress
  102. );
  103. ULONG
  104. RtlDebugSizeHeap (
  105. IN PVOID HeapHandle,
  106. IN ULONG Flags,
  107. IN PVOID BaseAddress
  108. );
  109. NTSTATUS
  110. RtlDebugZeroHeap (
  111. IN PVOID HeapHandle,
  112. IN ULONG Flags
  113. );
  114. SIZE_T
  115. GetUCBytes (
  116. IN PHEAP Heap,
  117. IN OUT SIZE_T *ReservedSpace,
  118. IN OUT PULONG NoOfUCRs
  119. );
  120. #endif // NTOS_KERNEL_RUNTIME
  121. //
  122. // Local procedure prototypes
  123. //
  124. PHEAP_UNCOMMMTTED_RANGE
  125. RtlpCreateUnCommittedRange (
  126. IN PHEAP_SEGMENT Segment
  127. );
  128. VOID
  129. RtlpDestroyUnCommittedRange (
  130. IN PHEAP_SEGMENT Segment,
  131. IN PHEAP_UNCOMMMTTED_RANGE UnCommittedRange
  132. );
  133. VOID
  134. RtlpInsertUnCommittedPages (
  135. IN PHEAP_SEGMENT Segment,
  136. IN ULONG_PTR Address,
  137. IN SIZE_T Size
  138. );
  139. NTSTATUS
  140. RtlpDestroyHeapSegment (
  141. IN PHEAP_SEGMENT Segment
  142. );
  143. PHEAP_FREE_ENTRY
  144. RtlpExtendHeap (
  145. IN PHEAP Heap,
  146. IN SIZE_T AllocationSize
  147. );
  148. #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  149. #pragma alloc_text(PAGE, RtlCreateHeap)
  150. #pragma alloc_text(PAGE, RtlDestroyHeap)
  151. #pragma alloc_text(PAGE, RtlAllocateHeap)
  152. #pragma alloc_text(PAGE, RtlAllocateHeapSlowly)
  153. #pragma alloc_text(PAGE, RtlFreeHeapSlowly)
  154. #pragma alloc_text(PAGE, RtlFreeHeap)
  155. #pragma alloc_text(PAGE, RtlSizeHeap)
  156. #pragma alloc_text(PAGE, RtlZeroHeap)
  157. #pragma alloc_text(PAGE, RtlpGetExtraStuffPointer)
  158. #pragma alloc_text(PAGE, RtlpCreateUnCommittedRange)
  159. #pragma alloc_text(PAGE, RtlpDestroyUnCommittedRange)
  160. #pragma alloc_text(PAGE, RtlpInsertUnCommittedPages)
  161. #pragma alloc_text(PAGE, RtlpDestroyHeapSegment)
  162. #pragma alloc_text(PAGE, RtlpExtendHeap)
  163. #pragma alloc_text(PAGE, RtlpFindAndCommitPages)
  164. #pragma alloc_text(PAGE, RtlpInitializeHeapSegment)
  165. #pragma alloc_text(PAGE, RtlpCoalesceFreeBlocks)
  166. #pragma alloc_text(PAGE, RtlpDeCommitFreeBlock)
  167. #pragma alloc_text(PAGE, RtlpInsertFreeBlock)
  168. #pragma alloc_text(PAGE, RtlpGetSizeOfBigBlock)
  169. #pragma alloc_text(PAGE, RtlpCheckBusyBlockTail)
  170. #endif // ALLOC_PRAGMA
  171. PVOID
  172. RtlCreateHeap (
  173. IN ULONG Flags,
  174. IN PVOID HeapBase OPTIONAL,
  175. IN SIZE_T ReserveSize OPTIONAL,
  176. IN SIZE_T CommitSize OPTIONAL,
  177. IN PVOID Lock OPTIONAL,
  178. IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
  179. )
  180. /*++
  181. Routine Description:
  182. This routine initializes a heap.
  183. Arguments:
  184. Flags - Specifies optional attributes of the heap.
  185. Valid Flags Values:
  186. HEAP_NO_SERIALIZE - if set, then allocations and deallocations on
  187. this heap are NOT synchronized by these routines.
  188. HEAP_GROWABLE - if set, then the heap is a "sparse" heap where
  189. memory is committed only as necessary instead of
  190. being preallocated.
  191. HeapBase - if not NULL, this specifies the base address for memory
  192. to use as the heap. If NULL, memory is allocated by these routines.
  193. ReserveSize - if not zero, this specifies the amount of virtual address
  194. space to reserve for the heap.
  195. CommitSize - if not zero, this specifies the amount of virtual address
  196. space to commit for the heap. Must be less than ReserveSize. If
  197. zero, then defaults to one page.
  198. Lock - if not NULL, this parameter points to the resource lock to
  199. use. Only valid if HEAP_NO_SERIALIZE is NOT set.
  200. Parameters - optional heap parameters.
  201. Return Value:
  202. PVOID - a pointer to be used in accessing the created heap.
  203. --*/
  204. {
  205. ULONG_PTR HighestUserAddress;
  206. NTSTATUS Status;
  207. PHEAP Heap = NULL;
  208. PHEAP_SEGMENT Segment = NULL;
  209. PLIST_ENTRY FreeListHead;
  210. ULONG SizeOfHeapHeader;
  211. ULONG SegmentFlags;
  212. PVOID CommittedBase;
  213. PVOID UnCommittedBase;
  214. MEMORY_BASIC_INFORMATION MemoryInformation;
  215. SYSTEM_BASIC_INFORMATION SystemInformation;
  216. ULONG n;
  217. ULONG InitialCountOfUnusedUnCommittedRanges;
  218. SIZE_T MaximumHeapBlockSize;
  219. PVOID NextHeapHeaderAddress;
  220. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
  221. RTL_HEAP_PARAMETERS TempParameters;
  222. ULONG NtGlobalFlag = RtlGetNtGlobalFlags();
  223. #ifndef NTOS_KERNEL_RUNTIME
  224. PPEB Peb;
  225. #else // NTOS_KERNEL_RUNTIME
  226. extern SIZE_T MmHeapSegmentReserve;
  227. extern SIZE_T MmHeapSegmentCommit;
  228. extern SIZE_T MmHeapDeCommitTotalFreeThreshold;
  229. extern SIZE_T MmHeapDeCommitFreeBlockThreshold;
  230. #endif // NTOS_KERNEL_RUNTIME
  231. RTL_PAGED_CODE();
  232. #ifndef NTOS_KERNEL_RUNTIME
  233. #ifdef NTHEAP_ENABLED
  234. {
  235. if (Flags & NTHEAP_ENABLED_FLAG) {
  236. Heap = RtlCreateNtHeap( Flags, NULL );
  237. if (Heap != NULL) {
  238. return Heap;
  239. }
  240. Flags &= ~NTHEAP_ENABLED_FLAG;
  241. }
  242. }
  243. #endif // NTHEAP_ENABLED
  244. #endif // NTOS_KERNEL_RUNTIME
  245. //
  246. // Check if we should be using the page heap code. If not then turn
  247. // off any of the page heap flags before going on
  248. //
  249. #ifdef DEBUG_PAGE_HEAP
  250. if ( RtlpDebugPageHeap && ( HeapBase == NULL ) && ( Lock == NULL )) {
  251. PVOID PageHeap;
  252. PageHeap = RtlpDebugPageHeapCreate(
  253. Flags,
  254. HeapBase,
  255. ReserveSize,
  256. CommitSize,
  257. Lock,
  258. Parameters );
  259. if (PageHeap != NULL) {
  260. return PageHeap;
  261. }
  262. //
  263. // A `-1' value signals a recursive call from page heap
  264. // manager. We set this to null and continue creating
  265. // a normal heap. This small hack is required so that we
  266. // minimize the dependencies between the normal and the page
  267. // heap manager.
  268. //
  269. if ((SIZE_T)Parameters == (SIZE_T)-1) {
  270. Parameters = NULL;
  271. }
  272. }
  273. Flags &= ~( HEAP_PROTECTION_ENABLED |
  274. HEAP_BREAK_WHEN_OUT_OF_VM |
  275. HEAP_NO_ALIGNMENT );
  276. #endif // DEBUG_PAGE_HEAP
  277. //
  278. // If the caller does not want to skip heap validiation checks then we
  279. // need to validate the rest of the flags but simply masking out only
  280. // those flags that want on a create heap call
  281. //
  282. if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS)) {
  283. if (Flags & ~HEAP_CREATE_VALID_MASK) {
  284. HeapDebugPrint(( "Invalid flags (%08x) specified to RtlCreateHeap\n", Flags ));
  285. HeapDebugBreak( NULL );
  286. Flags &= HEAP_CREATE_VALID_MASK;
  287. }
  288. }
  289. //
  290. // The maximum heap block size is really 0x7f000 which is 0x80000 minus a
  291. // page. Maximum block size is 0xfe00 and granularity shift is 3.
  292. //
  293. MaximumHeapBlockSize = HEAP_MAXIMUM_BLOCK_SIZE << HEAP_GRANULARITY_SHIFT;
  294. //
  295. // Assume we're going to be successful until we're shown otherwise
  296. //
  297. Status = STATUS_SUCCESS;
  298. //
  299. // This part of the routine builds up local variable containing all the
  300. // parameters used to initialize the heap. First thing we do is zero
  301. // it out.
  302. //
  303. RtlZeroMemory( &TempParameters, sizeof( TempParameters ) );
  304. //
  305. // If our caller supplied the optional heap parameters then we'll
  306. // make sure the size is good and copy over them over to our
  307. // local copy
  308. //
  309. if (ARGUMENT_PRESENT( Parameters )) {
  310. try {
  311. if (Parameters->Length == sizeof( *Parameters )) {
  312. RtlCopyMemory( &TempParameters, Parameters, sizeof( *Parameters ) );
  313. }
  314. } except( EXCEPTION_EXECUTE_HANDLER ) {
  315. Status = GetExceptionCode();
  316. }
  317. if (!NT_SUCCESS( Status )) {
  318. return NULL;
  319. }
  320. }
  321. //
  322. // Set the parameter block to the local copy
  323. //
  324. Parameters = &TempParameters;
  325. //
  326. // If nt global flags tells us to always do tail or free checking
  327. // or to disable coalescing then force those bits set in the user
  328. // specified flags
  329. //
  330. if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) {
  331. Flags |= HEAP_TAIL_CHECKING_ENABLED;
  332. }
  333. if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) {
  334. Flags |= HEAP_FREE_CHECKING_ENABLED;
  335. }
  336. if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {
  337. Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
  338. }
  339. #ifndef NTOS_KERNEL_RUNTIME
  340. //
  341. // In the non kernel case we also check if we should
  342. // validate parameters, validate all, or do stack backtraces
  343. //
  344. Peb = NtCurrentPeb();
  345. if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) {
  346. Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
  347. }
  348. if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) {
  349. Flags |= HEAP_VALIDATE_ALL_ENABLED;
  350. }
  351. if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
  352. Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
  353. }
  354. //
  355. // Also in the non kernel case the PEB will have some state
  356. // variables that we need to set if the user hasn't specified
  357. // otherwise
  358. //
  359. if (Parameters->SegmentReserve == 0) {
  360. Parameters->SegmentReserve = Peb->HeapSegmentReserve;
  361. }
  362. if (Parameters->SegmentCommit == 0) {
  363. Parameters->SegmentCommit = Peb->HeapSegmentCommit;
  364. }
  365. if (Parameters->DeCommitFreeBlockThreshold == 0) {
  366. Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
  367. }
  368. if (Parameters->DeCommitTotalFreeThreshold == 0) {
  369. Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
  370. }
  371. #else // NTOS_KERNEL_RUNTIME
  372. //
  373. // In the kernel case Mm has some global variables that we set
  374. // into the paramters if the user hasn't specified otherwise
  375. //
  376. if (Parameters->SegmentReserve == 0) {
  377. Parameters->SegmentReserve = MmHeapSegmentReserve;
  378. }
  379. if (Parameters->SegmentCommit == 0) {
  380. Parameters->SegmentCommit = MmHeapSegmentCommit;
  381. }
  382. if (Parameters->DeCommitFreeBlockThreshold == 0) {
  383. Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
  384. }
  385. if (Parameters->DeCommitTotalFreeThreshold == 0) {
  386. Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
  387. }
  388. #endif // NTOS_KERNEL_RUNTIME
  389. //
  390. // Get the highest user address
  391. //
  392. if (!NT_SUCCESS(ZwQuerySystemInformation(SystemBasicInformation,
  393. &SystemInformation,
  394. sizeof(SystemInformation),
  395. NULL))) {
  396. return NULL;
  397. }
  398. HighestUserAddress = SystemInformation.MaximumUserModeAddress;
  399. //
  400. // If the user hasn't said what the largest allocation size is then
  401. // we should compute it as the difference between the highest and lowest
  402. // address less one page
  403. //
  404. if (Parameters->MaximumAllocationSize == 0) {
  405. Parameters->MaximumAllocationSize = (HighestUserAddress -
  406. (ULONG_PTR)MM_LOWEST_USER_ADDRESS -
  407. PAGE_SIZE );
  408. }
  409. //
  410. // Set the virtual memory threshold to be non zero and not more than the
  411. // maximum heap block size of 0x7f000. If the user specified one that is
  412. // too large we automatically and silently drop it down.
  413. //
  414. if ((Parameters->VirtualMemoryThreshold == 0) ||
  415. (Parameters->VirtualMemoryThreshold > MaximumHeapBlockSize)) {
  416. Parameters->VirtualMemoryThreshold = MaximumHeapBlockSize;
  417. }
  418. //
  419. // The default commit size is MINIMUM_HEAP_COMMIT and the default
  420. // reserve size is 64 pages.
  421. //
  422. if (!ARGUMENT_PRESENT( CommitSize )) {
  423. CommitSize = MINIMUM_HEAP_COMMIT;
  424. if (!ARGUMENT_PRESENT( ReserveSize )) {
  425. ReserveSize = 64 * CommitSize;
  426. } else {
  427. ReserveSize = ROUND_UP_TO_POWER2( ReserveSize,
  428. MINIMUM_HEAP_COMMIT );
  429. }
  430. } else {
  431. //
  432. // The heap actually uses space that is reserved and commited
  433. // to store internal data structures (the LOCK,
  434. // the HEAP_PSEUDO_TAG, etc.). These structures can be larger than
  435. // 4K especially on a 64-bit build. So, make sure the commit
  436. // is at least 8K in length.
  437. //
  438. CommitSize = ROUND_UP_TO_POWER2(CommitSize, MINIMUM_HEAP_COMMIT);
  439. if (!ARGUMENT_PRESENT( ReserveSize )) {
  440. ReserveSize = ROUND_UP_TO_POWER2( CommitSize, 16 * PAGE_SIZE );
  441. } else {
  442. ReserveSize = ROUND_UP_TO_POWER2( ReserveSize,
  443. MINIMUM_HEAP_COMMIT );
  444. //
  445. // If the CommitSize is larger than the ReservedSize, adjust
  446. // it to the ReserveSize. Reserved size is already rounded up to
  447. // MINIMUM_HEAP_COMMIT.
  448. //
  449. if ( CommitSize > ReserveSize ) {
  450. CommitSize = ReserveSize;
  451. }
  452. }
  453. }
  454. #ifndef NTOS_KERNEL_RUNTIME
  455. //
  456. // In the non kernel case check if we are creating a debug heap
  457. // the test checks that skip validation checks is false.
  458. //
  459. if (DEBUG_HEAP( Flags )) {
  460. return RtlDebugCreateHeap( Flags,
  461. HeapBase,
  462. ReserveSize,
  463. CommitSize,
  464. Lock,
  465. Parameters );
  466. }
  467. #endif // NTOS_KERNEL_RUNTIME
  468. //
  469. // Compute the size of the heap which will be the
  470. // heap struct itself and if we are to serialize with
  471. // out own lock then add room for the lock. If the
  472. // user did not supply the lock then set the lock
  473. // variable to -1.
  474. //
  475. SizeOfHeapHeader = sizeof( HEAP );
  476. if (!(Flags & HEAP_NO_SERIALIZE)) {
  477. if (ARGUMENT_PRESENT( Lock )) {
  478. Flags |= HEAP_LOCK_USER_ALLOCATED;
  479. } else {
  480. SizeOfHeapHeader += sizeof( HEAP_LOCK );
  481. Lock = (PHEAP_LOCK)-1;
  482. }
  483. } else if (ARGUMENT_PRESENT( Lock )) {
  484. //
  485. // In this error case the call said not to serialize but also fed us
  486. // a lock
  487. //
  488. return NULL;
  489. }
  490. //
  491. // See if caller allocate the space for the heap.
  492. //
  493. if (ARGUMENT_PRESENT( HeapBase )) {
  494. //
  495. // The call specified a heap base now check if there is
  496. // a caller supplied commit routine
  497. //
  498. if (Parameters->CommitRoutine != NULL) {
  499. //
  500. // The caller specified a commit routine so the caller
  501. // also needs to have given us certain parameters and make
  502. // sure the heap is not growable. Otherwise it is an error
  503. //
  504. if ((Parameters->InitialCommit == 0) ||
  505. (Parameters->InitialReserve == 0) ||
  506. (Parameters->InitialCommit > Parameters->InitialReserve) ||
  507. (Flags & HEAP_GROWABLE)) {
  508. return NULL;
  509. }
  510. //
  511. // Set the commited base and the uncommited base to the
  512. // proper pointers within the heap.
  513. //
  514. CommittedBase = HeapBase;
  515. UnCommittedBase = (PCHAR)CommittedBase + Parameters->InitialCommit;
  516. ReserveSize = Parameters->InitialReserve;
  517. //
  518. // Zero out a page of the heap where our first part goes
  519. //
  520. RtlZeroMemory( CommittedBase, Parameters->InitialCommit );
  521. } else {
  522. //
  523. // The user gave us space but not commit routine
  524. // So query the base to get its size
  525. //
  526. Status = ZwQueryVirtualMemory( NtCurrentProcess(),
  527. HeapBase,
  528. MemoryBasicInformation,
  529. &MemoryInformation,
  530. sizeof( MemoryInformation ),
  531. NULL );
  532. if (!NT_SUCCESS( Status )) {
  533. return NULL;
  534. }
  535. //
  536. // Make sure the user gave us a base address for this block
  537. // and that the memory is not free
  538. //
  539. if (MemoryInformation.BaseAddress != HeapBase) {
  540. return NULL;
  541. }
  542. if (MemoryInformation.State == MEM_FREE) {
  543. return NULL;
  544. }
  545. //
  546. // Set our commit base to the start of the range
  547. //
  548. CommittedBase = MemoryInformation.BaseAddress;
  549. //
  550. // If the memory is committed then
  551. // we can zero out a page worth
  552. //
  553. if (MemoryInformation.State == MEM_COMMIT) {
  554. RtlZeroMemory( CommittedBase, PAGE_SIZE );
  555. //
  556. // Set the commit size and uncommitted base according
  557. // to the start of the vm
  558. //
  559. CommitSize = MemoryInformation.RegionSize;
  560. UnCommittedBase = (PCHAR)CommittedBase + CommitSize;
  561. //
  562. // Find out the uncommited base is reserved and if so
  563. // the update the reserve size accordingly.
  564. //
  565. Status = ZwQueryVirtualMemory( NtCurrentProcess(),
  566. UnCommittedBase,
  567. MemoryBasicInformation,
  568. &MemoryInformation,
  569. sizeof( MemoryInformation ),
  570. NULL );
  571. ReserveSize = CommitSize;
  572. if ((NT_SUCCESS( Status )) &&
  573. (MemoryInformation.State == MEM_RESERVE)) {
  574. ReserveSize += MemoryInformation.RegionSize;
  575. }
  576. } else {
  577. //
  578. // The memory the user gave us is not committed so dummy
  579. // up these small numbers
  580. //
  581. CommitSize = MINIMUM_HEAP_COMMIT;
  582. UnCommittedBase = CommittedBase;
  583. }
  584. }
  585. //
  586. // This user gave us a base and we've just taken care of the committed
  587. // bookkeeping. So mark this segment as user supplied and set the
  588. // heap
  589. //
  590. SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
  591. Heap = (PHEAP)HeapBase;
  592. } else {
  593. //
  594. // The user did not specify a heap base so we have to allocate the
  595. // vm here. First make sure the user did not give us a commit routine
  596. //
  597. if (Parameters->CommitRoutine != NULL) {
  598. return NULL;
  599. }
  600. //
  601. // Reserve the amount of virtual address space requested.
  602. //
  603. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  604. (PVOID *)&Heap,
  605. 0,
  606. &ReserveSize,
  607. MEM_RESERVE,
  608. PAGE_READWRITE );
  609. if (!NT_SUCCESS( Status )) {
  610. return NULL;
  611. }
  612. //
  613. // Indicate that this segment is not user supplied
  614. //
  615. SegmentFlags = 0;
  616. //
  617. // Set the default commit size to one page
  618. //
  619. if (!ARGUMENT_PRESENT( CommitSize )) {
  620. CommitSize = MINIMUM_HEAP_COMMIT;
  621. }
  622. //
  623. // Set the committed and uncommitted base to be the same the following
  624. // code will actually commit the page for us
  625. //
  626. CommittedBase = Heap;
  627. UnCommittedBase = Heap;
  628. }
  629. //
  630. // At this point we have a heap pointer, committed base, uncommitted base,
  631. // segment flags, commit size, and reserve size. If the committed and
  632. // uncommited base are the same then we need to commit the amount
  633. // specified by the commit size
  634. //
  635. if (CommittedBase == UnCommittedBase) {
  636. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  637. (PVOID *)&CommittedBase,
  638. 0,
  639. &CommitSize,
  640. MEM_COMMIT,
  641. PAGE_READWRITE );
  642. //
  643. // In the non successful case we need to back out any vm reservation
  644. // we did earlier
  645. //
  646. if (!NT_SUCCESS( Status )) {
  647. if (!ARGUMENT_PRESENT(HeapBase)) {
  648. //
  649. // Return the reserved virtual address space.
  650. //
  651. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  652. (PVOID *)&Heap,
  653. &ReserveSize,
  654. MEM_RELEASE );
  655. }
  656. return NULL;
  657. }
  658. //
  659. // The new uncommitted base is not adjusted above what we just
  660. // committed
  661. //
  662. UnCommittedBase = (PVOID)((PCHAR)UnCommittedBase + CommitSize);
  663. }
  664. //
  665. // At this point we have memory for the start of the heap committed and
  666. // ready to be initialized. So now we need initialize the heap
  667. //
  668. //
  669. // Calculate the end of the heap header and make room for 8 uncommitted
  670. // range structures. Once we have the room for them then chain them
  671. // together and null terminate the chain
  672. //
  673. NextHeapHeaderAddress = Heap + 1;
  674. UnCommittedRange = (PHEAP_UNCOMMMTTED_RANGE)ROUND_UP_TO_POWER2( NextHeapHeaderAddress,
  675. sizeof( QUAD ) );
  676. InitialCountOfUnusedUnCommittedRanges = 8;
  677. SizeOfHeapHeader += InitialCountOfUnusedUnCommittedRanges * sizeof( *UnCommittedRange );
  678. //
  679. // What a hack Pp is really a pointer to the next field of the
  680. // uncommitted range structure. So we set next by setting through Pp
  681. //
  682. pp = &Heap->UnusedUnCommittedRanges;
  683. while (InitialCountOfUnusedUnCommittedRanges--) {
  684. *pp = UnCommittedRange;
  685. pp = &UnCommittedRange->Next;
  686. UnCommittedRange += 1;
  687. }
  688. NextHeapHeaderAddress = UnCommittedRange;
  689. *pp = NULL;
  690. //
  691. // Check if tagging is enabled in global flags. This check is always true
  692. // in a debug build.
  693. //
  694. // If tagging is enabled then make room for 129 pseudo tag heap entry.
  695. // Which is one more than the number of free lists. Also point the heap
  696. // header to this array of pseudo tags entries.
  697. //
  698. if (IS_HEAP_TAGGING_ENABLED()) {
  699. Heap->PseudoTagEntries = (PHEAP_PSEUDO_TAG_ENTRY)ROUND_UP_TO_POWER2( NextHeapHeaderAddress,
  700. sizeof( QUAD ) );
  701. SizeOfHeapHeader += HEAP_NUMBER_OF_PSEUDO_TAG * sizeof( HEAP_PSEUDO_TAG_ENTRY );
  702. //
  703. // Update the next address with the number of pseudotags
  704. // (The math is right here because Heap->PseudoTagEntries is of
  705. // type PHEAP_PSEUDO_TAG_ENTRY)
  706. //
  707. NextHeapHeaderAddress = Heap->PseudoTagEntries + HEAP_NUMBER_OF_PSEUDO_TAG;
  708. }
  709. //
  710. // Round the size of the heap header to the next 8 byte boundary
  711. //
  712. SizeOfHeapHeader = (ULONG) ROUND_UP_TO_POWER2( SizeOfHeapHeader,
  713. HEAP_GRANULARITY );
  714. //
  715. // If the sizeof the heap header is larger than the native
  716. // page size, you have a problem. Further, if the CommitSize passed
  717. // in was smaller than the SizeOfHeapHeader, you may not even make it
  718. // this far before death...
  719. //
  720. // HeapDbgPrint() doesn't work for IA64 yet.
  721. //
  722. // HeapDbgPrint(("Size of the heap header is %u bytes, commit was %u bytes\n", SizeOfHeapHeader, (ULONG) CommitSize));
  723. //
  724. //
  725. // Fill in the heap header fields
  726. //
  727. Heap->Entry.Size = (USHORT)(SizeOfHeapHeader >> HEAP_GRANULARITY_SHIFT);
  728. Heap->Entry.Flags = HEAP_ENTRY_BUSY;
  729. Heap->Signature = HEAP_SIGNATURE;
  730. Heap->Flags = Flags;
  731. Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE |
  732. HEAP_GENERATE_EXCEPTIONS |
  733. HEAP_ZERO_MEMORY |
  734. HEAP_REALLOC_IN_PLACE_ONLY |
  735. HEAP_VALIDATE_PARAMETERS_ENABLED |
  736. HEAP_VALIDATE_ALL_ENABLED |
  737. HEAP_TAIL_CHECKING_ENABLED |
  738. HEAP_CREATE_ALIGN_16 |
  739. HEAP_FREE_CHECKING_ENABLED));
  740. // Heap->FreeListsInUseTerminate = 0xFFFF;
  741. Heap->u2.DecommitCount = 0;
  742. Heap->HeaderValidateLength = (USHORT)((PCHAR)NextHeapHeaderAddress - (PCHAR)Heap);
  743. Heap->HeaderValidateCopy = NULL;
  744. //
  745. // Initialize the free list to be all empty
  746. //
  747. FreeListHead = &Heap->FreeLists[ 0 ];
  748. n = HEAP_MAXIMUM_FREELISTS;
  749. while (n--) {
  750. InitializeListHead( FreeListHead );
  751. FreeListHead++;
  752. }
  753. //
  754. // Make it so that there a no big block allocations
  755. //
  756. InitializeListHead( &Heap->VirtualAllocdBlocks );
  757. //
  758. // Initialize the critical section that controls access to
  759. // the free list. If the lock variable is -1 then the caller
  760. // did not supply a lock so we need to make room for one
  761. // and initialize it.
  762. //
  763. if (Lock == (PHEAP_LOCK)-1) {
  764. Lock = (PHEAP_LOCK)NextHeapHeaderAddress;
  765. Status = RtlInitializeLockRoutine( Lock );
  766. if (!NT_SUCCESS( Status )) {
  767. if (!ARGUMENT_PRESENT(HeapBase)) {
  768. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  769. (PVOID *)&Heap,
  770. &ReserveSize,
  771. MEM_RELEASE );
  772. }
  773. return NULL;
  774. }
  775. NextHeapHeaderAddress = (PHEAP_LOCK)Lock + 1;
  776. }
  777. Heap->LockVariable = Lock;
  778. Heap->LastSegmentIndex = 0;
  779. //
  780. // Initialize the first segment for the heap
  781. //
  782. if (!RtlpInitializeHeapSegment( Heap,
  783. (PHEAP_SEGMENT)((PCHAR)Heap + SizeOfHeapHeader),
  784. 0,
  785. SegmentFlags,
  786. CommittedBase,
  787. UnCommittedBase,
  788. (PCHAR)CommittedBase + ReserveSize )) {
  789. if (!ARGUMENT_PRESENT(HeapBase)) {
  790. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  791. (PVOID *)&Heap,
  792. &ReserveSize,
  793. MEM_RELEASE );
  794. }
  795. return NULL;
  796. }
  797. //
  798. // Fill in additional heap entry fields
  799. //
  800. Heap->ProcessHeapsListIndex = 0;
  801. Heap->SegmentReserve = Parameters->SegmentReserve;
  802. Heap->SegmentCommit = Parameters->SegmentCommit;
  803. Heap->DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_GRANULARITY_SHIFT;
  804. Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_GRANULARITY_SHIFT;
  805. Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize;
  806. Heap->VirtualMemoryThreshold = (ULONG) (ROUND_UP_TO_POWER2( Parameters->VirtualMemoryThreshold,
  807. HEAP_GRANULARITY ) >> HEAP_GRANULARITY_SHIFT);
  808. Heap->CommitRoutine = Parameters->CommitRoutine;
  809. //
  810. // We either align the heap at 16 or 8 byte boundaries. The AlignRound
  811. // and AlignMask are used to bring allocation sizes up to the next
  812. // boundary. The align round includes the heap header and the optional
  813. // check tail size
  814. //
  815. if (Flags & HEAP_CREATE_ALIGN_16) {
  816. Heap->AlignRound = 15 + sizeof( HEAP_ENTRY );
  817. Heap->AlignMask = ~((ULONG_PTR)15);
  818. } else {
  819. Heap->AlignRound = HEAP_GRANULARITY - 1 + sizeof( HEAP_ENTRY );
  820. Heap->AlignMask = ~((ULONG_PTR)HEAP_GRANULARITY - 1);
  821. }
  822. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
  823. Heap->AlignRound += CHECK_HEAP_TAIL_SIZE;
  824. }
  825. #ifndef NTOS_KERNEL_RUNTIME
  826. //
  827. // In the non kernel case we need to add this heap to the processes heap
  828. // list
  829. //
  830. RtlpAddHeapToProcessList( Heap );
  831. //
  832. // Initialize the heap lookaside lists. This is only for the user mode
  833. // heap and the heap contains a pointer to the lookaside list array.
  834. // The array is sized the same as the dedicated free list. First we
  835. // allocate space for the lookaside list and then we initialize each
  836. // lookaside list.
  837. //
  838. // But the caller asked for no serialize or asked for non growable
  839. // heap then we won't enable the lookaside lists.
  840. //
  841. Heap->FrontEndHeap = NULL;
  842. Heap->FrontHeapLockCount = 0;
  843. Heap->FrontEndHeapType = 0;
  844. if ((!(Flags & HEAP_NO_SERIALIZE)) &&
  845. ( (Flags & HEAP_GROWABLE)) &&
  846. (!(RtlpDisableHeapLookaside & HEAP_COMPAT_DISABLE_LOOKASIDES))) {
  847. //
  848. // We do not allow creation of the cache heap if the tags are enabled.
  849. // We use the tag field to sign our private blocks
  850. //
  851. if (RtlpIsLowFragHeapEnabled() &&
  852. !IS_HEAP_TAGGING_ENABLED()) {
  853. RtlpActivateLowFragmentationHeap(Heap);
  854. } else {
  855. ULONG i;
  856. Heap->FrontEndHeap = RtlAllocateHeap( Heap,
  857. HEAP_ZERO_MEMORY,
  858. sizeof(HEAP_LOOKASIDE) * HEAP_MAXIMUM_FREELISTS );
  859. if (Heap->FrontEndHeap != NULL) {
  860. Heap->FrontEndHeapType = HEAP_FRONT_LOOKASIDE;
  861. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
  862. //
  863. // N.B. we should call here the function:
  864. //
  865. // RtlpInitializeHeapLookaside( &(((PHEAP_LOOKASIDE)(Heap->Lookaside))[i]), 32 );
  866. //
  867. // But for performance reasons, because the most fields are 0,
  868. // we've set the flag HEAP_ZERO_MEMORY in allocation above and we'll
  869. // initialize the only two NON-NULL fields: Depth and MaximumDepth
  870. // IceCap data showed that RtlHeapCreate spends ~30% of the time within
  871. // these calls.
  872. //
  873. // N.B. This works based on assumption that
  874. // RtlInitializeSListHead zeroed the SLIST_HEADER structure
  875. //
  876. PHEAP_LOOKASIDE HeapLookaside = &(((PHEAP_LOOKASIDE)(Heap->FrontEndHeap))[i]);
  877. HeapLookaside->Depth = MINIMUM_LOOKASIDE_DEPTH;
  878. HeapLookaside->MaximumDepth = 256; //Depth;
  879. }
  880. }
  881. }
  882. }
  883. if( IsHeapLogging( Heap )) {
  884. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  885. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  886. USHORT ReqSize = sizeof(HEAP_EVENT_CREATE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  887. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  888. if(pEventHeader && pThreadLocalData) {
  889. PHEAP_EVENT_CREATE pHeapEvent = (PHEAP_EVENT_CREATE)((SIZE_T)pEventHeader
  890. +(SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  891. pEventHeader->Packet.Size = (USHORT) ReqSize;
  892. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_CREATE;
  893. pHeapEvent->HeapHandle = (PVOID)Heap;
  894. pHeapEvent->Flags = Flags;
  895. ReleaseBufferLocation(pThreadLocalData);
  896. }
  897. }
  898. #endif // NTOS_KERNEL_RUNTIME
  899. //
  900. // And return the fully initialized heap to our caller
  901. //
  902. return (PVOID)Heap;
  903. }
  904. PVOID
  905. RtlDestroyHeap (
  906. IN PVOID HeapHandle
  907. )
  908. /*++
  909. Routine Description:
  910. This routine is the opposite of Rtl Create Heap. It tears down an
  911. existing heap structure.
  912. Arguments:
  913. HeapHandle - Supplies a pointer to the heap being destroyed
  914. Return Value:
  915. PVOID - Returns null if the heap was destroyed completely and a
  916. pointer back to the heap if for some reason the heap could
  917. not be destroyed.
  918. --*/
  919. {
  920. PHEAP Heap = (PHEAP)HeapHandle;
  921. PHEAP_SEGMENT Segment;
  922. PHEAP_UCR_SEGMENT UCRSegments;
  923. PLIST_ENTRY Head, Next;
  924. PVOID BaseAddress;
  925. SIZE_T RegionSize;
  926. UCHAR SegmentIndex;
  927. PVOID LowFragmentationHeap;
  928. //
  929. // Validate that HeapAddress points to a HEAP structure.
  930. //
  931. RTL_PAGED_CODE();
  932. if (HeapHandle == NULL) {
  933. HeapDebugPrint(( "Ignoring RtlDestroyHeap( NULL )\n" ));
  934. return NULL;
  935. }
  936. #ifndef NTOS_KERNEL_RUNTIME
  937. #ifdef NTHEAP_ENABLED
  938. {
  939. if (Heap->Flags & NTHEAP_ENABLED_FLAG) {
  940. return RtlDestroyNtHeap( HeapHandle );
  941. }
  942. }
  943. #endif // NTHEAP_ENABLED
  944. #endif // NTOS_KERNEL_RUNTIME
  945. //
  946. // Check if this is the debug version of heap using page allocation
  947. // with guard pages
  948. //
  949. IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle,
  950. RtlpDebugPageHeapDestroy( HeapHandle ));
  951. #ifndef NTOS_KERNEL_RUNTIME
  952. //
  953. // In the non kernel case check if this is the debug version of heap
  954. // and of so then call the debug version to do the teardown
  955. //
  956. if (DEBUG_HEAP( Heap->Flags )) {
  957. if (!RtlDebugDestroyHeap( HeapHandle )) {
  958. return HeapHandle;
  959. }
  960. }
  961. //
  962. // We are not allowed to destroy the process heap
  963. //
  964. if (HeapHandle == NtCurrentPeb()->ProcessHeap) {
  965. return HeapHandle;
  966. }
  967. if (LowFragmentationHeap = RtlpGetLowFragHeap(Heap)) {
  968. RtlpDestroyLowFragHeap(LowFragmentationHeap);
  969. }
  970. #endif // NTOS_KERNEL_RUNTIME
  971. //
  972. // For every big allocation we remove it from the list and free the
  973. // vm
  974. //
  975. Head = &Heap->VirtualAllocdBlocks;
  976. Next = Head->Flink;
  977. while (Head != Next) {
  978. BaseAddress = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
  979. Next = Next->Flink;
  980. RegionSize = 0;
  981. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  982. (PVOID *)&BaseAddress,
  983. &RegionSize,
  984. MEM_RELEASE );
  985. }
  986. #ifndef NTOS_KERNEL_RUNTIME
  987. //
  988. // In the non kernel case we need to destroy any heap tags we have setup
  989. // and remove this heap from the process heap list
  990. //
  991. RtlpDestroyTags( Heap );
  992. RtlpRemoveHeapFromProcessList( Heap );
  993. #endif // NTOS_KERNEL_RUNTIME
  994. //
  995. // If the heap is serialized, delete the critical section created
  996. // by RtlCreateHeap.
  997. //
  998. if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
  999. if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED)) {
  1000. (VOID)RtlDeleteLockRoutine( Heap->LockVariable );
  1001. }
  1002. Heap->LockVariable = NULL;
  1003. }
  1004. //
  1005. // For every uncommitted segment we free its vm
  1006. //
  1007. UCRSegments = Heap->UCRSegments;
  1008. Heap->UCRSegments = NULL;
  1009. while (UCRSegments) {
  1010. BaseAddress = UCRSegments;
  1011. UCRSegments = UCRSegments->Next;
  1012. RegionSize = 0;
  1013. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  1014. &BaseAddress,
  1015. &RegionSize,
  1016. MEM_RELEASE );
  1017. }
  1018. #ifndef NTOS_KERNEL_RUNTIME
  1019. //
  1020. // Free the large block index, if we have one
  1021. //
  1022. if (Heap->LargeBlocksIndex) {
  1023. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  1024. //
  1025. // Save the commited size for the index.
  1026. //
  1027. RegionSize = HeapIndex->VirtualMemorySize;
  1028. Heap->LargeBlocksIndex = NULL;
  1029. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  1030. &HeapIndex,
  1031. &RegionSize,
  1032. MEM_RELEASE );
  1033. }
  1034. #endif // NTOS_KERNEL_RUNTIME
  1035. //
  1036. // For every segment in the heap we call a worker routine to
  1037. // destroy the segment
  1038. //
  1039. SegmentIndex = HEAP_MAXIMUM_SEGMENTS;
  1040. while (SegmentIndex--) {
  1041. Segment = Heap->Segments[ SegmentIndex ];
  1042. if (Segment) {
  1043. RtlpDestroyHeapSegment( Segment );
  1044. }
  1045. }
  1046. #ifndef NTOS_KERNEL_RUNTIME
  1047. if( IsHeapLogging( HeapHandle ) ) {
  1048. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1049. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1050. USHORT ReqSize = sizeof(NTDLL_EVENT_COMMON) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1051. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize );
  1052. if(pEventHeader && pThreadLocalData) {
  1053. PNTDLL_EVENT_COMMON pHeapEvent = (PNTDLL_EVENT_COMMON)( (SIZE_T)pEventHeader
  1054. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1055. pEventHeader->Packet.Size = (USHORT) ReqSize;
  1056. pEventHeader->Packet.HookId= PERFINFO_LOG_TYPE_HEAP_DESTROY;
  1057. pHeapEvent->Handle = (PVOID)HeapHandle;
  1058. ReleaseBufferLocation(pThreadLocalData);
  1059. }
  1060. }
  1061. #endif // NTOS_KERNEL_RUNTIME
  1062. //
  1063. // And we return to our caller
  1064. //
  1065. return NULL;
  1066. }
  1067. PVOID
  1068. RtlAllocateHeap (
  1069. IN PVOID HeapHandle,
  1070. IN ULONG Flags,
  1071. IN SIZE_T Size
  1072. )
  1073. /*++
  1074. Routine Description:
  1075. This routine allocates a memory of the specified size from the specified
  1076. heap.
  1077. Arguments:
  1078. HeapHandle - Supplies a pointer to an initialized heap structure
  1079. Flags - Specifies the set of flags to use to control the allocation
  1080. Size - Specifies the size, in bytes, of the allocation
  1081. Return Value:
  1082. PVOID - returns a pointer to the newly allocated block
  1083. --*/
  1084. {
  1085. PHEAP Heap = (PHEAP)HeapHandle;
  1086. PULONG FreeListsInUse;
  1087. ULONG FreeListsInUseUlong;
  1088. SIZE_T AllocationSize;
  1089. SIZE_T FreeSize, AllocationIndex;
  1090. PLIST_ENTRY FreeListHead, Next;
  1091. PHEAP_ENTRY BusyBlock;
  1092. PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
  1093. ULONG InUseIndex;
  1094. UCHAR FreeFlags;
  1095. NTSTATUS Status;
  1096. EXCEPTION_RECORD ExceptionRecord;
  1097. PVOID ReturnValue = NULL;
  1098. BOOLEAN LockAcquired = FALSE;
  1099. SIZE_T BlockSize = 0;
  1100. PVOID FrontEndHeap = NULL;
  1101. HEAP_PERF_DECLARE_TIMER();
  1102. RTL_PAGED_CODE();
  1103. #ifndef NTOS_KERNEL_RUNTIME
  1104. #ifdef NTHEAP_ENABLED
  1105. {
  1106. if (Heap->Flags & NTHEAP_ENABLED_FLAG) {
  1107. return RtlAllocateNtHeap( HeapHandle,
  1108. Flags,
  1109. Size);
  1110. }
  1111. }
  1112. #endif // NTHEAP_ENABLED
  1113. #endif // NTOS_KERNEL_RUNTIME
  1114. //
  1115. // Take the callers flags and add in the flags that we must forcibly set
  1116. // in the heap
  1117. //
  1118. Flags |= Heap->ForceFlags;
  1119. //
  1120. // Check for special features that force us to call the slow, do-everything
  1121. // version. We do everything slow for any of the following flags.
  1122. //
  1123. // HEAP_SLOW_FLAGS defined as 0x6f030f60
  1124. //
  1125. // HEAP_DEBUG_FLAGS, defined as 0x69020000 (heappriv.h)
  1126. //
  1127. // HEAP_VALIDATE_PARAMETERS_ENABLED 0x40000000 (heap.h)
  1128. //
  1129. // HEAP_VALIDATE_ALL_ENABLED 0x20000000 (heap.h)
  1130. //
  1131. // HEAP_CAPTURE_STACK_BACKTRACES 0x08000000 (heap.h)
  1132. //
  1133. // HEAP_CREATE_ENABLE_TRACING 0x00020000 (ntrtl.h winnt obsolete)
  1134. //
  1135. // HEAP_FLAG_PAGE_ALLOCS 0x01000000 (heappage.h)
  1136. //
  1137. // HEAP_SETTABLE_USER_FLAGS 0x00000E00 (ntrtl.h)
  1138. //
  1139. // HEAP_NEED_EXTRA_FLAGS 0x0f000100 (heap.h)
  1140. //
  1141. // HEAP_CREATE_ALIGN_16 0x00010000 (ntrtl.h winnt obsolete)
  1142. //
  1143. // HEAP_FREE_CHECKING_ENABLED 0x00000040 (ntrtl.h winnt)
  1144. //
  1145. // HEAP_TAIL_CHECKING_ENABLED 0x00000020 (ntrtl.h winnt )
  1146. //
  1147. // We also do everything slow if the size is greater than max long
  1148. //
  1149. if ((Flags & HEAP_SLOW_FLAGS) || (Size >= 0x80000000)) {
  1150. ReturnValue = RtlAllocateHeapSlowly( HeapHandle, Flags, Size );
  1151. if ( (ReturnValue == NULL) &&
  1152. (Flags & HEAP_GENERATE_EXCEPTIONS) ) {
  1153. //
  1154. // Construct an exception record.
  1155. //
  1156. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  1157. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  1158. ExceptionRecord.NumberParameters = 1;
  1159. ExceptionRecord.ExceptionFlags = 0;
  1160. ExceptionRecord.ExceptionInformation[ 0 ] = Size;
  1161. RtlRaiseException( &ExceptionRecord );
  1162. }
  1163. return ReturnValue;
  1164. }
  1165. #ifndef NTOS_KERNEL_RUNTIME
  1166. if ((FrontEndHeap = RtlpGetLowFragHeap(Heap))
  1167. &&
  1168. RtlpIsFrontHeapUnlocked(Heap)
  1169. &&
  1170. !(Flags & (HEAP_NO_CACHE_BLOCK | HEAP_NO_SERIALIZE))) {
  1171. ReturnValue = RtlpLowFragHeapAlloc( FrontEndHeap, (Size ? Size : 1) );
  1172. if (ReturnValue != NULL) {
  1173. if (Flags & HEAP_ZERO_MEMORY) {
  1174. RtlZeroMemory( ReturnValue, Size );
  1175. }
  1176. if( IsHeapLogging( HeapHandle ) ) {
  1177. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1178. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1179. USHORT ReqSize = sizeof(HEAP_EVENT_ALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1180. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  1181. if(pEventHeader && pThreadLocalData) {
  1182. PHEAP_EVENT_ALLOC pHeapEvent = (PHEAP_EVENT_ALLOC)((SIZE_T)pEventHeader
  1183. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1184. pEventHeader->Packet.Size = (USHORT) ReqSize;
  1185. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_ALLOC;
  1186. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  1187. pHeapEvent->Size = Size;
  1188. pHeapEvent->Address = (PVOID)ReturnValue;
  1189. pHeapEvent->Source = MEMORY_FROM_LOWFRAG;
  1190. ReleaseBufferLocation(pThreadLocalData);
  1191. }
  1192. }
  1193. return ReturnValue;
  1194. }
  1195. }
  1196. #endif // NTOS_KERNEL_RUNTIME
  1197. //
  1198. // At this point we know we are doing everything in this routine
  1199. // and not taking the slow route.
  1200. //
  1201. // Round the requested size up to the allocation granularity. Note
  1202. // that if the request is for 0 bytes, we still allocate memory, because
  1203. // we add in an extra 1 byte to protect ourselves from mistakes.
  1204. //
  1205. // Allocation size will be either 16, 24, 32, ...
  1206. // Allocation index will be 2, 3, 4, ...
  1207. //
  1208. // Note that allocation size 8 is skipped and are indices 0 and 1
  1209. //
  1210. AllocationSize = ((Size ? Size : 1) + HEAP_GRANULARITY - 1 + sizeof( HEAP_ENTRY ))
  1211. & ~(HEAP_GRANULARITY -1);
  1212. #ifndef NTOS_KERNEL_RUNTIME
  1213. //
  1214. // Adjust the size to page boundary to reduce the virtual address fragmentation
  1215. //
  1216. if (FrontEndHeap
  1217. &&
  1218. (AllocationSize > HEAP_LARGEST_LFH_BLOCK)) {
  1219. AllocationSize = ROUND_UP_TO_POWER2(AllocationSize, PAGE_SIZE);
  1220. }
  1221. #endif // NTOS_KERNEL_RUNTIME
  1222. AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
  1223. //
  1224. // If there is a lookaside list and the index is within limits then
  1225. // try and allocate from the lookaside list. We'll actually capture
  1226. // the lookaside pointer from the heap and only use the captured pointer.
  1227. // This will take care of the condition where a walk or lock heap can
  1228. // cause us to check for a non null pointer and then have it become null
  1229. // when we read it again. If it is non null to start with then even if
  1230. // the user walks or locks the heap via another thread the pointer to
  1231. // still valid here so we can still try and do a lookaside list pop.
  1232. //
  1233. #ifndef NTOS_KERNEL_RUNTIME
  1234. {
  1235. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  1236. if ((Lookaside != NULL) &&
  1237. RtlpIsFrontHeapUnlocked(Heap) &&
  1238. (AllocationIndex < HEAP_MAXIMUM_FREELISTS)) {
  1239. //
  1240. // If the number of operation elapsed operations is 128 times the
  1241. // lookaside depth then it is time to adjust the depth
  1242. //
  1243. if ((LONG)(Lookaside[AllocationIndex].TotalAllocates - Lookaside[AllocationIndex].LastTotalAllocates) >=
  1244. (Lookaside[AllocationIndex].Depth * 128)) {
  1245. RtlpAdjustHeapLookasideDepth(&(Lookaside[AllocationIndex]));
  1246. }
  1247. ReturnValue = RtlpAllocateFromHeapLookaside(&(Lookaside[AllocationIndex]));
  1248. if (ReturnValue != NULL) {
  1249. PHEAP_ENTRY BusyBlock;
  1250. BusyBlock = ((PHEAP_ENTRY)ReturnValue) - 1;
  1251. BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
  1252. BusyBlock->SmallTagIndex = 0;
  1253. if (Flags & HEAP_ZERO_MEMORY) {
  1254. RtlZeroMemory( ReturnValue, Size );
  1255. }
  1256. #ifndef NTOS_KERNEL_RUNTIME
  1257. if( IsHeapLogging( HeapHandle ) && (TraceLevel & LOG_LOOKASIDE)) {
  1258. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1259. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1260. USHORT ReqSize = sizeof(HEAP_EVENT_ALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1261. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  1262. if(pEventHeader && pThreadLocalData) {
  1263. PHEAP_EVENT_ALLOC pHeapEvent = (PHEAP_EVENT_ALLOC)( (SIZE_T)pEventHeader
  1264. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1265. pEventHeader->Packet.Size = (USHORT) ReqSize;
  1266. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_ALLOC;
  1267. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  1268. pHeapEvent->Size = Size;
  1269. pHeapEvent->Address = (PVOID)ReturnValue;
  1270. pHeapEvent->Source = MEMORY_FROM_LOOKASIDE;
  1271. ReleaseBufferLocation(pThreadLocalData);
  1272. }
  1273. }
  1274. #endif //NTOS_KERNEL_RUNTIME
  1275. return ReturnValue;
  1276. }
  1277. }
  1278. }
  1279. #endif // NTOS_KERNEL_RUNTIME
  1280. try {
  1281. HEAP_PERF_START_TIMER(Heap);
  1282. //
  1283. // Check if we need to serialize our access to the heap
  1284. //
  1285. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1286. //
  1287. // Lock the free list.
  1288. //
  1289. RtlAcquireLockRoutine( Heap->LockVariable );
  1290. LockAcquired = TRUE;
  1291. }
  1292. //
  1293. // If the allocation index is less than the maximum free list size
  1294. // then we can use the index to check the free list otherwise we have
  1295. // to either pull the entry off of the [0] index list or allocate
  1296. // memory directly for this request.
  1297. //
  1298. if (AllocationIndex < HEAP_MAXIMUM_FREELISTS) {
  1299. //
  1300. // With a size that matches a free list size grab the head
  1301. // of the list and check if there is an available entry
  1302. //
  1303. FreeListHead = &Heap->FreeLists[ AllocationIndex ];
  1304. if ( !IsListEmpty( FreeListHead )) {
  1305. //
  1306. // We're in luck the list has an entry so now get the free
  1307. // entry, copy its flags, remove it from the free list
  1308. //
  1309. FreeBlock = CONTAINING_RECORD( FreeListHead->Blink,
  1310. HEAP_FREE_ENTRY,
  1311. FreeList );
  1312. FreeFlags = FreeBlock->Flags;
  1313. RtlpFastRemoveDedicatedFreeBlock( Heap, FreeBlock );
  1314. //
  1315. // Adjust the total number of bytes free in the heap
  1316. //
  1317. Heap->TotalFreeSize -= AllocationIndex;
  1318. //
  1319. // Mark the block as busy and set the number of bytes
  1320. // unused and tag index. Also if it is the last entry
  1321. // then keep that flag.
  1322. //
  1323. BusyBlock = (PHEAP_ENTRY)FreeBlock;
  1324. BusyBlock->Flags = HEAP_ENTRY_BUSY | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
  1325. RtlpSetUnusedBytes(Heap, BusyBlock, AllocationSize - Size);
  1326. BusyBlock->SmallTagIndex = 0;
  1327. } else {
  1328. //
  1329. // The free list that matches our request is empty
  1330. //
  1331. // Scan the free list in use vector to find the smallest
  1332. // available free block large enough for our allocations.
  1333. //
  1334. //
  1335. // Compute the index of the ULONG where the scan should begin
  1336. //
  1337. InUseIndex = (ULONG) (AllocationIndex >> 5);
  1338. FreeListsInUse = &Heap->u.FreeListsInUseUlong[InUseIndex];
  1339. //
  1340. // Mask off the bits in the first ULONG that represent allocations
  1341. // smaller than we need.
  1342. //
  1343. FreeListsInUseUlong = *FreeListsInUse++ & ~((1 << ((ULONG) AllocationIndex & 0x1f)) - 1);
  1344. //
  1345. // Begin unrolled loop to scan bit vector.
  1346. //
  1347. switch (InUseIndex) {
  1348. case 0:
  1349. if (FreeListsInUseUlong) {
  1350. FreeListHead = &Heap->FreeLists[0];
  1351. break;
  1352. }
  1353. FreeListsInUseUlong = *FreeListsInUse++;
  1354. //
  1355. // deliberate fallthrough to next ULONG
  1356. //
  1357. case 1:
  1358. if (FreeListsInUseUlong) {
  1359. FreeListHead = &Heap->FreeLists[32];
  1360. break;
  1361. }
  1362. FreeListsInUseUlong = *FreeListsInUse++;
  1363. //
  1364. // deliberate fallthrough to next ULONG
  1365. //
  1366. case 2:
  1367. if (FreeListsInUseUlong) {
  1368. FreeListHead = &Heap->FreeLists[64];
  1369. break;
  1370. }
  1371. FreeListsInUseUlong = *FreeListsInUse++;
  1372. //
  1373. // deliberate fallthrough to next ULONG
  1374. //
  1375. case 3:
  1376. if (FreeListsInUseUlong) {
  1377. FreeListHead = &Heap->FreeLists[96];
  1378. break;
  1379. }
  1380. //
  1381. // deliberate fallthrough to non dedicated list
  1382. //
  1383. default:
  1384. //
  1385. // No suitable entry on the free list was found.
  1386. //
  1387. goto LookInNonDedicatedList;
  1388. }
  1389. //
  1390. // A free list has been found with a large enough allocation.
  1391. // FreeListHead contains the base of the vector it was found in.
  1392. // FreeListsInUseUlong contains the vector.
  1393. //
  1394. FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1395. //
  1396. // Grab the free block and remove it from the free list
  1397. //
  1398. FreeBlock = CONTAINING_RECORD( FreeListHead->Blink,
  1399. HEAP_FREE_ENTRY,
  1400. FreeList );
  1401. RtlpFastRemoveDedicatedFreeBlock( Heap, FreeBlock );
  1402. SplitFreeBlock:
  1403. //
  1404. // Save the blocks flags and decrement the amount of
  1405. // free space left in the heap
  1406. //
  1407. FreeFlags = FreeBlock->Flags;
  1408. Heap->TotalFreeSize -= FreeBlock->Size;
  1409. //
  1410. // Mark the block busy
  1411. //
  1412. BusyBlock = (PHEAP_ENTRY)FreeBlock;
  1413. BusyBlock->Flags = HEAP_ENTRY_BUSY;
  1414. //
  1415. // Compute the size (i.e., index) of the amount from this block
  1416. // that we don't need and can return to the free list
  1417. //
  1418. FreeSize = BusyBlock->Size - AllocationIndex;
  1419. //
  1420. // Finish setting up the rest of the new busy block
  1421. //
  1422. BusyBlock->Size = (USHORT)AllocationIndex;
  1423. RtlpSetUnusedBytes(Heap, BusyBlock, (AllocationSize - Size));
  1424. BusyBlock->SmallTagIndex = 0;
  1425. //
  1426. // Now if the size that we are going to free up is not zero
  1427. // then lets get to work and to the split.
  1428. //
  1429. if (FreeSize != 0) {
  1430. //
  1431. // But first we won't ever bother doing a split that only
  1432. // gives us 8 bytes back. So if free size is one then just
  1433. // bump up the size of the new busy block
  1434. //
  1435. if (FreeSize == 1) {
  1436. BusyBlock->Size += 1;
  1437. RtlpSetUnusedBytes(Heap, BusyBlock, AllocationSize + sizeof( HEAP_ENTRY ) - Size );
  1438. } else {
  1439. //
  1440. // Get a pointer to where the new free block will be.
  1441. // When we split a block the first part goes to the new
  1442. // busy block and the second part goes back to the free
  1443. // list
  1444. //
  1445. SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
  1446. //
  1447. // Reset the flags that we copied from the original free list
  1448. // header, and set it other size fields.
  1449. //
  1450. SplitBlock->Flags = FreeFlags;
  1451. SplitBlock->PreviousSize = (USHORT)AllocationIndex;
  1452. SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
  1453. SplitBlock->Size = (USHORT)FreeSize;
  1454. //
  1455. // If nothing else follows this entry then we will insert
  1456. // this into the corresponding free list (and update
  1457. // Segment->LastEntryInSegment)
  1458. //
  1459. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  1460. RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize);
  1461. Heap->TotalFreeSize += FreeSize;
  1462. } else {
  1463. //
  1464. // Otherwise we need to check the following block
  1465. // and if it is busy then update its previous size
  1466. // before inserting our new free block into the
  1467. // free list
  1468. //
  1469. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  1470. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
  1471. SplitBlock2->PreviousSize = (USHORT)FreeSize;
  1472. RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  1473. Heap->TotalFreeSize += FreeSize;
  1474. } else {
  1475. //
  1476. // The following block is free so we'll merge
  1477. // these to blocks. by first merging the flags
  1478. //
  1479. SplitBlock->Flags = SplitBlock2->Flags;
  1480. //
  1481. // Removing the second block from its free list
  1482. //
  1483. RtlpFastRemoveFreeBlock( Heap, SplitBlock2 );
  1484. //
  1485. // Updating the free total number of free bytes
  1486. // in the heap and updating the size of the new
  1487. // free block
  1488. //
  1489. Heap->TotalFreeSize -= SplitBlock2->Size;
  1490. FreeSize += SplitBlock2->Size;
  1491. //
  1492. // If the new free block is still less than the
  1493. // maximum heap block size then we'll simply
  1494. // insert it back in the free list
  1495. //
  1496. if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
  1497. SplitBlock->Size = (USHORT)FreeSize;
  1498. //
  1499. // Again check if the new following block
  1500. // exists and if so then update is previous
  1501. // size
  1502. //
  1503. if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  1504. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  1505. }
  1506. //
  1507. // Insert the new free block into the free
  1508. // list and update the free heap size
  1509. //
  1510. RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  1511. Heap->TotalFreeSize += FreeSize;
  1512. } else {
  1513. //
  1514. // The new free block is pretty large so we
  1515. // need to call a private routine to do the
  1516. // insert
  1517. //
  1518. RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
  1519. }
  1520. }
  1521. }
  1522. //
  1523. // Now that free flags made it back into a free block
  1524. // we can zero out what we saved.
  1525. //
  1526. FreeFlags = 0;
  1527. //
  1528. // If splitblock now last, update LastEntryInSegment
  1529. //
  1530. if (SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  1531. PHEAP_SEGMENT Segment;
  1532. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  1533. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  1534. }
  1535. }
  1536. }
  1537. //
  1538. // If there are no following entries then mark the new block as
  1539. // such
  1540. //
  1541. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  1542. BusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
  1543. }
  1544. }
  1545. //
  1546. // Return the address of the user portion of the allocated block.
  1547. // This is the byte following the header.
  1548. //
  1549. ReturnValue = BusyBlock + 1;
  1550. BlockSize = BusyBlock->Size << HEAP_GRANULARITY_SHIFT;
  1551. //
  1552. // Release the lock before the zero memory call
  1553. //
  1554. if (LockAcquired) {
  1555. RtlReleaseLockRoutine( Heap->LockVariable );
  1556. LockAcquired = FALSE;
  1557. }
  1558. //
  1559. // If the flags indicate that we should zero memory then do it now
  1560. //
  1561. if (Flags & HEAP_ZERO_MEMORY) {
  1562. RtlZeroMemory( ReturnValue, Size );
  1563. }
  1564. //
  1565. // And return the allocated block to our caller
  1566. //
  1567. leave;
  1568. //
  1569. // Otherwise the allocation request is bigger than the last dedicated
  1570. // free list size. Now check if the size is within our threshold.
  1571. // Meaning that it could be in the [0] free list
  1572. //
  1573. } else if (AllocationIndex <= Heap->VirtualMemoryThreshold) {
  1574. LookInNonDedicatedList:
  1575. //
  1576. // The following code cycles through the [0] free list until
  1577. // it finds a block that satisfies the request. The list
  1578. // is sorted so the search is can be terminated early on success
  1579. //
  1580. FreeListHead = &Heap->FreeLists[0];
  1581. if (Heap->LargeBlocksIndex) {
  1582. //
  1583. // We can use the index to find the block very quick
  1584. //
  1585. Next = RtlpFindEntry( Heap, (ULONG)AllocationIndex );
  1586. if ( FreeListHead != Next ) {
  1587. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  1588. if ( FreeBlock->Size >= AllocationIndex ) {
  1589. //
  1590. // We've found something that we can use so now remove
  1591. // it from the free list and go to where we treat splitting
  1592. // a free block. Note that the block we found here might
  1593. // actually be the exact size we need and that is why
  1594. // in the split free block case we have to consider having
  1595. // nothing free after the split
  1596. //
  1597. #ifndef NTOS_KERNEL_RUNTIME
  1598. if ((((PHEAP_INDEX)Heap->LargeBlocksIndex)->LargeBlocksCacheSequence)
  1599. &&
  1600. (AllocationIndex > Heap->DeCommitFreeBlockThreshold)
  1601. &&
  1602. (FreeBlock->Size > (AllocationIndex * HEAP_REUSAGE_FACTOR))) {
  1603. RtlpFlushLargestCacheBlock(Heap);
  1604. } else {
  1605. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  1606. goto SplitFreeBlock;
  1607. }
  1608. #else // NTOS_KERNEL_RUNTIME
  1609. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  1610. goto SplitFreeBlock;
  1611. #endif // NTOS_KERNEL_RUNTIME
  1612. }
  1613. }
  1614. } else {
  1615. //
  1616. // Check if the largest block in the list is smaller than the request
  1617. //
  1618. Next = FreeListHead->Blink;
  1619. if (FreeListHead != Next) {
  1620. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  1621. if (FreeBlock->Size >= AllocationIndex) {
  1622. //
  1623. // Here we are sure there is at least a block here larger than
  1624. // the requested size. Start searching from the first block
  1625. //
  1626. Next = FreeListHead->Flink;
  1627. while (FreeListHead != Next) {
  1628. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  1629. if (FreeBlock->Size >= AllocationIndex) {
  1630. //
  1631. // We've found something that we can use so now remove
  1632. // it from the free list and go to where we treat splitting
  1633. // a free block. Note that the block we found here might
  1634. // actually be the exact size we need and that is why
  1635. // in the split free block case we have to consider having
  1636. // nothing free after the split
  1637. //
  1638. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  1639. goto SplitFreeBlock;
  1640. }
  1641. Next = Next->Flink;
  1642. }
  1643. }
  1644. }
  1645. }
  1646. //
  1647. // The [0] list is either empty or everything is too small
  1648. // so now extend the heap which should get us something less
  1649. // than or equal to the virtual memory threshold
  1650. //
  1651. FreeBlock = RtlpExtendHeap( Heap, AllocationSize );
  1652. //
  1653. // And provided we got something we'll treat it just like the previous
  1654. // split free block cases
  1655. //
  1656. if (FreeBlock != NULL) {
  1657. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  1658. goto SplitFreeBlock;
  1659. }
  1660. //
  1661. // We weren't able to extend the heap so we must be out of memory
  1662. //
  1663. Status = STATUS_NO_MEMORY;
  1664. //
  1665. // At this point the allocation is way too big for any of the free lists
  1666. // and we can only satisfy this request if the heap is growable
  1667. //
  1668. } else if (Heap->Flags & HEAP_GROWABLE) {
  1669. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  1670. VirtualAllocBlock = NULL;
  1671. //
  1672. // Compute how much memory we will need for this allocation which
  1673. // will include the allocation size plus a header, and then go
  1674. // get the committed memory
  1675. //
  1676. AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  1677. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  1678. (PVOID *)&VirtualAllocBlock,
  1679. 0,
  1680. &AllocationSize,
  1681. MEM_COMMIT,
  1682. PAGE_READWRITE );
  1683. if (NT_SUCCESS(Status)) {
  1684. //
  1685. // Just committed, already zero. Fill in the new block
  1686. // and insert it in the list of big allocation
  1687. //
  1688. VirtualAllocBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
  1689. VirtualAllocBlock->BusyBlock.Flags = HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT | HEAP_ENTRY_BUSY;
  1690. VirtualAllocBlock->CommitSize = AllocationSize;
  1691. VirtualAllocBlock->ReserveSize = AllocationSize;
  1692. InsertTailList( &Heap->VirtualAllocdBlocks, (PLIST_ENTRY)VirtualAllocBlock );
  1693. //
  1694. // Return the address of the user portion of the allocated block.
  1695. // This is the byte following the header.
  1696. //
  1697. ReturnValue = (PHEAP_ENTRY)(VirtualAllocBlock + 1);
  1698. BlockSize = AllocationSize;
  1699. leave;
  1700. }
  1701. } else {
  1702. Status = STATUS_BUFFER_TOO_SMALL;
  1703. }
  1704. //
  1705. // This is the error return.
  1706. //
  1707. if (Flags & HEAP_GENERATE_EXCEPTIONS) {
  1708. //
  1709. // Construct an exception record.
  1710. //
  1711. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  1712. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  1713. ExceptionRecord.NumberParameters = 1;
  1714. ExceptionRecord.ExceptionFlags = 0;
  1715. ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
  1716. RtlRaiseException( &ExceptionRecord );
  1717. }
  1718. SET_LAST_STATUS(Status);
  1719. ReturnValue = NULL;
  1720. } finally {
  1721. if (LockAcquired) {
  1722. RtlReleaseLockRoutine( Heap->LockVariable );
  1723. }
  1724. }
  1725. RtlpRegisterOperation(Heap, BlockSize, HEAP_OP_ALLOC);
  1726. HEAP_PERF_STOP_TIMER(Heap, HEAP_OP_ALLOC);
  1727. #ifndef NTOS_KERNEL_RUNTIME
  1728. if( IsHeapLogging( HeapHandle ) ) {
  1729. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1730. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1731. USHORT ReqSize = sizeof(HEAP_EVENT_ALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1732. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  1733. if(pEventHeader && pThreadLocalData) {
  1734. PHEAP_EVENT_ALLOC pHeapEvent = (PHEAP_EVENT_ALLOC)( (SIZE_T)pEventHeader
  1735. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1736. pEventHeader->Packet.Size = (USHORT) ReqSize;
  1737. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_ALLOC;
  1738. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  1739. pHeapEvent->Size = Size;
  1740. pHeapEvent->Address = (PVOID)ReturnValue;
  1741. pHeapEvent->Source = MEMORY_FROM_MAINPATH;
  1742. ReleaseBufferLocation(pThreadLocalData);
  1743. }
  1744. }
  1745. #endif //NTOS_KERNEL_RUNTIME
  1746. return ReturnValue;
  1747. }
  1748. PVOID
  1749. RtlAllocateHeapSlowly (
  1750. IN PVOID HeapHandle,
  1751. IN ULONG Flags,
  1752. IN SIZE_T Size
  1753. )
  1754. /*++
  1755. Routine Description:
  1756. This routine does the equivalent of Rtl Allocate Heap but it does it will
  1757. additional heap consistency checking logic and tagging.
  1758. Arguments:
  1759. HeapHandle - Supplies a pointer to an initialized heap structure
  1760. Flags - Specifies the set of flags to use to control the allocation
  1761. Size - Specifies the size, in bytes, of the allocation
  1762. Return Value:
  1763. PVOID - returns a pointer to the newly allocated block
  1764. --*/
  1765. {
  1766. PHEAP Heap = (PHEAP)HeapHandle;
  1767. BOOLEAN LockAcquired = FALSE;
  1768. PVOID ReturnValue = NULL;
  1769. PULONG FreeListsInUse;
  1770. ULONG FreeListsInUseUlong;
  1771. SIZE_T AllocationSize;
  1772. SIZE_T FreeSize, AllocationIndex;
  1773. UCHAR EntryFlags, FreeFlags;
  1774. PLIST_ENTRY FreeListHead, Next;
  1775. PHEAP_ENTRY BusyBlock;
  1776. PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
  1777. PHEAP_ENTRY_EXTRA ExtraStuff;
  1778. NTSTATUS Status;
  1779. EXCEPTION_RECORD ExceptionRecord;
  1780. SIZE_T ZeroSize = 0;
  1781. SIZE_T BlockSize = 0;
  1782. HEAP_PERF_DECLARE_TIMER();
  1783. RTL_PAGED_CODE();
  1784. //
  1785. // Note that Flags has already been OR'd with Heap->ForceFlags.
  1786. //
  1787. #ifndef NTOS_KERNEL_RUNTIME
  1788. //
  1789. // In the non kernel case check if we should be using the debug version
  1790. // of heap allocation
  1791. //
  1792. if (DEBUG_HEAP( Flags )) {
  1793. return RtlDebugAllocateHeap( HeapHandle, Flags, Size );
  1794. }
  1795. #endif // NTOS_KERNEL_RUNTIME
  1796. //
  1797. // If the size is greater than maxlong then say we can't allocate that
  1798. // much and return the error to our caller
  1799. //
  1800. if (Size > MAXINT_PTR) {
  1801. SET_LAST_STATUS( STATUS_NO_MEMORY );
  1802. return NULL;
  1803. }
  1804. //
  1805. // Round up the requested size to the allocation granularity. Note
  1806. // that if the request is for zero bytes we will still allocate memory,
  1807. //
  1808. // Allocation size will be either 16, 24, 32, ...
  1809. // Allocation index will be 2, 3, 4, ...
  1810. //
  1811. AllocationSize = ((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask;
  1812. //
  1813. // Generate the flags needed for this heap entry. Mark it busy and add
  1814. // any user settable bits. Also if the input flag indicates any entry
  1815. // extra fields and we have a tag to use then make room for the extra
  1816. // fields in the heap entry
  1817. //
  1818. EntryFlags = (UCHAR)(HEAP_ENTRY_BUSY | ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4));
  1819. if ((Flags & HEAP_NEED_EXTRA_FLAGS) || (Heap->PseudoTagEntries != NULL)) {
  1820. EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
  1821. AllocationSize += sizeof( HEAP_ENTRY_EXTRA );
  1822. }
  1823. AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
  1824. try {
  1825. HEAP_PERF_START_TIMER(Heap);
  1826. //
  1827. // Lock the free list.
  1828. //
  1829. if (!(Flags & HEAP_NO_SERIALIZE)) {
  1830. RtlAcquireLockRoutine( Heap->LockVariable );
  1831. LockAcquired = TRUE;
  1832. }
  1833. //
  1834. // Do all the actual heap work under the protection of a try-except clause
  1835. // to protect us from corruption
  1836. //
  1837. try {
  1838. //
  1839. // If the allocation index is less than the maximum free list size
  1840. // then we can use the index to check the free list otherwise we have
  1841. // to either pull the entry off of the [0] index list or allocate
  1842. // memory directly for this request.
  1843. //
  1844. if (AllocationIndex < HEAP_MAXIMUM_FREELISTS) {
  1845. //
  1846. // With a size that matches a free list size grab the head
  1847. // of the list and check if there is an available entry
  1848. //
  1849. FreeListHead = &Heap->FreeLists[ AllocationIndex ];
  1850. if ( !IsListEmpty( FreeListHead )) {
  1851. //
  1852. // We're in luck the list has an entry so now get the free
  1853. // entry, copy its flags, remove it from the free list
  1854. //
  1855. FreeBlock = CONTAINING_RECORD( FreeListHead->Flink,
  1856. HEAP_FREE_ENTRY,
  1857. FreeList );
  1858. FreeFlags = FreeBlock->Flags;
  1859. RtlpRemoveFreeBlock( Heap, FreeBlock );
  1860. //
  1861. // Adjust the total number of bytes free in the heap
  1862. //
  1863. Heap->TotalFreeSize -= AllocationIndex;
  1864. //
  1865. // Mark the block as busy and set the number of bytes
  1866. // unused and tag index. Also if it is the last entry
  1867. // then keep that flag.
  1868. //
  1869. BusyBlock = (PHEAP_ENTRY)FreeBlock;
  1870. BusyBlock->Flags = EntryFlags | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
  1871. RtlpSetUnusedBytes(Heap, BusyBlock, (AllocationSize - Size));
  1872. } else {
  1873. //
  1874. // The free list that matches our request is empty. We know
  1875. // that there are 128 free lists managed by a 4 ULONG bitmap.
  1876. // The next big if-else-if statement will decide which ULONG
  1877. // we tackle
  1878. //
  1879. // Check if the requested allocation index within the first
  1880. // quarter of the free lists.
  1881. //
  1882. if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 1) / 4) {
  1883. //
  1884. // Grab a pointer to the corresponding bitmap ULONG, and
  1885. // then get the bit we're actually interested in to be the
  1886. // first bit of the ULONG.
  1887. //
  1888. FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 0 ];
  1889. FreeListsInUseUlong = *FreeListsInUse++ >> ((ULONG) AllocationIndex & 0x1F);
  1890. //
  1891. // If the remaining bitmap has any bits set then we know
  1892. // there is a non empty list that is larger than our
  1893. // requested index so find that bit and compute the list
  1894. // head of the next non empty list
  1895. //
  1896. if (FreeListsInUseUlong) {
  1897. FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1898. } else {
  1899. //
  1900. // The rest of the first ULONG is all zeros so we need
  1901. // to move to the second ULONG
  1902. //
  1903. FreeListsInUseUlong = *FreeListsInUse++;
  1904. //
  1905. // Check if the second ULONG has any bits set and if
  1906. // so then compute the list head of the next non empty
  1907. // list
  1908. //
  1909. if (FreeListsInUseUlong) {
  1910. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
  1911. (AllocationIndex & 0x1F) +
  1912. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1913. } else {
  1914. //
  1915. // Do the same test for the third ULONG
  1916. //
  1917. FreeListsInUseUlong = *FreeListsInUse++;
  1918. if (FreeListsInUseUlong) {
  1919. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 2) / 4) -
  1920. (AllocationIndex & 0x1F) +
  1921. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1922. } else {
  1923. //
  1924. // Repeat the test for the forth ULONG, and if
  1925. // that one is also empty then we need to grab
  1926. // the allocation off of the [0] index list
  1927. //
  1928. FreeListsInUseUlong = *FreeListsInUse++;
  1929. if (FreeListsInUseUlong) {
  1930. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 3) / 4) -
  1931. (AllocationIndex & 0x1F) +
  1932. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1933. } else {
  1934. goto LookInNonDedicatedList;
  1935. }
  1936. }
  1937. }
  1938. }
  1939. //
  1940. // Otherwise check if the requested allocation index lies
  1941. // within the second quarter of the free lists. We repeat the
  1942. // test just like we did above on the second, third, and forth
  1943. // bitmap ulongs.
  1944. //
  1945. } else if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 2) / 4) {
  1946. FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 1 ];
  1947. FreeListsInUseUlong = *FreeListsInUse++ >> ((ULONG) AllocationIndex & 0x1F);
  1948. if (FreeListsInUseUlong) {
  1949. FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1950. } else {
  1951. FreeListsInUseUlong = *FreeListsInUse++;
  1952. if (FreeListsInUseUlong) {
  1953. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
  1954. (AllocationIndex & 0x1F) +
  1955. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1956. } else {
  1957. FreeListsInUseUlong = *FreeListsInUse++;
  1958. if (FreeListsInUseUlong) {
  1959. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 2) / 4) -
  1960. (AllocationIndex & 0x1F) +
  1961. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1962. } else {
  1963. goto LookInNonDedicatedList;
  1964. }
  1965. }
  1966. }
  1967. //
  1968. // Otherwise check if the requested allocation index lies
  1969. // within the third quarter of the free lists. We repeat the
  1970. // test just like we did above on the third and forth bitmap
  1971. // ulongs
  1972. //
  1973. } else if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 3) / 4) {
  1974. FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 2 ];
  1975. FreeListsInUseUlong = *FreeListsInUse++ >> ((ULONG) AllocationIndex & 0x1F);
  1976. if (FreeListsInUseUlong) {
  1977. FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1978. } else {
  1979. FreeListsInUseUlong = *FreeListsInUse++;
  1980. if (FreeListsInUseUlong) {
  1981. FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
  1982. (AllocationIndex & 0x1F) +
  1983. RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1984. } else {
  1985. goto LookInNonDedicatedList;
  1986. }
  1987. }
  1988. //
  1989. // Lastly the requested allocation index must lie within the
  1990. // last quarter of the free lists. We repeat the test just
  1991. // like we did above on the forth ulong
  1992. //
  1993. } else {
  1994. FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 3 ];
  1995. FreeListsInUseUlong = *FreeListsInUse++ >> ((ULONG) AllocationIndex & 0x1F);
  1996. if (FreeListsInUseUlong) {
  1997. FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
  1998. } else {
  1999. goto LookInNonDedicatedList;
  2000. }
  2001. }
  2002. //
  2003. // At this point the free list head points to a non empty free
  2004. // list that is of greater size than we need.
  2005. //
  2006. FreeBlock = CONTAINING_RECORD( FreeListHead->Flink,
  2007. HEAP_FREE_ENTRY,
  2008. FreeList );
  2009. SplitFreeBlock:
  2010. //
  2011. // Remember the flags that go with this block and remove it
  2012. // from its list
  2013. //
  2014. FreeFlags = FreeBlock->Flags;
  2015. RtlpRemoveFreeBlock( Heap, FreeBlock );
  2016. //
  2017. // Adjust the amount free in the heap
  2018. //
  2019. Heap->TotalFreeSize -= FreeBlock->Size;
  2020. //
  2021. // Mark the block busy
  2022. //
  2023. BusyBlock = (PHEAP_ENTRY)FreeBlock;
  2024. BusyBlock->Flags = EntryFlags;
  2025. //
  2026. // Compute the size (i.e., index) of the amount from this
  2027. // block that we don't need and can return to the free list
  2028. //
  2029. FreeSize = BusyBlock->Size - AllocationIndex;
  2030. //
  2031. // Finish setting up the rest of the new busy block
  2032. //
  2033. BusyBlock->Size = (USHORT)AllocationIndex;
  2034. RtlpSetUnusedBytes(Heap, BusyBlock, ((AllocationSize - Size)));
  2035. //
  2036. // Now if the size that we are going to free up is not zero
  2037. // then lets get to work and to the split.
  2038. //
  2039. if (FreeSize != 0) {
  2040. //
  2041. // But first we won't ever bother doing a split that only
  2042. // gives us 8 bytes back. So if free size is one then
  2043. // just bump up the size of the new busy block
  2044. //
  2045. if (FreeSize == 1) {
  2046. BusyBlock->Size += 1;
  2047. RtlpSetUnusedBytes(Heap, BusyBlock, AllocationSize + sizeof( HEAP_ENTRY ) - Size);
  2048. } else {
  2049. //
  2050. // Get a pointer to where the new free block will be.
  2051. // When we split a block the first part goes to the
  2052. // new busy block and the second part goes back to the
  2053. // free list
  2054. //
  2055. SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
  2056. //
  2057. // Reset the flags that we copied from the original
  2058. // free list header, and set it other size fields.
  2059. //
  2060. SplitBlock->Flags = FreeFlags;
  2061. SplitBlock->PreviousSize = (USHORT)AllocationIndex;
  2062. SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
  2063. SplitBlock->Size = (USHORT)FreeSize;
  2064. //
  2065. // If nothing else follows this entry then we will
  2066. // insert this into the corresponding free list
  2067. //
  2068. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  2069. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  2070. Heap->TotalFreeSize += FreeSize;
  2071. } else {
  2072. //
  2073. // Otherwise we need to check the following block
  2074. // and if it is busy then update its previous size
  2075. // before inserting our new free block into the
  2076. // free list
  2077. //
  2078. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  2079. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
  2080. SplitBlock2->PreviousSize = (USHORT)FreeSize;
  2081. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  2082. Heap->TotalFreeSize += FreeSize;
  2083. } else {
  2084. //
  2085. // The following block is free so we'll merge
  2086. // these to blocks. by first merging the flags
  2087. //
  2088. SplitBlock->Flags = SplitBlock2->Flags;
  2089. //
  2090. // Removing the second block from its free
  2091. // list
  2092. //
  2093. RtlpRemoveFreeBlock( Heap, SplitBlock2 );
  2094. //
  2095. // Updating the free total number of free
  2096. // bytes in the heap and updating the size of
  2097. // the new free block
  2098. //
  2099. Heap->TotalFreeSize -= SplitBlock2->Size;
  2100. FreeSize += SplitBlock2->Size;
  2101. //
  2102. // If the new free block is still less than
  2103. // the maximum heap block size then we'll
  2104. // simply insert it back in the free list
  2105. //
  2106. if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
  2107. SplitBlock->Size = (USHORT)FreeSize;
  2108. //
  2109. // Again check if the new following block
  2110. // exists and if so then update is
  2111. // previous size
  2112. //
  2113. if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  2114. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  2115. }
  2116. //
  2117. // Insert the new free block into the free
  2118. // list and update the free heap size
  2119. //
  2120. RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
  2121. Heap->TotalFreeSize += FreeSize;
  2122. } else {
  2123. //
  2124. // The new free block is pretty large so
  2125. // we need to call a private routine to do
  2126. // the insert
  2127. //
  2128. RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
  2129. }
  2130. }
  2131. }
  2132. //
  2133. // Now that free flags made it back into a free block
  2134. // we can zero out what we saved.
  2135. //
  2136. FreeFlags = 0;
  2137. //
  2138. // If splitblock now last, update LastEntryInSegment
  2139. //
  2140. if (SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  2141. PHEAP_SEGMENT Segment;
  2142. Segment = Heap->Segments[SplitBlock->SegmentIndex];
  2143. Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
  2144. }
  2145. }
  2146. }
  2147. //
  2148. // If there are no following entries then mark the new block
  2149. // as such
  2150. //
  2151. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
  2152. BusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
  2153. }
  2154. }
  2155. //
  2156. // Return the address of the user portion of the allocated block.
  2157. // This is the byte following the header.
  2158. //
  2159. ReturnValue = BusyBlock + 1;
  2160. BlockSize = BusyBlock->Size << HEAP_GRANULARITY_SHIFT;
  2161. //
  2162. // If the flags indicate that we should zero memory then
  2163. // remember how much to zero. We'll do the zeroing later
  2164. //
  2165. if (Flags & HEAP_ZERO_MEMORY) {
  2166. ZeroSize = Size;
  2167. //
  2168. // Otherwise if the flags indicate that we should fill heap then
  2169. // it it now.
  2170. //
  2171. } else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
  2172. RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1), Size & ~0x3, ALLOC_HEAP_FILL );
  2173. }
  2174. //
  2175. // If the flags indicate that we should do tail checking then copy
  2176. // the fill pattern right after the heap block.
  2177. //
  2178. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
  2179. RtlFillMemory( (PCHAR)ReturnValue + Size,
  2180. CHECK_HEAP_TAIL_SIZE,
  2181. CHECK_HEAP_TAIL_FILL );
  2182. BusyBlock->Flags |= HEAP_ENTRY_FILL_PATTERN;
  2183. }
  2184. BusyBlock->SmallTagIndex = 0;
  2185. //
  2186. // If the flags indicate that there is an extra block persent then
  2187. // we'll fill it in
  2188. //
  2189. if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  2190. ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
  2191. RtlZeroMemory( ExtraStuff, sizeof( *ExtraStuff ));
  2192. #ifndef NTOS_KERNEL_RUNTIME
  2193. //
  2194. // In the non kernel case the tagging goes in either the extra
  2195. // stuff of the busy block small tag index
  2196. //
  2197. if (IS_HEAP_TAGGING_ENABLED()) {
  2198. ExtraStuff->TagIndex = RtlpUpdateTagEntry( Heap,
  2199. (USHORT)((Flags & HEAP_TAG_MASK) >> HEAP_TAG_SHIFT),
  2200. 0,
  2201. BusyBlock->Size,
  2202. AllocationAction );
  2203. }
  2204. } else if (IS_HEAP_TAGGING_ENABLED()) {
  2205. BusyBlock->SmallTagIndex = (UCHAR)RtlpUpdateTagEntry( Heap,
  2206. (USHORT)((Flags & HEAP_SMALL_TAG_MASK) >> HEAP_TAG_SHIFT),
  2207. 0,
  2208. BusyBlock->Size,
  2209. AllocationAction );
  2210. #endif // NTOS_KERNEL_RUNTIME
  2211. }
  2212. //
  2213. // Return the address of the user portion of the allocated block.
  2214. // This is the byte following the header.
  2215. //
  2216. leave;
  2217. //
  2218. // Otherwise the allocation request is bigger than the last dedicated
  2219. // free list size. Now check if the size is within our threshold.
  2220. // Meaning that it could be in the [0] free list
  2221. //
  2222. } else if (AllocationIndex <= Heap->VirtualMemoryThreshold) {
  2223. LookInNonDedicatedList:
  2224. //
  2225. // The following code cycles through the [0] free list until
  2226. // it finds a block that satisfies the request. The list
  2227. // is sorted so the search is can be terminated early on success
  2228. //
  2229. FreeListHead = &Heap->FreeLists[ 0 ];
  2230. if (Heap->LargeBlocksIndex) {
  2231. Next = RtlpFindEntry(Heap, (ULONG)AllocationIndex);
  2232. if (FreeListHead != Next) {
  2233. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  2234. if (FreeBlock->Size >= AllocationIndex) {
  2235. //
  2236. // We've found something that we can use so now remove
  2237. // it from the free list and go to where we treat splitting
  2238. // a free block. Note that the block we found here might
  2239. // actually be the exact size we need and that is why
  2240. // in the split free block case we have to consider having
  2241. // nothing free after the split
  2242. //
  2243. #ifndef NTOS_KERNEL_RUNTIME
  2244. if ((((PHEAP_INDEX)Heap->LargeBlocksIndex)->LargeBlocksCacheSequence)
  2245. &&
  2246. (AllocationIndex > Heap->DeCommitFreeBlockThreshold)
  2247. &&
  2248. (FreeBlock->Size > (AllocationIndex * HEAP_REUSAGE_FACTOR))) {
  2249. RtlpFlushLargestCacheBlock(Heap);
  2250. } else {
  2251. goto SplitFreeBlock;
  2252. }
  2253. #else // NTOS_KERNEL_RUNTIME
  2254. goto SplitFreeBlock;
  2255. #endif // NTOS_KERNEL_RUNTIME
  2256. }
  2257. }
  2258. } else {
  2259. Next = FreeListHead->Flink;
  2260. while (FreeListHead != Next) {
  2261. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  2262. if (FreeBlock->Size >= AllocationIndex) {
  2263. //
  2264. // We've found something that we can use so now go to
  2265. // where we treat splitting a free block. Note that
  2266. // the block we found here might actually be the exact
  2267. // size we need and that is why in the split free block
  2268. // case we have to consider having nothing free after the
  2269. // split
  2270. //
  2271. goto SplitFreeBlock;
  2272. } else {
  2273. Next = Next->Flink;
  2274. }
  2275. }
  2276. }
  2277. //
  2278. // The [0] list is either empty or everything is too small
  2279. // so now extend the heap which should get us something less
  2280. // than or equal to the virtual memory threshold
  2281. //
  2282. FreeBlock = RtlpExtendHeap( Heap, AllocationSize );
  2283. //
  2284. // And provided we got something we'll treat it just like the
  2285. // previous split free block cases
  2286. //
  2287. if (FreeBlock != NULL) {
  2288. goto SplitFreeBlock;
  2289. }
  2290. //
  2291. // We weren't able to extend the heap so we must be out of memory
  2292. //
  2293. Status = STATUS_NO_MEMORY;
  2294. //
  2295. // At this point the allocation is way too big for any of the free
  2296. // lists and we can only satisfy this request if the heap is growable
  2297. //
  2298. } else if (Heap->Flags & HEAP_GROWABLE) {
  2299. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2300. VirtualAllocBlock = NULL;
  2301. //
  2302. // Compute how much memory we will need for this allocation which
  2303. // will include the allocation size plus a header, and then go
  2304. // get the committed memory
  2305. //
  2306. AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  2307. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  2308. (PVOID *)&VirtualAllocBlock,
  2309. 0,
  2310. &AllocationSize,
  2311. MEM_COMMIT,
  2312. PAGE_READWRITE );
  2313. if (NT_SUCCESS( Status )) {
  2314. //
  2315. // Just committed, already zero. Fill in the new block
  2316. // and insert it in the list of big allocation
  2317. //
  2318. VirtualAllocBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
  2319. VirtualAllocBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT;
  2320. VirtualAllocBlock->CommitSize = AllocationSize;
  2321. VirtualAllocBlock->ReserveSize = AllocationSize;
  2322. #ifndef NTOS_KERNEL_RUNTIME
  2323. //
  2324. // In the non kernel case see if we need to add heap tagging
  2325. //
  2326. if (IS_HEAP_TAGGING_ENABLED()) {
  2327. VirtualAllocBlock->ExtraStuff.TagIndex =
  2328. RtlpUpdateTagEntry( Heap,
  2329. (USHORT)((Flags & HEAP_SMALL_TAG_MASK) >> HEAP_TAG_SHIFT),
  2330. 0,
  2331. VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT,
  2332. VirtualAllocationAction );
  2333. }
  2334. #endif // NTOS_KERNEL_RUNTIME
  2335. InsertTailList( &Heap->VirtualAllocdBlocks, (PLIST_ENTRY)VirtualAllocBlock );
  2336. //
  2337. // Return the address of the user portion of the allocated
  2338. // block. This is the byte following the header.
  2339. //
  2340. ReturnValue = (PHEAP_ENTRY)(VirtualAllocBlock + 1);
  2341. BlockSize = AllocationSize;
  2342. leave;
  2343. }
  2344. //
  2345. // Otherwise we have an error condition
  2346. //
  2347. } else {
  2348. Status = STATUS_BUFFER_TOO_SMALL;
  2349. }
  2350. SET_LAST_STATUS( Status );
  2351. if (Flags & HEAP_GENERATE_EXCEPTIONS) {
  2352. //
  2353. // Construct an exception record.
  2354. //
  2355. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  2356. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  2357. ExceptionRecord.NumberParameters = 1;
  2358. ExceptionRecord.ExceptionFlags = 0;
  2359. ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
  2360. RtlRaiseException( &ExceptionRecord );
  2361. }
  2362. } except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
  2363. EXCEPTION_EXECUTE_HANDLER ) {
  2364. SET_LAST_STATUS( GetExceptionCode() );
  2365. }
  2366. //
  2367. // Check if there is anything to zero out
  2368. //
  2369. if ( ZeroSize ) {
  2370. RtlZeroMemory( ReturnValue, ZeroSize );
  2371. }
  2372. } finally {
  2373. if (LockAcquired) {
  2374. RtlReleaseLockRoutine( Heap->LockVariable );
  2375. }
  2376. }
  2377. //
  2378. // And return to our caller
  2379. //
  2380. RtlpRegisterOperation(Heap, BlockSize, HEAP_OP_ALLOC);
  2381. HEAP_PERF_STOP_TIMER(Heap, HEAP_OP_ALLOC);
  2382. if(ReturnValue) {
  2383. #ifndef NTOS_KERNEL_RUNTIME
  2384. if( IsHeapLogging( HeapHandle ) ) {
  2385. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2386. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2387. USHORT ReqSize = sizeof(HEAP_EVENT_ALLOC) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2388. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize);
  2389. if(pEventHeader && pThreadLocalData) {
  2390. PHEAP_EVENT_ALLOC pHeapEvent = (PHEAP_EVENT_ALLOC)( (SIZE_T)pEventHeader
  2391. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2392. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2393. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_ALLOC;
  2394. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  2395. pHeapEvent->Size = Size;
  2396. pHeapEvent->Address = (PVOID)ReturnValue;
  2397. pHeapEvent->Source = MEMORY_FROM_SLOWPATH;
  2398. ReleaseBufferLocation(pThreadLocalData);
  2399. }
  2400. }
  2401. #endif // NTOS_KERNEL_RUNTIME
  2402. }
  2403. return ReturnValue;
  2404. }
  2405. BOOLEAN
  2406. RtlFreeHeap (
  2407. IN PVOID HeapHandle,
  2408. IN ULONG Flags,
  2409. IN PVOID BaseAddress
  2410. )
  2411. /*++
  2412. Routine Description:
  2413. This routine returns a previously allocated block back to its heap
  2414. Arguments:
  2415. HeapHandle - Supplies a pointer to the owning heap structure
  2416. Flags - Specifies the set of flags to use in the deallocation
  2417. BaseAddress - Supplies a pointer to the block being freed
  2418. Return Value:
  2419. BOOLEAN - TRUE if the block was properly freed and FALSE otherwise
  2420. --*/
  2421. {
  2422. NTSTATUS Status;
  2423. PHEAP Heap = (PHEAP)HeapHandle;
  2424. PHEAP_ENTRY BusyBlock;
  2425. PHEAP_ENTRY_EXTRA ExtraStuff;
  2426. SIZE_T FreeSize;
  2427. BOOLEAN LockAcquired = FALSE;
  2428. BOOLEAN ReturnValue = TRUE;
  2429. SIZE_T BlockSize;
  2430. PVOID FrontHeap = NULL;
  2431. HEAP_PERF_DECLARE_TIMER();
  2432. RTL_PAGED_CODE();
  2433. //
  2434. // First check if the address we're given is null and if so then
  2435. // there is really nothing to do so just return success
  2436. //
  2437. if (BaseAddress == NULL) {
  2438. return TRUE;
  2439. }
  2440. #ifndef NTOS_KERNEL_RUNTIME
  2441. if (FrontHeap = RtlpGetLowFragHeap(Heap)) {
  2442. //
  2443. // We can do everything in this routine. So now backup to get
  2444. // a pointer to the start of the block
  2445. //
  2446. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  2447. if (BusyBlock->SegmentIndex >= HEAP_LFH_IN_CONVERSION) {
  2448. if (RtlpLowFragHeapFree( FrontHeap, BaseAddress)) {
  2449. #ifndef NTOS_KERNEL_RUNTIME
  2450. if( IsHeapLogging( HeapHandle ) ) {
  2451. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2452. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2453. USHORT ReqSize = sizeof(HEAP_EVENT_FREE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2454. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  2455. if(pEventHeader && pThreadLocalData) {
  2456. PHEAP_EVENT_FREE pHeapEvent = (PHEAP_EVENT_FREE)( (SIZE_T)pEventHeader
  2457. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2458. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2459. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_FREE;
  2460. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  2461. pHeapEvent->Address = (PVOID)BaseAddress;
  2462. pHeapEvent->Source = MEMORY_FROM_LOWFRAG;
  2463. ReleaseBufferLocation(pThreadLocalData);
  2464. }
  2465. }
  2466. #endif //NTOS_KERNEL_RUNTIME
  2467. return TRUE;
  2468. }
  2469. }
  2470. }
  2471. #ifdef NTHEAP_ENABLED
  2472. {
  2473. if (Heap->Flags & NTHEAP_ENABLED_FLAG) {
  2474. return RtlFreeNtHeap( HeapHandle,
  2475. Flags,
  2476. BaseAddress);
  2477. }
  2478. }
  2479. #endif // NTHEAP_ENABLED
  2480. #endif // NTOS_KERNEL_RUNTIME
  2481. //
  2482. // Compliment the input flags with those enforced by the heap
  2483. //
  2484. Flags |= Heap->ForceFlags;
  2485. //
  2486. // Now check if we should go the slow route
  2487. //
  2488. if (Flags & HEAP_SLOW_FLAGS) {
  2489. return RtlFreeHeapSlowly(HeapHandle, Flags, BaseAddress);
  2490. }
  2491. //
  2492. // We can do everything in this routine. So now backup to get
  2493. // a pointer to the start of the block
  2494. //
  2495. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  2496. //
  2497. // Protect ourselves from mistakes by refusing to free blocks
  2498. // that do not have the busy bit set.
  2499. //
  2500. // Also refuse to free blocks that are not eight-byte aligned.
  2501. // The specific mistake in this case is Office95, which likes
  2502. // to free a random pointer when you start Word95 from a desktop
  2503. // shortcut.
  2504. //
  2505. // As further insurance against mistakes, check the segment index
  2506. // to make sure it is less than HEAP_MAXIMUM_SEGMENTS (16). This
  2507. // should fix all the dorks who have ASCII or Unicode where the
  2508. // heap header is supposed to be.
  2509. //
  2510. try {
  2511. if ((((ULONG_PTR)BaseAddress & 0x7) != 0) ||
  2512. (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) ||
  2513. (BusyBlock->SegmentIndex >= HEAP_MAXIMUM_SEGMENTS)) {
  2514. //
  2515. // Not a busy block, or it's not aligned or the segment is
  2516. // to big, meaning it's corrupt
  2517. //
  2518. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  2519. return FALSE;
  2520. }
  2521. } except(EXCEPTION_EXECUTE_HANDLER) {
  2522. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  2523. return FALSE;
  2524. }
  2525. BlockSize = BusyBlock->Size << HEAP_GRANULARITY_SHIFT;
  2526. //
  2527. // If there is a lookaside list and the block is not a big allocation
  2528. // and the index is for a dedicated list then free the block to the
  2529. // lookaside list. We'll actually capture
  2530. // the lookaside pointer from the heap and only use the captured pointer.
  2531. // This will take care of the condition where a walk or lock heap can
  2532. // cause us to check for a non null pointer and then have it become null
  2533. // when we read it again. If it is non null to start with then even if
  2534. // the user walks or locks the heap via another thread the pointer to
  2535. // still valid here so we can still try and do a lookaside list push
  2536. //
  2537. #ifndef NTOS_KERNEL_RUNTIME
  2538. if ( !(BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) ) {
  2539. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  2540. if ((Lookaside != NULL) &&
  2541. RtlpIsFrontHeapUnlocked(Heap) &&
  2542. (!(BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)) &&
  2543. ((FreeSize = BusyBlock->Size) < HEAP_MAXIMUM_FREELISTS)) {
  2544. if (RtlpFreeToHeapLookaside( &Lookaside[FreeSize], BaseAddress)) {
  2545. if( IsHeapLogging( HeapHandle ) && (TraceLevel & LOG_LOOKASIDE)) {
  2546. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2547. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2548. USHORT ReqSize = sizeof(HEAP_EVENT_FREE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2549. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  2550. if(pEventHeader && pThreadLocalData) {
  2551. PHEAP_EVENT_FREE pHeapEvent = (PHEAP_EVENT_FREE)( (SIZE_T)pEventHeader
  2552. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2553. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2554. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_FREE;
  2555. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  2556. pHeapEvent->Address = (PVOID)BaseAddress;
  2557. pHeapEvent->Source = MEMORY_FROM_LOOKASIDE;
  2558. ReleaseBufferLocation(pThreadLocalData);
  2559. }
  2560. }
  2561. return TRUE;
  2562. }
  2563. }
  2564. }
  2565. #endif // NTOS_KERNEL_RUNTIME
  2566. try {
  2567. HEAP_PERF_START_TIMER(Heap);
  2568. //
  2569. // Check if we need to lock the heap
  2570. //
  2571. if (!(Flags & HEAP_NO_SERIALIZE)) {
  2572. RtlAcquireLockRoutine( Heap->LockVariable );
  2573. LockAcquired = TRUE;
  2574. }
  2575. //
  2576. // Check if this is not a virtual block allocation meaning
  2577. // that we it is part of the heap free list structure and not
  2578. // one huge allocation that we got from vm
  2579. //
  2580. if (!(BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)) {
  2581. //
  2582. // This block is not a big allocation so we need to
  2583. // to get its size, and coalesce the blocks note that
  2584. // the user mode heap does this conditionally on a heap
  2585. // flag. The coalesce function returns the newly formed
  2586. // free block and the new size.
  2587. //
  2588. FreeSize = BusyBlock->Size;
  2589. #ifdef NTOS_KERNEL_RUNTIME
  2590. BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap,
  2591. (PHEAP_FREE_ENTRY)BusyBlock,
  2592. &FreeSize,
  2593. FALSE );
  2594. #else // NTOS_KERNEL_RUNTIME
  2595. if (!(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)) {
  2596. BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap,
  2597. (PHEAP_FREE_ENTRY)BusyBlock,
  2598. &FreeSize,
  2599. FALSE );
  2600. }
  2601. #endif // NTOS_KERNEL_RUNTIME
  2602. //
  2603. // Check for a small allocation that can go on a freelist
  2604. // first, these should never trigger a decommit.
  2605. //
  2606. HEAPASSERT(HEAP_MAXIMUM_FREELISTS < Heap->DeCommitFreeBlockThreshold);
  2607. //
  2608. // If the allocation fits on a free list then insert it on
  2609. // the appropriate free list. If the block is not the last
  2610. // entry then make sure that the next block knows our correct
  2611. // size, and update the heap free space counter.
  2612. //
  2613. if (FreeSize < HEAP_MAXIMUM_FREELISTS) {
  2614. RtlpFastInsertDedicatedFreeBlockDirect( Heap,
  2615. (PHEAP_FREE_ENTRY)BusyBlock,
  2616. (USHORT)FreeSize );
  2617. if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  2618. HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
  2619. }
  2620. Heap->TotalFreeSize += FreeSize;
  2621. //
  2622. // Otherwise the block is to big for one of the dedicated free list so
  2623. // see if the free size is under the decommit threshold by itself
  2624. // or the total free in the heap is under the decomit threshold then
  2625. // we'll put this into a free list
  2626. //
  2627. } else if ((FreeSize < Heap->DeCommitFreeBlockThreshold) ||
  2628. ((Heap->TotalFreeSize + FreeSize) < Heap->DeCommitTotalFreeThreshold)) {
  2629. #ifndef NTOS_KERNEL_RUNTIME
  2630. //
  2631. // If the block is larger than 1 page, and has uncommited ranges around
  2632. // force the decommit to reduce the VA fragmentation
  2633. //
  2634. if (((Heap->TotalFreeSize + FreeSize) > Heap->DeCommitTotalFreeThreshold)
  2635. &&
  2636. !(RtlpDisableHeapLookaside & HEAP_COMPAT_DISABLE_LARGECACHE)
  2637. &&
  2638. (FreeSize >= (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT))
  2639. &&
  2640. ((BusyBlock->PreviousSize == 0) || (BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY))) {
  2641. //
  2642. // Check if the block can go into the [0] index free list, and if
  2643. // so then do the insert and make sure the following block is
  2644. // needed knows our correct size, and update the heaps free space
  2645. // counter
  2646. //
  2647. RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
  2648. } else
  2649. #endif //NTOS_KERNEL_RUNTIME
  2650. //
  2651. // Check if the block can go into the [0] index free list, and if
  2652. // so then do the insert and make sure the following block is
  2653. // needed knows our correct size, and update the heaps free space
  2654. // counter
  2655. //
  2656. if (FreeSize <= (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
  2657. RtlpFastInsertNonDedicatedFreeBlockDirect( Heap,
  2658. (PHEAP_FREE_ENTRY)BusyBlock,
  2659. (USHORT)FreeSize );
  2660. if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  2661. HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
  2662. }
  2663. Heap->TotalFreeSize += FreeSize;
  2664. } else {
  2665. //
  2666. // The block is too big to go on a free list in its
  2667. // entirety but we don't want to decommit anything so
  2668. // simply call a worker routine to hack up the block
  2669. // into pieces that will fit on the free lists.
  2670. //
  2671. RtlpInsertFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
  2672. }
  2673. //
  2674. // Otherwise the block is to big for any lists and we should decommit
  2675. // the block
  2676. //
  2677. } else {
  2678. RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
  2679. }
  2680. } else {
  2681. //
  2682. // This is a big virtual block allocation. To free it we only have to
  2683. // remove it from the heaps list of virtual allocated blocks, unlock
  2684. // the heap, and return the block to vm
  2685. //
  2686. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2687. VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  2688. RemoveEntryList( &VirtualAllocBlock->Entry );
  2689. //
  2690. // Release lock here as there is no reason to hold it across
  2691. // the system call.
  2692. //
  2693. if (LockAcquired) {
  2694. RtlReleaseLockRoutine( Heap->LockVariable );
  2695. LockAcquired = FALSE;
  2696. }
  2697. FreeSize = 0;
  2698. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  2699. (PVOID *)&VirtualAllocBlock,
  2700. &FreeSize,
  2701. MEM_RELEASE );
  2702. //
  2703. // Check if we had trouble freeing the block back to vm
  2704. // and return an error if necessary
  2705. //
  2706. if (!NT_SUCCESS( Status )) {
  2707. SET_LAST_STATUS( Status );
  2708. ReturnValue = FALSE;
  2709. }
  2710. }
  2711. } finally {
  2712. if (LockAcquired) {
  2713. RtlReleaseLockRoutine( Heap->LockVariable );
  2714. }
  2715. }
  2716. //
  2717. // The block was freed successfully so return success to our
  2718. // caller
  2719. //
  2720. RtlpRegisterOperation(Heap, BlockSize, HEAP_OP_FREE);
  2721. HEAP_PERF_STOP_TIMER(Heap, HEAP_OP_FREE);
  2722. #ifndef NTOS_KERNEL_RUNTIME
  2723. if( IsHeapLogging( HeapHandle ) ) {
  2724. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2725. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2726. USHORT ReqSize = sizeof(HEAP_EVENT_FREE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2727. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  2728. if(pEventHeader && pThreadLocalData) {
  2729. PHEAP_EVENT_FREE pHeapEvent = (PHEAP_EVENT_FREE)( (SIZE_T)pEventHeader
  2730. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2731. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2732. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_FREE;
  2733. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  2734. pHeapEvent->Address = (PVOID)BaseAddress;
  2735. pHeapEvent->Source = MEMORY_FROM_MAINPATH;
  2736. ReleaseBufferLocation(pThreadLocalData);
  2737. }
  2738. }
  2739. #endif //NTOS_KERNEL_RUNTIME
  2740. return ReturnValue;
  2741. }
  2742. BOOLEAN
  2743. RtlFreeHeapSlowly (
  2744. IN PVOID HeapHandle,
  2745. IN ULONG Flags,
  2746. IN PVOID BaseAddress
  2747. )
  2748. /*++
  2749. Routine Description:
  2750. This routine returns a previously allocated block back to its heap.
  2751. It is the slower version of Rtl Free Heap and does more checking and
  2752. tagging control.
  2753. Arguments:
  2754. HeapHandle - Supplies a pointer to the owning heap structure
  2755. Flags - Specifies the set of flags to use in the deallocation
  2756. BaseAddress - Supplies a pointer to the block being freed
  2757. Return Value:
  2758. BOOLEAN - TRUE if the block was properly freed and FALSE otherwise
  2759. --*/
  2760. {
  2761. NTSTATUS Status;
  2762. PHEAP Heap = (PHEAP)HeapHandle;
  2763. PHEAP_ENTRY BusyBlock;
  2764. PHEAP_ENTRY_EXTRA ExtraStuff;
  2765. SIZE_T FreeSize;
  2766. BOOLEAN Result;
  2767. BOOLEAN LockAcquired = FALSE;
  2768. SIZE_T BlockSize;
  2769. #ifndef NTOS_KERNEL_RUNTIME
  2770. USHORT TagIndex;
  2771. #endif // NTOS_KERNEL_RUNTIME
  2772. HEAP_PERF_DECLARE_TIMER();
  2773. RTL_PAGED_CODE();
  2774. //
  2775. // Note that Flags has already been OR'd with Heap->ForceFlags.
  2776. //
  2777. #ifndef NTOS_KERNEL_RUNTIME
  2778. //
  2779. // In the non kernel case see if we should be calling the debug version to
  2780. // free the heap
  2781. //
  2782. if (DEBUG_HEAP( Flags )) {
  2783. return RtlDebugFreeHeap( HeapHandle, Flags, BaseAddress );
  2784. }
  2785. #endif // NTOS_KERNEL_RUNTIME
  2786. //
  2787. // Until we figure out otherwise we'll assume that this call will fail
  2788. //
  2789. Result = FALSE;
  2790. try {
  2791. HEAP_PERF_START_TIMER(Heap);
  2792. //
  2793. // Lock the heap
  2794. //
  2795. if (!(Flags & HEAP_NO_SERIALIZE)) {
  2796. RtlAcquireLockRoutine( Heap->LockVariable );
  2797. LockAcquired = TRUE;
  2798. }
  2799. try {
  2800. //
  2801. // Backup to get a pointer to the start of the block
  2802. //
  2803. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  2804. BlockSize = BusyBlock->Size << HEAP_GRANULARITY_SHIFT;
  2805. //
  2806. // Protect ourselves from mistakes by refusing to free blocks
  2807. // that do not have the busy bit set.
  2808. //
  2809. // Also refuse to free blocks that are not eight-byte aligned.
  2810. // The specific mistake in this case is Office95, which likes
  2811. // to free a random pointer when you start Word95 from a desktop
  2812. // shortcut.
  2813. //
  2814. // As further insurance against mistakes, check the segment index
  2815. // to make sure it is less than HEAP_MAXIMUM_SEGMENTS (16). This
  2816. // should fix all the dorks who have ASCII or Unicode where the
  2817. // heap header is supposed to be.
  2818. //
  2819. // Note that this test is just opposite from the test used in
  2820. // Rtl Free Heap
  2821. //
  2822. if ((BusyBlock->Flags & HEAP_ENTRY_BUSY) &&
  2823. (((ULONG_PTR)BaseAddress & 0x7) == 0) &&
  2824. (BusyBlock->SegmentIndex < HEAP_MAXIMUM_SEGMENTS)) {
  2825. //
  2826. // Check if this is a virtual block allocation
  2827. //
  2828. if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  2829. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2830. //
  2831. // This is a big virtual block allocation. To free it
  2832. // we only have to remove it from the heaps list of
  2833. // virtual allocated blocks, unlock the heap, and return
  2834. // the block to vm
  2835. //
  2836. VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  2837. RemoveEntryList( &VirtualAllocBlock->Entry );
  2838. #ifndef NTOS_KERNEL_RUNTIME
  2839. //
  2840. // In the non kernel case see if we need to free the tag
  2841. //
  2842. if (IS_HEAP_TAGGING_ENABLED()) {
  2843. RtlpUpdateTagEntry( Heap,
  2844. VirtualAllocBlock->ExtraStuff.TagIndex,
  2845. VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT,
  2846. 0,
  2847. VirtualFreeAction );
  2848. }
  2849. #endif // NTOS_KERNEL_RUNTIME
  2850. FreeSize = 0;
  2851. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  2852. (PVOID *)&VirtualAllocBlock,
  2853. &FreeSize,
  2854. MEM_RELEASE );
  2855. //
  2856. // Check if everything worked okay, if we had trouble freeing
  2857. // the block back to vm return an error if necessary,
  2858. //
  2859. if (NT_SUCCESS( Status )) {
  2860. #ifndef NTOS_KERNEL_RUNTIME
  2861. if( IsHeapLogging( HeapHandle ) ) {
  2862. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2863. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2864. USHORT ReqSize = sizeof(HEAP_EVENT_FREE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2865. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  2866. if(pEventHeader && pThreadLocalData) {
  2867. PHEAP_EVENT_FREE pHeapEvent = (PHEAP_EVENT_FREE)( (SIZE_T)pEventHeader
  2868. +(SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2869. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2870. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_FREE;
  2871. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  2872. pHeapEvent->Address = (PVOID)BaseAddress;
  2873. pHeapEvent->Source = MEMORY_FROM_SLOWPATH;
  2874. ReleaseBufferLocation(pThreadLocalData);
  2875. }
  2876. }
  2877. #endif //NTOS_KERNEL_RUNTIME
  2878. Result = TRUE;
  2879. } else {
  2880. SET_LAST_STATUS( Status );
  2881. }
  2882. } else {
  2883. //
  2884. // This block is not a big allocation so we need to
  2885. // to get its size, and coalesce the blocks note that
  2886. // the user mode heap does this conditionally on a heap
  2887. // flag. The coalesce function returns the newly formed
  2888. // free block and the new size.
  2889. //
  2890. #ifndef NTOS_KERNEL_RUNTIME
  2891. //
  2892. // First in the non kernel case remove any tagging we might
  2893. // have been using. Note that the will either be in
  2894. // the heap header, or in the extra block if present
  2895. //
  2896. if (IS_HEAP_TAGGING_ENABLED()) {
  2897. if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
  2898. ExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
  2899. TagIndex = RtlpUpdateTagEntry( Heap,
  2900. ExtraStuff->TagIndex,
  2901. BusyBlock->Size,
  2902. 0,
  2903. FreeAction );
  2904. } else {
  2905. TagIndex = RtlpUpdateTagEntry( Heap,
  2906. BusyBlock->SmallTagIndex,
  2907. BusyBlock->Size,
  2908. 0,
  2909. FreeAction );
  2910. }
  2911. } else {
  2912. TagIndex = 0;
  2913. }
  2914. #endif // NTOS_KERNEL_RUNTIME
  2915. //
  2916. // This is the size of the block we are freeing
  2917. //
  2918. FreeSize = BusyBlock->Size;
  2919. #ifndef NTOS_KERNEL_RUNTIME
  2920. //
  2921. // In the non kernel case see if we should coalesce on free
  2922. //
  2923. if (!(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)) {
  2924. #endif // NTOS_KERNEL_RUNTIME
  2925. //
  2926. // In kernel case and in the tested user mode case we
  2927. // now coalesce free blocks
  2928. //
  2929. BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap, (PHEAP_FREE_ENTRY)BusyBlock, &FreeSize, FALSE );
  2930. #ifndef NTOS_KERNEL_RUNTIME
  2931. }
  2932. #endif // NTOS_KERNEL_RUNTIME
  2933. //
  2934. // If the block should not be decommit then try and put it
  2935. // on a free list
  2936. //
  2937. if ((FreeSize < Heap->DeCommitFreeBlockThreshold) ||
  2938. ((Heap->TotalFreeSize + FreeSize) < Heap->DeCommitTotalFreeThreshold)) {
  2939. //
  2940. // Check if the block can fit on one of the dedicated free
  2941. // lists
  2942. //
  2943. if (FreeSize <= (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
  2944. //
  2945. // It can fit on a dedicated free list so insert it on
  2946. //
  2947. RtlpInsertFreeBlockDirect( Heap, (PHEAP_FREE_ENTRY)BusyBlock, (USHORT)FreeSize );
  2948. //
  2949. // If there is a following entry then make sure the
  2950. // sizes agree
  2951. //
  2952. if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  2953. HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
  2954. }
  2955. //
  2956. // Update the heap with the amount of free space
  2957. // available
  2958. //
  2959. Heap->TotalFreeSize += FreeSize;
  2960. } else {
  2961. //
  2962. // The block goes on the non dedicated free list
  2963. //
  2964. RtlpInsertFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
  2965. }
  2966. #ifndef NTOS_KERNEL_RUNTIME
  2967. //
  2968. // In the non kernel case see if the there was tag and if
  2969. // so then update the entry to show that it's been freed
  2970. //
  2971. if (TagIndex != 0) {
  2972. PHEAP_FREE_ENTRY_EXTRA FreeExtra;
  2973. BusyBlock->Flags |= HEAP_ENTRY_EXTRA_PRESENT;
  2974. FreeExtra = (PHEAP_FREE_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size) - 1;
  2975. FreeExtra->TagIndex = TagIndex;
  2976. FreeExtra->FreeBackTraceIndex = 0;
  2977. if (Heap->Flags & HEAP_CAPTURE_STACK_BACKTRACES) {
  2978. FreeExtra->FreeBackTraceIndex = (USHORT)RtlLogStackBackTrace();
  2979. }
  2980. }
  2981. #endif // NTOS_KERNEL_RUNTIME
  2982. } else {
  2983. //
  2984. // Otherwise the block is big enough to decommit so have a
  2985. // worker routine to do the decommit
  2986. //
  2987. RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
  2988. }
  2989. #ifndef NTOS_KERNEL_RUNTIME
  2990. if( IsHeapLogging( HeapHandle ) ) {
  2991. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  2992. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  2993. USHORT ReqSize = sizeof(HEAP_EVENT_FREE) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  2994. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  2995. if(pEventHeader && pThreadLocalData) {
  2996. PHEAP_EVENT_FREE pHeapEvent = (PHEAP_EVENT_FREE)( (SIZE_T)pEventHeader
  2997. +(SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  2998. pEventHeader->Packet.Size = (USHORT) ReqSize;
  2999. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_FREE;
  3000. pHeapEvent->HeapHandle = (PVOID)HeapHandle;
  3001. pHeapEvent->Address = (PVOID)BaseAddress;
  3002. pHeapEvent->Source = MEMORY_FROM_SLOWPATH;
  3003. ReleaseBufferLocation(pThreadLocalData);
  3004. }
  3005. }
  3006. #endif //NTOS_KERNEL_RUNTIME
  3007. //
  3008. // And say the free worked fine
  3009. //
  3010. Result = TRUE;
  3011. }
  3012. } else {
  3013. //
  3014. // Not a busy block, or it's not aligned or the segment is
  3015. // to big, meaning it's corrupt
  3016. //
  3017. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  3018. }
  3019. } except( EXCEPTION_EXECUTE_HANDLER ) {
  3020. SET_LAST_STATUS( GetExceptionCode() );
  3021. Result = FALSE;
  3022. }
  3023. } finally {
  3024. //
  3025. // Unlock the heap
  3026. //
  3027. if (LockAcquired) {
  3028. RtlReleaseLockRoutine( Heap->LockVariable );
  3029. }
  3030. }
  3031. //
  3032. // And return to our caller
  3033. //
  3034. RtlpRegisterOperation(Heap, BlockSize, HEAP_OP_FREE);
  3035. HEAP_PERF_STOP_TIMER(Heap, HEAP_OP_FREE);
  3036. return Result;
  3037. }
  3038. SIZE_T
  3039. RtlSizeHeap (
  3040. IN PVOID HeapHandle,
  3041. IN ULONG Flags,
  3042. IN PVOID BaseAddress
  3043. )
  3044. /*++
  3045. Routine Description:
  3046. This routine returns the size, in bytes, of the indicated block
  3047. of heap storage. The size only includes the number of bytes the
  3048. original caller used to allocate the block and not any unused
  3049. bytes at the end of the block.
  3050. Arguments:
  3051. HeapHandle - Supplies a pointer to the heap that owns the block
  3052. being queried
  3053. Flags - Supplies a set of flags used to allocate the block
  3054. BaseAddress - Supplies the address of the block being queried
  3055. Return Value:
  3056. SIZE_T - returns the size, in bytes, of the queried block, or -1
  3057. if the block is not in use.
  3058. --*/
  3059. {
  3060. PHEAP Heap = (PHEAP)HeapHandle;
  3061. PHEAP_ENTRY BusyBlock;
  3062. SIZE_T BusySize;
  3063. //
  3064. // Compliment the input flags with those enforced by the heap
  3065. //
  3066. Flags |= Heap->ForceFlags;
  3067. //
  3068. // Check if this is the nonkernel debug version of heap
  3069. //
  3070. #ifndef NTOS_KERNEL_RUNTIME
  3071. if (DEBUG_HEAP( Flags )) {
  3072. return RtlDebugSizeHeap( HeapHandle, Flags, BaseAddress );
  3073. }
  3074. #endif // NTOS_KERNEL_RUNTIME
  3075. //
  3076. // No lock is required since nothing is modified and nothing
  3077. // outside the busy block is read. Backup to get a pointer
  3078. // to the heap entry
  3079. //
  3080. BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
  3081. //
  3082. // If the block is not in use then the answer is -1 and
  3083. // we'll set the error status for the user mode thread
  3084. //
  3085. if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
  3086. BusySize = -1;
  3087. SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
  3088. //
  3089. // Otherwise if the block is from our large allocation then
  3090. // we'll get the result from that routine
  3091. //
  3092. } else if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  3093. BusySize = RtlpGetSizeOfBigBlock( BusyBlock );
  3094. //
  3095. // Otherwise the block must be one that we can handle so
  3096. // calculate its block size and then subtract what's not being
  3097. // used by the caller.
  3098. //
  3099. // Note: This includes the heap entry header in its calculation.
  3100. //
  3101. } else {
  3102. BusySize = (((SIZE_T)RtlpGetAllocationUnits(Heap, BusyBlock)) << HEAP_GRANULARITY_SHIFT) -
  3103. RtlpGetUnusedBytes(Heap, BusyBlock);
  3104. }
  3105. //
  3106. // And return to our caller
  3107. //
  3108. return BusySize;
  3109. }
  3110. NTSTATUS
  3111. RtlZeroHeap (
  3112. IN PVOID HeapHandle,
  3113. IN ULONG Flags
  3114. )
  3115. /*++
  3116. Routine Description:
  3117. This routine zero's (or fills) in all the free blocks in a heap.
  3118. It does not touch big allocations.
  3119. Arguments:
  3120. HeapHandle - Supplies a pointer to the heap being zeroed
  3121. Flags - Supplies a set of heap flags to compliment those already
  3122. set in the heap
  3123. Return Value:
  3124. NTSTATUS - An appropriate status code
  3125. --*/
  3126. {
  3127. PHEAP Heap = (PHEAP)HeapHandle;
  3128. NTSTATUS Status;
  3129. BOOLEAN LockAcquired = FALSE;
  3130. PHEAP_SEGMENT Segment;
  3131. ULONG SegmentIndex;
  3132. PHEAP_ENTRY CurrentBlock;
  3133. PHEAP_FREE_ENTRY FreeBlock;
  3134. SIZE_T Size;
  3135. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
  3136. RTL_PAGED_CODE();
  3137. //
  3138. // Compliment the input flags with those enforced by the heap
  3139. //
  3140. Flags |= Heap->ForceFlags;
  3141. //
  3142. // Check if this is the nonkernel debug version of heap
  3143. //
  3144. #ifndef NTOS_KERNEL_RUNTIME
  3145. if (DEBUG_HEAP( Flags )) {
  3146. return RtlDebugZeroHeap( HeapHandle, Flags );
  3147. }
  3148. #endif // NTOS_KERNEL_RUNTIME
  3149. //
  3150. // Unless something happens otherwise we'll assume that we'll
  3151. // be successful
  3152. //
  3153. Status = STATUS_SUCCESS;
  3154. try {
  3155. //
  3156. // Lock the heap
  3157. //
  3158. if (!(Flags & HEAP_NO_SERIALIZE)) {
  3159. RtlAcquireLockRoutine( Heap->LockVariable );
  3160. LockAcquired = TRUE;
  3161. }
  3162. try {
  3163. //
  3164. // Zero fill all the free blocks in all the segements
  3165. //
  3166. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  3167. Segment = Heap->Segments[ SegmentIndex ];
  3168. if (!Segment) {
  3169. continue;
  3170. }
  3171. UnCommittedRange = Segment->UnCommittedRanges;
  3172. CurrentBlock = Segment->FirstEntry;
  3173. //
  3174. // With the current segment we'll zoom through the
  3175. // blocks until we reach the end
  3176. //
  3177. while (CurrentBlock < Segment->LastValidEntry) {
  3178. Size = CurrentBlock->Size << HEAP_GRANULARITY_SHIFT;
  3179. //
  3180. // If the block is not in use then we'll either zero
  3181. // it or fill it.
  3182. //
  3183. if (!(CurrentBlock->Flags & HEAP_ENTRY_BUSY)) {
  3184. FreeBlock = (PHEAP_FREE_ENTRY)CurrentBlock;
  3185. if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) &&
  3186. (CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN)) {
  3187. RtlFillMemoryUlong( FreeBlock + 1,
  3188. Size - sizeof( *FreeBlock ),
  3189. FREE_HEAP_FILL );
  3190. } else {
  3191. RtlFillMemoryUlong( FreeBlock + 1,
  3192. Size - sizeof( *FreeBlock ),
  3193. 0 );
  3194. }
  3195. }
  3196. //
  3197. // If the following entry is uncommited then we need to
  3198. // skip over it. This code strongly implies that the
  3199. // uncommitted range list is in perfect sync with the
  3200. // blocks in the segement
  3201. //
  3202. if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  3203. CurrentBlock += CurrentBlock->Size;
  3204. //
  3205. // Check if the we've reached the end of the segment
  3206. // and should just break out of the while loop
  3207. //
  3208. // "break;" would probably be more clear here
  3209. //
  3210. if (UnCommittedRange == NULL) {
  3211. CurrentBlock = Segment->LastValidEntry;
  3212. //
  3213. // Otherwise skip over the uncommitted range
  3214. //
  3215. } else {
  3216. CurrentBlock = (PHEAP_ENTRY)
  3217. ((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size);
  3218. UnCommittedRange = UnCommittedRange->Next;
  3219. }
  3220. //
  3221. // Otherwise the next block exists so advance to it
  3222. //
  3223. } else {
  3224. CurrentBlock += CurrentBlock->Size;
  3225. }
  3226. }
  3227. }
  3228. } except( EXCEPTION_EXECUTE_HANDLER ) {
  3229. Status = GetExceptionCode();
  3230. }
  3231. } finally {
  3232. //
  3233. // Unlock the heap
  3234. //
  3235. if (LockAcquired) {
  3236. RtlReleaseLockRoutine( Heap->LockVariable );
  3237. }
  3238. }
  3239. return Status;
  3240. }
  3241. //
  3242. // Local Support Routine
  3243. //
  3244. PHEAP_UNCOMMMTTED_RANGE
  3245. RtlpCreateUnCommittedRange (
  3246. IN PHEAP_SEGMENT Segment
  3247. )
  3248. /*++
  3249. Routine Description:
  3250. This routine add a new uncommitted range structure to the specified heap
  3251. segment. This routine works by essentially doing a pop of the stack of
  3252. unused uncommitted range structures located off the heap structure. If
  3253. the stack is empty then we'll create some more before doing the pop.
  3254. Arguments:
  3255. Segment - Supplies the heap segment being modified
  3256. Return Value:
  3257. PHEAP_UNCOMMITTED_RANGE - returns a pointer to the newly created
  3258. uncommitted range structure
  3259. --*/
  3260. {
  3261. NTSTATUS Status;
  3262. PVOID FirstEntry, LastEntry;
  3263. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
  3264. SIZE_T ReserveSize, CommitSize;
  3265. PHEAP_UCR_SEGMENT UCRSegment;
  3266. RTL_PAGED_CODE();
  3267. //
  3268. // Get a pointer to the unused uncommitted range structures for
  3269. // the specified heap
  3270. //
  3271. pp = &Segment->Heap->UnusedUnCommittedRanges;
  3272. //
  3273. // If the list is null then we need to allocate some more to
  3274. // put on the list
  3275. //
  3276. if (*pp == NULL) {
  3277. //
  3278. // Get the next uncommitted range segment from the heap
  3279. //
  3280. UCRSegment = Segment->Heap->UCRSegments;
  3281. //
  3282. // If there are no more uncommitted range segments or
  3283. // the segments commited and reserved sizes are equal (meaning
  3284. // it's all used up) then we need to allocate another uncommitted
  3285. // range segment
  3286. //
  3287. if ((UCRSegment == NULL) ||
  3288. (UCRSegment->CommittedSize == UCRSegment->ReservedSize)) {
  3289. //
  3290. // We'll reserve 16 pages of memory and commit at this
  3291. // time one page of it.
  3292. //
  3293. ReserveSize = PAGE_SIZE * 16;
  3294. UCRSegment = NULL;
  3295. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  3296. &UCRSegment,
  3297. 0,
  3298. &ReserveSize,
  3299. MEM_RESERVE,
  3300. PAGE_READWRITE );
  3301. if (!NT_SUCCESS( Status )) {
  3302. return NULL;
  3303. }
  3304. CommitSize = PAGE_SIZE;
  3305. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  3306. &UCRSegment,
  3307. 0,
  3308. &CommitSize,
  3309. MEM_COMMIT,
  3310. PAGE_READWRITE );
  3311. if (!NT_SUCCESS( Status )) {
  3312. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  3313. &UCRSegment,
  3314. &ReserveSize,
  3315. MEM_RELEASE );
  3316. return NULL;
  3317. }
  3318. //
  3319. // Add this new segment to the front of the UCR segments
  3320. //
  3321. UCRSegment->Next = Segment->Heap->UCRSegments;
  3322. Segment->Heap->UCRSegments = UCRSegment;
  3323. //
  3324. // Set the segments commit and reserve size
  3325. //
  3326. UCRSegment->ReservedSize = ReserveSize;
  3327. UCRSegment->CommittedSize = CommitSize;
  3328. //
  3329. // Point to the first free spot in the segment
  3330. //
  3331. FirstEntry = (PCHAR)(UCRSegment + 1);
  3332. } else {
  3333. //
  3334. // We have an existing UCR segment with available space
  3335. // So now try and commit another PAGE_SIZE bytes. When we are done
  3336. // FirstEntry will point to the newly committed space
  3337. //
  3338. CommitSize = PAGE_SIZE;
  3339. FirstEntry = (PCHAR)UCRSegment + UCRSegment->CommittedSize;
  3340. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  3341. &FirstEntry,
  3342. 0,
  3343. &CommitSize,
  3344. MEM_COMMIT,
  3345. PAGE_READWRITE );
  3346. if (!NT_SUCCESS( Status )) {
  3347. return NULL;
  3348. }
  3349. //
  3350. // And update the amount committed in the segment
  3351. //
  3352. UCRSegment->CommittedSize += CommitSize;
  3353. }
  3354. //
  3355. // At this point UCR segment exists and First Entry points to the
  3356. // start of the available committed space. We'll make Last Entry
  3357. // point to the end of the committed space
  3358. //
  3359. LastEntry = (PCHAR)UCRSegment + UCRSegment->CommittedSize;
  3360. //
  3361. // Now the task is to push all of this new space unto the
  3362. // unused uncommitted range list off the heap, then we can
  3363. // do a regular pop
  3364. //
  3365. UnCommittedRange = (PHEAP_UNCOMMMTTED_RANGE)FirstEntry;
  3366. pp = &Segment->Heap->UnusedUnCommittedRanges;
  3367. while ((PCHAR)UnCommittedRange < (PCHAR)LastEntry) {
  3368. *pp = UnCommittedRange;
  3369. pp = &UnCommittedRange->Next;
  3370. UnCommittedRange += 1;
  3371. }
  3372. //
  3373. // Null terminate the list
  3374. //
  3375. *pp = NULL;
  3376. //
  3377. // And have Pp point the new top of the list
  3378. //
  3379. pp = &Segment->Heap->UnusedUnCommittedRanges;
  3380. }
  3381. //
  3382. // At this point the Pp points to a non empty list of unused uncommitted
  3383. // range structures. So we pop the list and return the top to our caller
  3384. //
  3385. UnCommittedRange = *pp;
  3386. *pp = UnCommittedRange->Next;
  3387. return UnCommittedRange;
  3388. }
  3389. //
  3390. // Local Support Routine
  3391. //
  3392. VOID
  3393. RtlpDestroyUnCommittedRange (
  3394. IN PHEAP_SEGMENT Segment,
  3395. IN PHEAP_UNCOMMMTTED_RANGE UnCommittedRange
  3396. )
  3397. /*++
  3398. Routine Description:
  3399. This routine returns an uncommitted range structure back to the unused
  3400. uncommitted range list
  3401. Arguments:
  3402. Segment - Supplies any segment in the heap being modified. Most likely but
  3403. not necessarily the segment containing the uncommitted range structure
  3404. UnCommittedRange - Supplies a pointer to the uncommitted range structure
  3405. being decommissioned.
  3406. Return Value:
  3407. None.
  3408. --*/
  3409. {
  3410. RTL_PAGED_CODE();
  3411. //
  3412. // This routine simply does a "push" of the uncommitted range structure
  3413. // onto the heap's stack of unused uncommitted ranges
  3414. //
  3415. UnCommittedRange->Next = Segment->Heap->UnusedUnCommittedRanges;
  3416. Segment->Heap->UnusedUnCommittedRanges = UnCommittedRange;
  3417. //
  3418. // For safety sake we'll also zero out the fields in the decommissioned
  3419. // structure
  3420. //
  3421. UnCommittedRange->Address = 0;
  3422. UnCommittedRange->Size = 0;
  3423. //
  3424. // And return to our caller
  3425. //
  3426. return;
  3427. }
  3428. //
  3429. // Local Support Routine
  3430. //
  3431. VOID
  3432. RtlpInsertUnCommittedPages (
  3433. IN PHEAP_SEGMENT Segment,
  3434. IN ULONG_PTR Address,
  3435. IN SIZE_T Size
  3436. )
  3437. /*++
  3438. Routine Description:
  3439. This routine adds the specified range to the list of uncommitted pages
  3440. in the segment. When done the information will hang off the segments
  3441. uncommitted ranges list.
  3442. Arguments:
  3443. Segment - Supplies a segment whose uncommitted range is being modified
  3444. Address - Supplies the base (start) address for the uncommitted range
  3445. Size - Supplies the size, in bytes, of the uncommitted range
  3446. Return Value:
  3447. None.
  3448. --*/
  3449. {
  3450. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
  3451. RTL_PAGED_CODE();
  3452. //
  3453. // Get a pointer to the front of the segments uncommitted range list
  3454. // The list is sorted by ascending address
  3455. //
  3456. pp = &Segment->UnCommittedRanges;
  3457. //
  3458. // While we haven't reached the end of the list we'll zoom through
  3459. // trying to find a fit
  3460. //
  3461. while (UnCommittedRange = *pp) {
  3462. //
  3463. // If address we want is less than what we're pointing at then
  3464. // we've found where this new entry goes
  3465. //
  3466. if (UnCommittedRange->Address > Address) {
  3467. //
  3468. // If the new block matches right up to the existing block
  3469. // then we can simply backup the existing block and add
  3470. // to its size
  3471. //
  3472. if ((Address + Size) == UnCommittedRange->Address) {
  3473. UnCommittedRange->Address = Address;
  3474. UnCommittedRange->Size += Size;
  3475. //
  3476. // Check if we need to update our notion of what the
  3477. // largest uncommitted range is
  3478. //
  3479. if (UnCommittedRange->Size > Segment->LargestUnCommittedRange) {
  3480. Segment->LargestUnCommittedRange = UnCommittedRange->Size;
  3481. }
  3482. //
  3483. // And return to our caller
  3484. //
  3485. return;
  3486. }
  3487. //
  3488. // Pp is the address of the block right before us, and *Pp is the
  3489. // address of the block right after us. So now fall out to where
  3490. // the insertion takes place.
  3491. //
  3492. break;
  3493. //
  3494. // Otherwise if this existing block stops right where the new block
  3495. // starts then we get to modify this entry.
  3496. //
  3497. } else if ((UnCommittedRange->Address + UnCommittedRange->Size) == Address) {
  3498. //
  3499. // Remember the starting address and compute the new larger size
  3500. //
  3501. Address = UnCommittedRange->Address;
  3502. Size += UnCommittedRange->Size;
  3503. //
  3504. // Remove this entry from the list and then return it to the
  3505. // unused uncommitted list
  3506. //
  3507. *pp = UnCommittedRange->Next;
  3508. RtlpDestroyUnCommittedRange( Segment, UnCommittedRange );
  3509. //
  3510. // Modify the segment counters and largest size state. The next
  3511. // time through the loop should hit the first case above where
  3512. // we'll either merge with a list following us or add a new
  3513. // entry
  3514. //
  3515. Segment->NumberOfUnCommittedRanges -= 1;
  3516. if (Size > Segment->LargestUnCommittedRange) {
  3517. Segment->LargestUnCommittedRange = Size;
  3518. }
  3519. //
  3520. // Otherwise we'll continue search down the list
  3521. //
  3522. } else {
  3523. pp = &UnCommittedRange->Next;
  3524. }
  3525. }
  3526. //
  3527. // If we reach this point that means we've either fallen off the end of the
  3528. // list, or the list is empty, or we've located the spot where a new uncommitted
  3529. // range structure belongs. So allocate a new uncommitted range structure,
  3530. // and make sure we got one.
  3531. //
  3532. // Pp is the address of the block right before us and *Pp is the address of the
  3533. // block right after us
  3534. //
  3535. UnCommittedRange = RtlpCreateUnCommittedRange( Segment );
  3536. if (UnCommittedRange == NULL) {
  3537. HeapDebugPrint(( "Abandoning uncommitted range (%x for %x)\n", Address, Size ));
  3538. // HeapDebugBreak( NULL );
  3539. return;
  3540. }
  3541. //
  3542. // Fill in the new uncommitted range structure
  3543. //
  3544. UnCommittedRange->Address = Address;
  3545. UnCommittedRange->Size = Size;
  3546. //
  3547. // Insert it in the list for the segment
  3548. //
  3549. UnCommittedRange->Next = *pp;
  3550. *pp = UnCommittedRange;
  3551. //
  3552. // Update the segment counters and notion of the largest uncommitted range
  3553. //
  3554. Segment->NumberOfUnCommittedRanges += 1;
  3555. if (Size >= Segment->LargestUnCommittedRange) {
  3556. Segment->LargestUnCommittedRange = Size;
  3557. }
  3558. //
  3559. // And return to our caller
  3560. //
  3561. return;
  3562. }
  3563. //
  3564. // Declared in heappriv.h
  3565. //
  3566. PHEAP_FREE_ENTRY
  3567. RtlpFindAndCommitPages (
  3568. IN PHEAP Heap,
  3569. IN PHEAP_SEGMENT Segment,
  3570. IN OUT PSIZE_T Size,
  3571. IN PVOID AddressWanted OPTIONAL
  3572. )
  3573. /*++
  3574. Routine Description:
  3575. This function searches the supplied segment for an uncommitted range that
  3576. satisfies the specified size. It commits the range and returns a heap entry
  3577. for the range.
  3578. Arguments:
  3579. Heap - Supplies the heap being manipulated
  3580. Segment - Supplies the segment being searched
  3581. Size - Supplies the size of what we need to look for, on return it contains
  3582. the size of what we're just found and committed.
  3583. AddressWanted - Optionally gives an address where we would like the pages
  3584. based. If supplied the entry must start at this address
  3585. Return Value:
  3586. PHEAP_FREE_ENTRY - Returns a pointer to the newly committed range that
  3587. satisfies the given size requirement, or NULL if we could not find
  3588. something large enough and/or based at the address wanted.
  3589. --*/
  3590. {
  3591. NTSTATUS Status;
  3592. PHEAP_ENTRY FirstEntry, LastEntry, PreviousLastEntry;
  3593. PHEAP_UNCOMMMTTED_RANGE PreviousUnCommittedRange, UnCommittedRange, *pp;
  3594. ULONG_PTR Address;
  3595. SIZE_T Length;
  3596. RTL_PAGED_CODE();
  3597. //
  3598. // What the outer loop does is cycle through the uncommited ranges
  3599. // stored in in the specified segment
  3600. //
  3601. PreviousUnCommittedRange = NULL;
  3602. pp = &Segment->UnCommittedRanges;
  3603. while (UnCommittedRange = *pp) {
  3604. //
  3605. // Check for the best of worlds, where the size of this current
  3606. // uncommitted range satisfies our size request and either the user
  3607. // didn't specify an address or the address match
  3608. //
  3609. if ((UnCommittedRange->Size >= *Size) &&
  3610. (!ARGUMENT_PRESENT( AddressWanted ) || (UnCommittedRange->Address == (ULONG_PTR)AddressWanted ))) {
  3611. //
  3612. // Calculate an address
  3613. //
  3614. Address = UnCommittedRange->Address;
  3615. //
  3616. // Commit the memory. If the heap doesn't have a commit
  3617. // routine then use the default mm supplied routine.
  3618. //
  3619. if (Heap->CommitRoutine != NULL) {
  3620. Status = (Heap->CommitRoutine)( Heap,
  3621. (PVOID *)&Address,
  3622. Size );
  3623. } else {
  3624. #ifndef NTOS_KERNEL_RUNTIME
  3625. //
  3626. // If we have a small uncommited range left, Adjust the size to
  3627. // take that block too
  3628. //
  3629. if (!(RtlpDisableHeapLookaside & HEAP_COMPAT_DISABLE_LARGECACHE)
  3630. &&
  3631. ( (UnCommittedRange->Size - (*Size)) <= (((SIZE_T)Heap->DeCommitFreeBlockThreshold) << HEAP_GRANULARITY_SHIFT) )
  3632. &&
  3633. (UnCommittedRange->Size < (((SIZE_T)Heap->VirtualMemoryThreshold) << HEAP_GRANULARITY_SHIFT)) ) {
  3634. *Size = UnCommittedRange->Size;
  3635. }
  3636. #endif // NTOS_KERNEL_RUNTIME
  3637. #ifdef _WIN64
  3638. //
  3639. // This is for Wow64 processes. This is needed to return PAGE_SIZE aligned
  3640. // aligned sizes.
  3641. //
  3642. *Size = ROUND_UP_TO_POWER2 (*Size, PAGE_SIZE);
  3643. #endif
  3644. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  3645. (PVOID *)&Address,
  3646. 0,
  3647. Size,
  3648. MEM_COMMIT,
  3649. PAGE_READWRITE );
  3650. }
  3651. if (!NT_SUCCESS( Status )) {
  3652. return NULL;
  3653. }
  3654. //
  3655. // At this point we have some committed memory, with Address and Size
  3656. // giving us the necessary details
  3657. //
  3658. // Update the number of uncommitted pages in the segment and if necessary
  3659. // mark down the largest uncommitted range
  3660. //
  3661. Segment->NumberOfUnCommittedPages -= (ULONG) (*Size / PAGE_SIZE);
  3662. if (Segment->LargestUnCommittedRange == UnCommittedRange->Size) {
  3663. Segment->LargestUnCommittedRange = 0;
  3664. }
  3665. //
  3666. // First entry is the start of the newly committed range
  3667. //
  3668. FirstEntry = (PHEAP_ENTRY)Address;
  3669. //
  3670. // We want last entry to point to the last real entry before
  3671. // this newly committed spot. To do this we start by
  3672. // setting last entry to either the first entry for the
  3673. // segment or (if we can do better), to right after the last
  3674. // uncommitted range we examined. Either way it points to
  3675. // some committed range
  3676. //
  3677. if ((Segment->LastEntryInSegment->Flags & HEAP_ENTRY_LAST_ENTRY) &&
  3678. (ULONG_PTR)(Segment->LastEntryInSegment + Segment->LastEntryInSegment->Size) == UnCommittedRange->Address) {
  3679. LastEntry = Segment->LastEntryInSegment;
  3680. } else {
  3681. if (PreviousUnCommittedRange == NULL) {
  3682. LastEntry = Segment->FirstEntry;
  3683. } else {
  3684. LastEntry = (PHEAP_ENTRY)(PreviousUnCommittedRange->Address +
  3685. PreviousUnCommittedRange->Size);
  3686. }
  3687. //
  3688. // Now we zoom through the entries until we find the one
  3689. // marked last
  3690. //
  3691. while (!(LastEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  3692. PreviousLastEntry = LastEntry;
  3693. LastEntry += LastEntry->Size;
  3694. if (((PCHAR)LastEntry >= (PCHAR)Segment->LastValidEntry) || (LastEntry->Size == 0)) {
  3695. //
  3696. // Check for the situation where the last entry in the
  3697. // segment isn't marked as a last entry but does put
  3698. // us right where the have a new committed range
  3699. //
  3700. if (LastEntry == (PHEAP_ENTRY)Address) {
  3701. LastEntry = PreviousLastEntry;
  3702. break;
  3703. }
  3704. HeapDebugPrint(( "Heap missing last entry in committed range near %x\n", PreviousLastEntry ));
  3705. HeapDebugBreak( PreviousLastEntry );
  3706. return NULL;
  3707. }
  3708. }
  3709. }
  3710. //
  3711. // Turn off the last bit on this entry because what's following
  3712. // is no longer uncommitted
  3713. //
  3714. LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
  3715. //
  3716. // Shrink the uncommited range by the size we've committed
  3717. //
  3718. UnCommittedRange->Address += *Size;
  3719. UnCommittedRange->Size -= *Size;
  3720. //
  3721. // Now if the size is zero then we've committed everything that there
  3722. // was in the range. Otherwise make sure the first entry of what
  3723. // we've just committed knows that an uncommitted range follows.
  3724. //
  3725. if (UnCommittedRange->Size == 0) {
  3726. //
  3727. // This uncommitted range is about to vanish. Base on if the
  3728. // range is the last one in the segment then we know how to
  3729. // mark the committed range as being last or not.
  3730. //
  3731. if (UnCommittedRange->Address == (ULONG_PTR)Segment->LastValidEntry) {
  3732. FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  3733. Segment->LastEntryInSegment = FirstEntry;
  3734. } else {
  3735. FirstEntry->Flags = 0;
  3736. Segment->LastEntryInSegment = Segment->FirstEntry;
  3737. }
  3738. //
  3739. // Remove this zero sized range from the uncommitted range
  3740. // list, and update the segment counters
  3741. //
  3742. *pp = UnCommittedRange->Next;
  3743. RtlpDestroyUnCommittedRange( Segment, UnCommittedRange );
  3744. Segment->NumberOfUnCommittedRanges -= 1;
  3745. } else {
  3746. //
  3747. // Otherwise the range is not empty so we know what we committed
  3748. // is immediately followed by an uncommitted range
  3749. //
  3750. FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  3751. Segment->LastEntryInSegment = FirstEntry;
  3752. }
  3753. //
  3754. // Update the fields in the first entry, and optional
  3755. // following entry.
  3756. //
  3757. FirstEntry->SegmentIndex = LastEntry->SegmentIndex;
  3758. FirstEntry->Size = (USHORT)(*Size >> HEAP_GRANULARITY_SHIFT);
  3759. FirstEntry->PreviousSize = LastEntry->Size;
  3760. if (!(FirstEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  3761. (FirstEntry + FirstEntry->Size)->PreviousSize = FirstEntry->Size;
  3762. }
  3763. //
  3764. // Now if we adjusted the largest uncommitted range to zero then
  3765. // we need to go back and find the largest uncommitted range
  3766. // To do that we simply zoom down the uncommitted range list
  3767. // remembering the largest one
  3768. //
  3769. if (Segment->LargestUnCommittedRange == 0) {
  3770. UnCommittedRange = Segment->UnCommittedRanges;
  3771. while (UnCommittedRange != NULL) {
  3772. if (UnCommittedRange->Size >= Segment->LargestUnCommittedRange) {
  3773. Segment->LargestUnCommittedRange = UnCommittedRange->Size;
  3774. }
  3775. UnCommittedRange = UnCommittedRange->Next;
  3776. }
  3777. }
  3778. #ifndef NTOS_KERNEL_RUNTIME
  3779. if(IsHeapLogging( Heap ) ) {
  3780. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  3781. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  3782. USHORT ReqSize = sizeof(HEAP_EVENT_EXPANSION) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  3783. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  3784. if(pEventHeader && pThreadLocalData) {
  3785. SIZE_T UCBytes = 0;
  3786. PHEAP_EVENT_EXPANSION pHeapEvent = (PHEAP_EVENT_EXPANSION)( (SIZE_T)pEventHeader
  3787. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  3788. pEventHeader->Packet.Size = (USHORT) ReqSize;
  3789. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_EXTEND;
  3790. pHeapEvent->HeapHandle = (PVOID)Heap;
  3791. pHeapEvent->CommittedSize = *Size;
  3792. pHeapEvent->Address = (PVOID)FirstEntry;
  3793. pHeapEvent->FreeSpace = Heap->TotalFreeSize;
  3794. pHeapEvent->ReservedSpace = 0;
  3795. pHeapEvent->CommittedSpace = 0;
  3796. pHeapEvent->NoOfUCRs = 0;
  3797. UCBytes = GetUCBytes(Heap, &pHeapEvent->ReservedSpace, &pHeapEvent->NoOfUCRs);
  3798. pHeapEvent->ReservedSpace *= PAGE_SIZE;
  3799. pHeapEvent->CommittedSpace = pHeapEvent->ReservedSpace - UCBytes;
  3800. ReleaseBufferLocation(pThreadLocalData);
  3801. }
  3802. }
  3803. #endif //NTOS_KERNEL_RUNTIME
  3804. //
  3805. // And return the heap entry to our caller
  3806. //
  3807. return (PHEAP_FREE_ENTRY)FirstEntry;
  3808. } else {
  3809. //
  3810. // Otherwise the current uncommited range is too small or
  3811. // doesn't have the right address so go to the next uncommitted
  3812. // range entry
  3813. //
  3814. PreviousUnCommittedRange = UnCommittedRange;
  3815. pp = &UnCommittedRange->Next;
  3816. }
  3817. }
  3818. //
  3819. // At this point we did not find an uncommitted range entry that satisfied
  3820. // our requirements either because of size and/or address. So return null
  3821. // to tell the user we didn't find anything.
  3822. //
  3823. return NULL;
  3824. }
  3825. //
  3826. // Declared in heappriv.h
  3827. //
  3828. BOOLEAN
  3829. RtlpInitializeHeapSegment (
  3830. IN PHEAP Heap,
  3831. IN PHEAP_SEGMENT Segment,
  3832. IN UCHAR SegmentIndex,
  3833. IN ULONG Flags,
  3834. IN PVOID BaseAddress,
  3835. IN PVOID UnCommittedAddress,
  3836. IN PVOID CommitLimitAddress
  3837. )
  3838. /*++
  3839. Routine Description:
  3840. This routines initializes the internal structures for a heap segment.
  3841. The caller supplies the heap and the memory for the segment being
  3842. initialized
  3843. Arguments:
  3844. Heap - Supplies the address of the heap owning this segment
  3845. Segment - Supplies a pointer to the segment being initialized
  3846. SegmentIndex - Supplies the segement index within the heap that this
  3847. new segment is being assigned
  3848. Flags - Supplies flags controlling the initialization of the segment
  3849. Valid flags are:
  3850. HEAP_SEGMENT_USER_ALLOCATED
  3851. BaseAddress - Supplies the base address for the segment
  3852. UnCommittedAddress - Supplies the address where the uncommited range starts
  3853. CommitLimitAddress - Supplies the top address available to the segment
  3854. Return Value:
  3855. BOOLEAN - TRUE if the initialization is successful and FALSE otherwise
  3856. --*/
  3857. {
  3858. NTSTATUS Status;
  3859. PHEAP_ENTRY FirstEntry;
  3860. USHORT PreviousSize, Size;
  3861. ULONG NumberOfPages;
  3862. ULONG NumberOfCommittedPages;
  3863. ULONG NumberOfUnCommittedPages;
  3864. SIZE_T CommitSize;
  3865. ULONG NtGlobalFlag = RtlGetNtGlobalFlags();
  3866. RTL_PAGED_CODE();
  3867. //
  3868. // Compute the total number of pages possible in this segment
  3869. //
  3870. NumberOfPages = (ULONG) (((PCHAR)CommitLimitAddress - (PCHAR)BaseAddress) / PAGE_SIZE);
  3871. //
  3872. // First entry points to the first possible segment entry after
  3873. // the segment header
  3874. //
  3875. FirstEntry = (PHEAP_ENTRY)ROUND_UP_TO_POWER2( Segment + 1,
  3876. HEAP_GRANULARITY );
  3877. //
  3878. // Now if the heap is equal to the base address for the segment which
  3879. // it the case for the segment zero then the previous size is the
  3880. // heap header. Otherwise there isn't a previous entry
  3881. //
  3882. if ((PVOID)Heap == BaseAddress) {
  3883. PreviousSize = Heap->Entry.Size;
  3884. } else {
  3885. PreviousSize = 0;
  3886. }
  3887. //
  3888. // Compute the index size of the segment header
  3889. //
  3890. Size = (USHORT)(((PCHAR)FirstEntry - (PCHAR)Segment) >> HEAP_GRANULARITY_SHIFT);
  3891. //
  3892. // If the first available heap entry is not committed and
  3893. // it is beyond the heap limit then we cannot initialize
  3894. //
  3895. if ((PCHAR)(FirstEntry + 1) >= (PCHAR)UnCommittedAddress) {
  3896. if ((PCHAR)(FirstEntry + 1) >= (PCHAR)CommitLimitAddress) {
  3897. return FALSE;
  3898. }
  3899. //
  3900. // Enough of the segment has not been committed so we
  3901. // will commit enough now to handle the first entry
  3902. //
  3903. CommitSize = (PCHAR)(FirstEntry + 1) - (PCHAR)UnCommittedAddress;
  3904. #ifdef _WIN64
  3905. //
  3906. // This is for Wow64 processes. This is needed to return PAGE_SIZE aligned
  3907. // aligned sizes.
  3908. //
  3909. CommitSize = ROUND_UP_TO_POWER2 (CommitSize, PAGE_SIZE);
  3910. #endif
  3911. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  3912. (PVOID *)&UnCommittedAddress,
  3913. 0,
  3914. &CommitSize,
  3915. MEM_COMMIT,
  3916. PAGE_READWRITE );
  3917. if (!NT_SUCCESS( Status )) {
  3918. return FALSE;
  3919. }
  3920. //
  3921. // Because we had to commit some memory we need to adjust
  3922. // the uncommited address
  3923. //
  3924. UnCommittedAddress = (PVOID)((PCHAR)UnCommittedAddress + CommitSize);
  3925. }
  3926. //
  3927. // At this point we know there is enough memory committed to handle the
  3928. // segment header and one heap entry
  3929. //
  3930. // Now compute the number of uncommited pages and the number of committed
  3931. // pages
  3932. //
  3933. NumberOfUnCommittedPages = (ULONG)(((PCHAR)CommitLimitAddress - (PCHAR)UnCommittedAddress) / PAGE_SIZE);
  3934. NumberOfCommittedPages = NumberOfPages - NumberOfUnCommittedPages;
  3935. //
  3936. // Initialize the heap segment heap entry. We
  3937. // calculated earlier if there was a previous entry
  3938. //
  3939. Segment->Entry.PreviousSize = PreviousSize;
  3940. Segment->Entry.Size = Size;
  3941. Segment->Entry.Flags = HEAP_ENTRY_BUSY;
  3942. Segment->Entry.SegmentIndex = SegmentIndex;
  3943. #if !NTOS_KERNEL_RUNTIME
  3944. //
  3945. // In the non kernel case see if we need to capture the callers stack
  3946. // backtrace
  3947. //
  3948. if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
  3949. Segment->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
  3950. }
  3951. #endif // !NTOS_KERNEL_RUNTIME
  3952. //
  3953. // Now initializes the heap segment
  3954. //
  3955. Segment->Signature = HEAP_SEGMENT_SIGNATURE;
  3956. Segment->Flags = Flags;
  3957. Segment->Heap = Heap;
  3958. Segment->BaseAddress = BaseAddress;
  3959. Segment->FirstEntry = FirstEntry;
  3960. Segment->LastValidEntry = (PHEAP_ENTRY)((PCHAR)BaseAddress + (NumberOfPages * PAGE_SIZE));
  3961. Segment->NumberOfPages = NumberOfPages;
  3962. Segment->NumberOfUnCommittedPages = NumberOfUnCommittedPages;
  3963. //
  3964. // If there are uncommitted pages then we need to insert them
  3965. // into the uncommitted ranges list
  3966. //
  3967. if (NumberOfUnCommittedPages) {
  3968. RtlpInsertUnCommittedPages( Segment,
  3969. (ULONG_PTR)UnCommittedAddress,
  3970. NumberOfUnCommittedPages * PAGE_SIZE );
  3971. //
  3972. // Test if we successfully created the uncommitted range within the segment
  3973. //
  3974. if (Segment->NumberOfUnCommittedRanges == 0) {
  3975. HeapDebugPrint(( "Failed to initialize a new segment (%x)\n", Segment ));
  3976. //
  3977. // We don't need to decommitt the extra memory commited before because
  3978. // the caller for this function will do this for the entire reserved size
  3979. //
  3980. return FALSE;
  3981. }
  3982. }
  3983. //
  3984. // Have the containing heap point to this segment via the specified index
  3985. //
  3986. Heap->Segments[ SegmentIndex ] = Segment;
  3987. if (Heap->LastSegmentIndex < SegmentIndex) {
  3988. Heap->LastSegmentIndex = SegmentIndex;
  3989. }
  3990. //
  3991. // Initialize the first free heap entry after the heap segment header and
  3992. // put it in the free list. This first entry will be for whatever is left
  3993. // of the committed range
  3994. //
  3995. PreviousSize = Segment->Entry.Size;
  3996. FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  3997. Segment->LastEntryInSegment = FirstEntry;
  3998. FirstEntry->PreviousSize = PreviousSize;
  3999. FirstEntry->SegmentIndex = SegmentIndex;
  4000. RtlpInsertFreeBlock( Heap,
  4001. (PHEAP_FREE_ENTRY)FirstEntry,
  4002. (PHEAP_ENTRY)UnCommittedAddress - FirstEntry);
  4003. //
  4004. // And return to our caller
  4005. //
  4006. return TRUE;
  4007. }
  4008. //
  4009. // Local Support Routine
  4010. //
  4011. NTSTATUS
  4012. RtlpDestroyHeapSegment (
  4013. IN PHEAP_SEGMENT Segment
  4014. )
  4015. /*++
  4016. Routine Description:
  4017. This routine removes an existing heap segment. After the call it
  4018. is as if the segment never existed
  4019. Arguments:
  4020. Segment - Supplies a pointer to the heap segment being destroyed
  4021. Return Value:
  4022. NTSTATUS - An appropriate status value
  4023. --*/
  4024. {
  4025. PVOID BaseAddress;
  4026. SIZE_T BytesToFree;
  4027. RTL_PAGED_CODE();
  4028. //
  4029. // We actually only have work to do if the segment is not
  4030. // user allocated. If the segment is user allocated then
  4031. // we'll assume knows how to get rid of the memory
  4032. //
  4033. if (!(Segment->Flags & HEAP_SEGMENT_USER_ALLOCATED)) {
  4034. BaseAddress = Segment->BaseAddress;
  4035. BytesToFree = 0;
  4036. //
  4037. // Free all the virtual memory for the segment and return
  4038. // to our caller.
  4039. //
  4040. return RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  4041. (PVOID *)&BaseAddress,
  4042. &BytesToFree,
  4043. MEM_RELEASE );
  4044. } else {
  4045. //
  4046. // User allocated segments are a noop
  4047. //
  4048. return STATUS_SUCCESS;
  4049. }
  4050. }
  4051. //
  4052. // Local Support Routine
  4053. //
  4054. PHEAP_FREE_ENTRY
  4055. RtlpExtendHeap (
  4056. IN PHEAP Heap,
  4057. IN SIZE_T AllocationSize
  4058. )
  4059. /*++
  4060. Routine Description:
  4061. This routine is used to extend the amount of committed memory in a heap
  4062. Arguments:
  4063. Heap - Supplies the heap being modified
  4064. AllocationSize - Supplies the size, in bytes, that we need to extend the
  4065. heap
  4066. Return Value:
  4067. PHEAP_FREE_ENTRY - Returns a pointer to the newly created heap entry
  4068. of the specified size, or NULL if we weren't able to extend the heap
  4069. --*/
  4070. {
  4071. NTSTATUS Status;
  4072. PHEAP_SEGMENT Segment;
  4073. PHEAP_FREE_ENTRY FreeBlock;
  4074. UCHAR SegmentIndex, EmptySegmentIndex;
  4075. ULONG NumberOfPages;
  4076. SIZE_T CommitSize;
  4077. SIZE_T ReserveSize;
  4078. SIZE_T FreeSize;
  4079. RTL_PAGED_CODE();
  4080. #ifndef NTOS_KERNEL_RUNTIME
  4081. if (Heap->LargeBlocksIndex) {
  4082. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  4083. if (HeapIndex->LargeBlocksCacheMaxDepth < RtlpLargeListDepthLimit) {
  4084. HeapIndex->LargeBlocksCacheMaxDepth += 1;
  4085. }
  4086. HeapIndex->CacheStats.Committs += 1;
  4087. }
  4088. #endif // NTOS_KERNEL_RUNTIME
  4089. //
  4090. // Compute the number of pages need to hold this extension
  4091. // And then compute the real free, still in bytes, based on
  4092. // the page count
  4093. //
  4094. NumberOfPages = (ULONG) ((AllocationSize + PAGE_SIZE - 1) / PAGE_SIZE);
  4095. FreeSize = NumberOfPages * PAGE_SIZE;
  4096. //
  4097. // For every segment we're either going to look for an existing
  4098. // heap segment that we can get some pages out of or we will
  4099. // identify a free heap segment index where we'll try and create a new
  4100. // segment
  4101. //
  4102. EmptySegmentIndex = (UCHAR)(Heap->LastSegmentIndex + 1);
  4103. for (SegmentIndex=0; SegmentIndex <= Heap->LastSegmentIndex; SegmentIndex++) {
  4104. #ifndef NTOS_KERNEL_RUNTIME
  4105. if ((RtlpGetLowFragHeap(Heap) != NULL)
  4106. &&
  4107. (AllocationSize > HEAP_LARGEST_LFH_BLOCK)) {
  4108. //
  4109. // Search backward for large blocks. This will group the
  4110. // large allocations into upper segments, and small allocations
  4111. // into lower index segments. It helps the fragmentation
  4112. //
  4113. Segment = Heap->Segments[ Heap->LastSegmentIndex - SegmentIndex ];
  4114. } else {
  4115. Segment = Heap->Segments[ SegmentIndex ];
  4116. }
  4117. #else // NTOS_KERNEL_RUNTIME
  4118. Segment = Heap->Segments[ SegmentIndex ];
  4119. #endif // NTOS_KERNEL_RUNTIME
  4120. //
  4121. // If the segment exists and number of uncommitted pages will
  4122. // satisfy our request and the largest uncommitted range will
  4123. // also satisfy our request then we'll try and segment
  4124. //
  4125. // Note that this second test seems unnecessary given that
  4126. // the largest uncommitted range is also being tested
  4127. //
  4128. if ((Segment) &&
  4129. (NumberOfPages <= Segment->NumberOfUnCommittedPages) &&
  4130. (FreeSize <= Segment->LargestUnCommittedRange)) {
  4131. //
  4132. // Looks like a good segment so try and commit the
  4133. // amount we need
  4134. //
  4135. FreeBlock = RtlpFindAndCommitPages( Heap,
  4136. Segment,
  4137. &FreeSize,
  4138. NULL );
  4139. //
  4140. // If we were successful the we will coalesce it with adjacent
  4141. // free blocks and put it in the free list then return the
  4142. // the free block
  4143. //
  4144. if (FreeBlock != NULL) {
  4145. //
  4146. // RtlpCoalesceFreeBlocks needs the free size in heap units.
  4147. // We'll shift with the granularity before calling the coalesce.
  4148. //
  4149. FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
  4150. FreeBlock = RtlpCoalesceFreeBlocks( Heap, FreeBlock, &FreeSize, FALSE );
  4151. RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
  4152. return FreeBlock;
  4153. }
  4154. }
  4155. }
  4156. //
  4157. // At this point we weren't able to get the memory from an existing
  4158. // heap segment so now check if we found an unused segment index
  4159. // and if we're allowed to grow the heap.
  4160. //
  4161. if ((EmptySegmentIndex != HEAP_MAXIMUM_SEGMENTS) &&
  4162. (Heap->Flags & HEAP_GROWABLE)) {
  4163. Segment = NULL;
  4164. //
  4165. // Calculate a reserve size for the new segment, we might
  4166. // need to fudge it up if the allocation size we're going for
  4167. // right now is already beyond the default reserve size
  4168. //
  4169. if ((AllocationSize + PAGE_SIZE) > Heap->SegmentReserve) {
  4170. ReserveSize = AllocationSize + PAGE_SIZE;
  4171. } else {
  4172. ReserveSize = Heap->SegmentReserve;
  4173. }
  4174. #if defined(_WIN64)
  4175. //
  4176. // Limit the size of the segments to 2 GBytes
  4177. //
  4178. #define HEAP_MAX_SEGMENT_SIZE 0x80000000
  4179. if (ReserveSize >= HEAP_MAX_SEGMENT_SIZE) {
  4180. ReserveSize = HEAP_MAX_SEGMENT_SIZE;
  4181. }
  4182. #endif
  4183. //
  4184. // Try and reserve some vm
  4185. //
  4186. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  4187. (PVOID *)&Segment,
  4188. 0,
  4189. &ReserveSize,
  4190. MEM_RESERVE,
  4191. PAGE_READWRITE );
  4192. //
  4193. // If we get back status no memory then we should trim back the
  4194. // request to something reasonable and try again. We'll half
  4195. // the amount until we it either succeeds or until we reach
  4196. // the allocation size. In the latter case we are really
  4197. // out of memory.
  4198. //
  4199. while ((!NT_SUCCESS( Status )) && (ReserveSize != (AllocationSize + PAGE_SIZE))) {
  4200. ReserveSize = ReserveSize / 2;
  4201. if( ReserveSize < (AllocationSize + PAGE_SIZE) ) {
  4202. ReserveSize = (AllocationSize + PAGE_SIZE);
  4203. }
  4204. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  4205. (PVOID *)&Segment,
  4206. 0,
  4207. &ReserveSize,
  4208. MEM_RESERVE,
  4209. PAGE_READWRITE );
  4210. }
  4211. if (NT_SUCCESS( Status )) {
  4212. //
  4213. // Adjust the heap state information
  4214. //
  4215. Heap->SegmentReserve += ReserveSize;
  4216. //
  4217. // Compute the commit size to be either the default, or if
  4218. // that's not big enough then make it big enough to handle
  4219. // this current request
  4220. //
  4221. if ((AllocationSize + PAGE_SIZE) > Heap->SegmentCommit) {
  4222. CommitSize = AllocationSize + PAGE_SIZE;
  4223. } else {
  4224. CommitSize = Heap->SegmentCommit;
  4225. }
  4226. #ifdef _WIN64
  4227. //
  4228. // This is for Wow64 processes. This is needed to return PAGE_SIZE aligned
  4229. // aligned sizes.
  4230. //
  4231. CommitSize = ROUND_UP_TO_POWER2 (CommitSize, PAGE_SIZE);
  4232. #endif
  4233. //
  4234. // Try and commit the memory
  4235. //
  4236. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  4237. (PVOID *)&Segment,
  4238. 0,
  4239. &CommitSize,
  4240. MEM_COMMIT,
  4241. PAGE_READWRITE );
  4242. //
  4243. // If the commit is successful but we were not able to
  4244. // initialize the heap segment then still make the status
  4245. // and error value
  4246. //
  4247. if (NT_SUCCESS( Status ) &&
  4248. !RtlpInitializeHeapSegment( Heap,
  4249. Segment,
  4250. EmptySegmentIndex,
  4251. 0,
  4252. Segment,
  4253. (PCHAR)Segment + CommitSize,
  4254. (PCHAR)Segment + ReserveSize)) {
  4255. Status = STATUS_NO_MEMORY;
  4256. }
  4257. //
  4258. // If we've been successful so far then we're done and we
  4259. // can return the first entry in the segment to our caller
  4260. //
  4261. if (NT_SUCCESS(Status)) {
  4262. #ifndef NTOS_KERNEL_RUNTIME
  4263. if(IsHeapLogging( Heap ) ) {
  4264. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  4265. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  4266. USHORT ReqSize = sizeof(HEAP_EVENT_EXPANSION) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  4267. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  4268. if(pEventHeader && pThreadLocalData) {
  4269. SIZE_T UCBytes = 0;
  4270. PHEAP_EVENT_EXPANSION pHeapEvent = (PHEAP_EVENT_EXPANSION)( (SIZE_T)pEventHeader
  4271. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  4272. pEventHeader->Packet.Size = (USHORT) ReqSize;
  4273. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_EXTEND;
  4274. pHeapEvent->HeapHandle = (PVOID)Heap;
  4275. pHeapEvent->CommittedSize = CommitSize;
  4276. pHeapEvent->Address = (PVOID)Segment->FirstEntry;
  4277. pHeapEvent->FreeSpace = Heap->TotalFreeSize;
  4278. pHeapEvent->ReservedSpace = 0;
  4279. pHeapEvent->CommittedSpace = 0;
  4280. pHeapEvent->NoOfUCRs = 0;
  4281. UCBytes = GetUCBytes(Heap, &pHeapEvent->ReservedSpace, &pHeapEvent->NoOfUCRs);
  4282. pHeapEvent->ReservedSpace *= PAGE_SIZE;
  4283. pHeapEvent->CommittedSpace = pHeapEvent->ReservedSpace - UCBytes;
  4284. ReleaseBufferLocation(pThreadLocalData);
  4285. }
  4286. }
  4287. #endif //NTOS_KERNEL_RUNTIME
  4288. return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
  4289. }
  4290. //
  4291. // Otherwise either the commit or heap segment initialization failed
  4292. // so we'll release the memory which will also decommit it if necessary
  4293. //
  4294. RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  4295. (PVOID *)&Segment,
  4296. &ReserveSize,
  4297. MEM_RELEASE );
  4298. }
  4299. }
  4300. #ifndef NTOS_KERNEL_RUNTIME
  4301. //
  4302. // In the non kernel case we disabled coalescing on free then what we'll
  4303. // do as a last resort is coalesce the heap and see if a block comes out
  4304. // that we can use
  4305. //
  4306. if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE) {
  4307. FreeBlock = RtlpCoalesceHeap( Heap );
  4308. if ((FreeBlock != NULL) && (FreeBlock->Size >= AllocationSize)) {
  4309. return FreeBlock;
  4310. }
  4311. }
  4312. #endif // NTOS_KERNEL_RUNTIME
  4313. //
  4314. // Either the heap cannot grow or we out of resources of some type
  4315. // so we're going to return null
  4316. //
  4317. return NULL;
  4318. }
  4319. //
  4320. // Declared in heappriv.h
  4321. //
  4322. PHEAP_FREE_ENTRY
  4323. RtlpCoalesceFreeBlocks (
  4324. IN PHEAP Heap,
  4325. IN PHEAP_FREE_ENTRY FreeBlock,
  4326. IN OUT PSIZE_T FreeSize,
  4327. IN BOOLEAN RemoveFromFreeList
  4328. )
  4329. /*++
  4330. Routine Description:
  4331. This routine coalesces the free block together.
  4332. Arguments:
  4333. Heap - Supplies a pointer to the heap being manipulated
  4334. FreeBlock - Supplies a pointer to the free block that we want coalesced
  4335. FreeSize - Supplies the size, in heap units, of the free block. On return it
  4336. contains the size, in bytes, of the of the newly coalesced free block
  4337. RemoveFromFreeList - Indicates if the input free block is already on a
  4338. free list and needs to be removed to before coalescing
  4339. Return Value:
  4340. PHEAP_FREE_ENTRY - returns a pointer to the newly coalesced free block
  4341. --*/
  4342. {
  4343. PHEAP_FREE_ENTRY FreeBlock1, NextFreeBlock;
  4344. RTL_PAGED_CODE();
  4345. //
  4346. // Point to the preceding block
  4347. //
  4348. FreeBlock1 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock - FreeBlock->PreviousSize);
  4349. //
  4350. // Check if there is a preceding block, and if it is free, and the two sizes
  4351. // put together will still fit on a free lists.
  4352. //
  4353. if ((FreeBlock1 != FreeBlock) &&
  4354. !(FreeBlock1->Flags & HEAP_ENTRY_BUSY) &&
  4355. ((*FreeSize + FreeBlock1->Size) <= HEAP_MAXIMUM_BLOCK_SIZE)) {
  4356. //
  4357. // We are going to merge ourselves with the preceding block
  4358. //
  4359. HEAPASSERT(FreeBlock->PreviousSize == FreeBlock1->Size);
  4360. //
  4361. // Check if we need to remove the input block from the free list
  4362. //
  4363. if (RemoveFromFreeList) {
  4364. RtlpRemoveFreeBlock( Heap, FreeBlock );
  4365. Heap->TotalFreeSize -= FreeBlock->Size;
  4366. //
  4367. // We're removed so we don't have to do it again
  4368. //
  4369. RemoveFromFreeList = FALSE;
  4370. }
  4371. //
  4372. // Remove the preceding block from its free list
  4373. //
  4374. RtlpRemoveFreeBlock( Heap, FreeBlock1 );
  4375. //
  4376. // Copy over the last entry flag if necessary from what we're freeing
  4377. // to the preceding block
  4378. //
  4379. FreeBlock1->Flags = FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY;
  4380. if( FreeBlock1->Flags & HEAP_ENTRY_LAST_ENTRY ) {
  4381. PHEAP_SEGMENT Segment;
  4382. Segment = Heap->Segments[FreeBlock1->SegmentIndex];
  4383. Segment->LastEntryInSegment = (PHEAP_ENTRY)FreeBlock1;
  4384. }
  4385. //
  4386. // Point to the preceding block, and adjust the sizes for the
  4387. // new free block. It is the total of both blocks.
  4388. //
  4389. FreeBlock = FreeBlock1;
  4390. *FreeSize += FreeBlock1->Size;
  4391. Heap->TotalFreeSize -= FreeBlock1->Size;
  4392. FreeBlock->Size = (USHORT)*FreeSize;
  4393. //
  4394. // Check if we need to update the previous size of the next
  4395. // entry
  4396. //
  4397. if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  4398. ((PHEAP_ENTRY)FreeBlock + *FreeSize)->PreviousSize = (USHORT)*FreeSize;
  4399. }
  4400. }
  4401. //
  4402. // Check if there is a following block.
  4403. //
  4404. if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  4405. //
  4406. // There is a following block so now get a pointer to it
  4407. // and check if it is free and if putting the two blocks together
  4408. // still fits on a free list
  4409. //
  4410. NextFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + *FreeSize);
  4411. if (!(NextFreeBlock->Flags & HEAP_ENTRY_BUSY) &&
  4412. ((*FreeSize + NextFreeBlock->Size) <= HEAP_MAXIMUM_BLOCK_SIZE)) {
  4413. //
  4414. // We are going to merge ourselves with the following block
  4415. //
  4416. HEAPASSERT(*FreeSize == NextFreeBlock->PreviousSize);
  4417. //
  4418. // Check if we need to remove the input block from the free list
  4419. //
  4420. if (RemoveFromFreeList) {
  4421. RtlpRemoveFreeBlock( Heap, FreeBlock );
  4422. Heap->TotalFreeSize -= FreeBlock->Size;
  4423. }
  4424. //
  4425. // Copy up the last entry flag if necessary from the following
  4426. // block to our input block
  4427. //
  4428. FreeBlock->Flags = NextFreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY;
  4429. if( FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY ) {
  4430. PHEAP_SEGMENT Segment;
  4431. Segment = Heap->Segments[FreeBlock->SegmentIndex];
  4432. Segment->LastEntryInSegment = (PHEAP_ENTRY)FreeBlock;
  4433. }
  4434. //
  4435. // Remove the following block from its free list
  4436. //
  4437. RtlpRemoveFreeBlock( Heap, NextFreeBlock );
  4438. //
  4439. // Adjust the size for the newly combined block
  4440. //
  4441. *FreeSize += NextFreeBlock->Size;
  4442. Heap->TotalFreeSize -= NextFreeBlock->Size;
  4443. FreeBlock->Size = (USHORT)*FreeSize;
  4444. //
  4445. // Check if we need to update the previous size of the next block
  4446. //
  4447. if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  4448. ((PHEAP_ENTRY)FreeBlock + *FreeSize)->PreviousSize = (USHORT)*FreeSize;
  4449. }
  4450. }
  4451. }
  4452. //
  4453. // And return the free block to our caller
  4454. //
  4455. return FreeBlock;
  4456. }
  4457. //
  4458. // Declared in heappriv.h
  4459. //
  4460. VOID
  4461. RtlpDeCommitFreeBlock (
  4462. IN PHEAP Heap,
  4463. IN PHEAP_FREE_ENTRY FreeBlock,
  4464. IN SIZE_T FreeSize
  4465. )
  4466. /*++
  4467. Routine Description:
  4468. This routine takes a free block and decommits it. This is usually called
  4469. because the block is beyond the decommit threshold
  4470. Arguments:
  4471. Heap - Supplies a pointer to the heap being manipulated
  4472. FreeBlock - Supplies a pointer to the block being decommitted
  4473. FreeSize - Supplies the size, in heap units, of the free block being decommitted
  4474. Return Value:
  4475. None.
  4476. --*/
  4477. {
  4478. NTSTATUS Status;
  4479. ULONG_PTR DeCommitAddress;
  4480. SIZE_T DeCommitSize;
  4481. USHORT LeadingFreeSize, TrailingFreeSize;
  4482. PHEAP_SEGMENT Segment;
  4483. PHEAP_FREE_ENTRY LeadingFreeBlock, TrailingFreeBlock;
  4484. PHEAP_ENTRY LeadingBusyBlock, TrailingBusyBlock;
  4485. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
  4486. PHEAP_FREE_ENTRY LeadingBlockToDecommit = NULL, TrailingBlockToDecommit = NULL;
  4487. RTL_PAGED_CODE();
  4488. //
  4489. // If the heap has a user specified decommit routine then we won't really
  4490. // decommit anything instead we'll call a worker routine to chop it up
  4491. // into pieces that will fit on the free lists
  4492. //
  4493. if (Heap->CommitRoutine != NULL) {
  4494. RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
  4495. return;
  4496. }
  4497. //
  4498. // Get a pointer to the owning segment
  4499. //
  4500. Segment = Heap->Segments[ FreeBlock->SegmentIndex ];
  4501. //
  4502. // The leading busy block identifies the preceding in use block before
  4503. // what we are trying to decommit. It is only used if what we are trying
  4504. // decommit is right on a page boundary and then it is the block right
  4505. // before us if it exists.
  4506. //
  4507. // The leading free block is used to identify whatever space is needed
  4508. // to round up the callers specified address to a page address. If the
  4509. // caller already gave us a page aligned address then the free block
  4510. // address is identical to what the caller supplied.
  4511. //
  4512. LeadingBusyBlock = NULL;
  4513. LeadingFreeBlock = FreeBlock;
  4514. //
  4515. // Make sure the block we are trying to decommit start on the next full
  4516. // page boundary. The leading free size is the size of whatever it takes
  4517. // to round up the free block to the next page specified in units of
  4518. // heap entries.
  4519. //
  4520. DeCommitAddress = ROUND_UP_TO_POWER2( LeadingFreeBlock, PAGE_SIZE );
  4521. LeadingFreeSize = (USHORT)((PHEAP_ENTRY)DeCommitAddress - (PHEAP_ENTRY)LeadingFreeBlock);
  4522. //
  4523. // If we leading free size only has space for one heap entry then we'll
  4524. // bump it up to include the next page, because we don't want to leave
  4525. // anything that small laying around. Otherwise if we have a preceding
  4526. // block and the leading free size is zero then identify the preceding
  4527. // block as the leading busy block
  4528. //
  4529. if (LeadingFreeSize == 1) {
  4530. DeCommitAddress += PAGE_SIZE;
  4531. LeadingFreeSize += PAGE_SIZE >> HEAP_GRANULARITY_SHIFT;
  4532. } else if (LeadingFreeBlock->PreviousSize != 0) {
  4533. if (DeCommitAddress == (ULONG_PTR)LeadingFreeBlock) {
  4534. LeadingBusyBlock = (PHEAP_ENTRY)LeadingFreeBlock - LeadingFreeBlock->PreviousSize;
  4535. }
  4536. }
  4537. //
  4538. // The trailing busy block identifies the block immediately after the one
  4539. // we are trying to decommit provided what we are decommitting ends right
  4540. // on a page boundary otherwise the trailing busy block stays null and
  4541. // the trailing free block value is used.
  4542. //
  4543. TrailingBusyBlock = NULL;
  4544. TrailingFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + FreeSize);
  4545. //
  4546. // Make sure the block we are trying to decommit ends on a page boundary.
  4547. //
  4548. // And compute how many heap entries we had to backup to make it land on a
  4549. // page boundary.
  4550. //
  4551. DeCommitSize = ROUND_DOWN_TO_POWER2( (ULONG_PTR)TrailingFreeBlock, PAGE_SIZE );
  4552. TrailingFreeSize = (USHORT)((PHEAP_ENTRY)TrailingFreeBlock - (PHEAP_ENTRY)DeCommitSize);
  4553. //
  4554. // If the trailing free size is exactly one heap in size then we will
  4555. // nibble off a bit more from the decommit size because free block of
  4556. // exactly one heap entry in size are useless. Otherwise if we actually
  4557. // ended on a page boundary and there is a block after us then indicate
  4558. // that we have a trailing busy block
  4559. //
  4560. if (TrailingFreeSize == (sizeof( HEAP_ENTRY ) >> HEAP_GRANULARITY_SHIFT)) {
  4561. DeCommitSize -= PAGE_SIZE;
  4562. TrailingFreeSize += PAGE_SIZE >> HEAP_GRANULARITY_SHIFT;
  4563. } else if ((TrailingFreeSize == 0) && !(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  4564. TrailingBusyBlock = (PHEAP_ENTRY)TrailingFreeBlock;
  4565. }
  4566. //
  4567. // Now adjust the trailing free block to compensate for the trailing free size
  4568. // we just computed.
  4569. //
  4570. TrailingFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)TrailingFreeBlock - TrailingFreeSize);
  4571. //
  4572. // Right now DeCommit size is really a pointer. If it points at is beyond
  4573. // the decommit address then make the size really be just the byte count
  4574. // to decommit. Otherwise the decommit size is zero.
  4575. //
  4576. if (DeCommitSize > DeCommitAddress) {
  4577. DeCommitSize -= DeCommitAddress;
  4578. } else {
  4579. DeCommitSize = 0;
  4580. }
  4581. //
  4582. // Now check if we still have something to decommit
  4583. //
  4584. if (DeCommitSize != 0) {
  4585. #ifndef NTOS_KERNEL_RUNTIME
  4586. //
  4587. // We do not try to push the large block to the
  4588. // cache if it already has uncommited ranges arround
  4589. //
  4590. if ( (FreeBlock->PreviousSize != 0)
  4591. &&
  4592. !(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY) ) {
  4593. if (Heap->LargeBlocksIndex == NULL) {
  4594. Heap->u2.DecommitCount += 1;
  4595. if ( (Heap->u2.DecommitCount == HEAP_ACTIVATE_CACHE_THRESHOLD) &&
  4596. (Heap->Flags & HEAP_GROWABLE) &&
  4597. !(RtlpDisableHeapLookaside & HEAP_COMPAT_DISABLE_LARGECACHE) ) {
  4598. RtlpInitializeListIndex( Heap );
  4599. }
  4600. } else {
  4601. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  4602. //
  4603. // Check if the cache is locked for flushing
  4604. //
  4605. if ((HeapIndex->LargeBlocksCacheSequence != 0)
  4606. &&
  4607. ( (LeadingFreeBlock->PreviousSize != 0)
  4608. ||
  4609. (TrailingFreeSize != 0) ) ) {
  4610. if (HeapIndex->LargeBlocksCacheDepth < HeapIndex->LargeBlocksCacheMaxDepth) {
  4611. //
  4612. // There is nothing left to decommit to take our leading free block
  4613. // and put it on a free list
  4614. //
  4615. RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
  4616. RtlpCheckLargeCache(Heap);
  4617. return;
  4618. //
  4619. // Check whether the block being deleted is the only one
  4620. // between two uncommitted ranges. If no, we'll decommit the largest block from the list
  4621. //
  4622. } else {
  4623. PLIST_ENTRY Head, Next;
  4624. PHEAP_FREE_ENTRY LargestFreeBlock;
  4625. //
  4626. // we have too many blocks outside. We need to decommit one.
  4627. // To reduce the virtual address fragmentation we nned to decommit the
  4628. // largest block available
  4629. //
  4630. Head = &Heap->FreeLists[ 0 ];
  4631. Next = Head->Blink;
  4632. if (Head != Next) {
  4633. //
  4634. // Lock the cache operations
  4635. //
  4636. LargestFreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  4637. //
  4638. // Even if the we found a larger block in the list, because of a wrong
  4639. // alignment it can produce less uncommitted space. We'll try to free the larges one instead
  4640. // the current block only if the size is significant bigger (+ one page)
  4641. //
  4642. if (LargestFreeBlock->Size > (FreeSize + (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT))) {
  4643. //
  4644. // If we have a larger block into the list
  4645. // we'll insert this one into the list and we'll decommitt
  4646. // the largest one
  4647. //
  4648. RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
  4649. RtlpFlushLargestCacheBlock(Heap);
  4650. RtlpCheckLargeCache(Heap);
  4651. return;
  4652. }
  4653. }
  4654. }
  4655. }
  4656. HeapIndex->CacheStats.Decommitts += 1;
  4657. // HeapDebugPrint(("Decommitting size %ld\n", DeCommitSize));
  4658. }
  4659. }
  4660. #endif // NTOS_KERNEL_RUNTIME
  4661. //
  4662. // Before freeing the memory to MM we have to be sure we can create
  4663. // a PHEAP_UNCOMMMTTED_RANGE later. So we do it right now
  4664. //
  4665. UnCommittedRange = RtlpCreateUnCommittedRange(Segment);
  4666. if (UnCommittedRange == NULL) {
  4667. HeapDebugPrint(( "Failing creating uncommitted range (%x for %x)\n", DeCommitAddress, DeCommitSize ));
  4668. //
  4669. // We weren't successful in the decommit so now simply
  4670. // add the leading free block to the free list
  4671. //
  4672. RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
  4673. return;
  4674. }
  4675. //
  4676. // Decommit the memory
  4677. //
  4678. Status = RtlpHeapFreeVirtualMemory( NtCurrentProcess(),
  4679. (PVOID *)&DeCommitAddress,
  4680. &DeCommitSize,
  4681. MEM_DECOMMIT );
  4682. //
  4683. // Push back the UnCommittedRange structure. Now the insert cannot fail
  4684. //
  4685. RtlpDestroyUnCommittedRange( Segment, UnCommittedRange );
  4686. if (NT_SUCCESS( Status )) {
  4687. //
  4688. // Insert information regarding the pages we just decommitted
  4689. // to the lsit of uncommited pages in the segment
  4690. //
  4691. RtlpInsertUnCommittedPages( Segment,
  4692. DeCommitAddress,
  4693. DeCommitSize );
  4694. //
  4695. // Adjust the segments count of uncommitted pages
  4696. //
  4697. Segment->NumberOfUnCommittedPages += (ULONG)(DeCommitSize / PAGE_SIZE);
  4698. //
  4699. // If we have a leading free block then mark its proper state
  4700. // update the heap, and put it on the free list
  4701. //
  4702. if (LeadingFreeSize != 0) {
  4703. SIZE_T TempSize;
  4704. LeadingFreeBlock->Flags = HEAP_ENTRY_LAST_ENTRY;
  4705. TempSize = LeadingFreeBlock->Size = LeadingFreeSize;
  4706. Segment->LastEntryInSegment = (PHEAP_ENTRY)LeadingFreeBlock;
  4707. LeadingFreeBlock = RtlpCoalesceFreeBlocks( Heap,
  4708. LeadingFreeBlock,
  4709. &TempSize,
  4710. FALSE );
  4711. if (LeadingFreeBlock->Size < Heap->DeCommitFreeBlockThreshold) {
  4712. Heap->TotalFreeSize += LeadingFreeBlock->Size;
  4713. RtlpInsertFreeBlockDirect( Heap, LeadingFreeBlock, LeadingFreeBlock->Size );
  4714. } else {
  4715. LeadingBlockToDecommit = LeadingFreeBlock;
  4716. }
  4717. //
  4718. // Otherwise if we actually have a leading busy block then
  4719. // make sure the busy block knows we're uncommitted
  4720. //
  4721. } else if (LeadingBusyBlock != NULL) {
  4722. LeadingBusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
  4723. Segment->LastEntryInSegment = LeadingBusyBlock;
  4724. } else if ((Segment->LastEntryInSegment >= (PHEAP_ENTRY)DeCommitAddress)
  4725. &&
  4726. ((PCHAR)Segment->LastEntryInSegment < ((PCHAR)DeCommitAddress + DeCommitSize))) {
  4727. Segment->LastEntryInSegment = Segment->FirstEntry;
  4728. }
  4729. //
  4730. // If there is a trailing free block then sets its state,
  4731. // update the heap, and insert it on a free list
  4732. //
  4733. if (TrailingFreeSize != 0) {
  4734. SIZE_T TempSize;
  4735. TrailingFreeBlock->PreviousSize = 0;
  4736. TrailingFreeBlock->SegmentIndex = Segment->Entry.SegmentIndex;
  4737. TrailingFreeBlock->Flags = 0;
  4738. TempSize = TrailingFreeBlock->Size = TrailingFreeSize;
  4739. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)TrailingFreeBlock + TrailingFreeSize))->PreviousSize = (USHORT)TrailingFreeSize;
  4740. TrailingFreeBlock = RtlpCoalesceFreeBlocks( Heap,
  4741. TrailingFreeBlock,
  4742. &TempSize,
  4743. FALSE );
  4744. if (TrailingFreeBlock->Size < Heap->DeCommitFreeBlockThreshold) {
  4745. RtlpInsertFreeBlockDirect( Heap, TrailingFreeBlock, TrailingFreeBlock->Size );
  4746. Heap->TotalFreeSize += TrailingFreeBlock->Size;
  4747. } else {
  4748. TrailingBlockToDecommit = TrailingFreeBlock;
  4749. }
  4750. //
  4751. // Otherwise if we actually have a succeeding block then
  4752. // make it know we are uncommitted
  4753. //
  4754. } else if (TrailingBusyBlock != NULL) {
  4755. TrailingBusyBlock->PreviousSize = 0;
  4756. }
  4757. #ifndef NTOS_KERNEL_RUNTIME
  4758. if( IsHeapLogging( Heap ) ) {
  4759. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  4760. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  4761. USHORT ReqSize = sizeof(HEAP_EVENT_CONTRACTION) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  4762. AcquireBufferLocation(&pEventHeader, &pThreadLocalData,&ReqSize);
  4763. if(pEventHeader && pThreadLocalData) {
  4764. SIZE_T UCBytes = 0;
  4765. PHEAP_EVENT_CONTRACTION pHeapEvent = (PHEAP_EVENT_CONTRACTION)( (SIZE_T)pEventHeader
  4766. + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  4767. pEventHeader->Packet.Size = (USHORT) ReqSize;
  4768. pEventHeader->Packet.HookId = PERFINFO_LOG_TYPE_HEAP_CONTRACT;
  4769. pHeapEvent->HeapHandle = (PVOID)Heap;
  4770. pHeapEvent->DeCommitAddress = (PVOID)DeCommitAddress;
  4771. pHeapEvent->DeCommitSize = DeCommitSize;
  4772. pHeapEvent->FreeSpace = Heap->TotalFreeSize;
  4773. pHeapEvent->ReservedSpace = 0;
  4774. pHeapEvent->CommittedSpace = 0;
  4775. pHeapEvent->NoOfUCRs = 0;
  4776. UCBytes = GetUCBytes(Heap, &pHeapEvent->ReservedSpace, &pHeapEvent->NoOfUCRs);
  4777. pHeapEvent->ReservedSpace *= PAGE_SIZE;
  4778. pHeapEvent->CommittedSpace = pHeapEvent->ReservedSpace - UCBytes;
  4779. ReleaseBufferLocation(pThreadLocalData);
  4780. }
  4781. }
  4782. #endif // NTOS_KERNEL_RUNTIME
  4783. } else {
  4784. //
  4785. // We weren't successful in the decommit so now simply
  4786. // add the leading free block to the free list
  4787. //
  4788. RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
  4789. }
  4790. } else {
  4791. //
  4792. // There is nothing left to decommit to take our leading free block
  4793. // and put it on a free list
  4794. //
  4795. RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
  4796. }
  4797. #ifndef NTOS_KERNEL_RUNTIME
  4798. if ( (LeadingBlockToDecommit != NULL)
  4799. ||
  4800. (TrailingBlockToDecommit != NULL)){
  4801. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  4802. LONG PreviousSequence = 0;
  4803. if (HeapIndex) {
  4804. PreviousSequence = HeapIndex->LargeBlocksCacheSequence;
  4805. //
  4806. // Lock the cache for the next two decommits
  4807. //
  4808. HeapIndex->LargeBlocksCacheSequence = 0;
  4809. }
  4810. #endif // NTOS_KERNEL_RUNTIME
  4811. if (LeadingBlockToDecommit) {
  4812. RtlpDeCommitFreeBlock( Heap,
  4813. LeadingBlockToDecommit,
  4814. LeadingBlockToDecommit->Size
  4815. );
  4816. }
  4817. if (TrailingBlockToDecommit) {
  4818. RtlpDeCommitFreeBlock( Heap,
  4819. TrailingBlockToDecommit,
  4820. TrailingBlockToDecommit->Size
  4821. );
  4822. }
  4823. #ifndef NTOS_KERNEL_RUNTIME
  4824. if (HeapIndex) {
  4825. //
  4826. // Unlock the large block cache
  4827. //
  4828. HeapIndex->LargeBlocksCacheSequence = PreviousSequence;
  4829. }
  4830. }
  4831. //
  4832. // At this point the free block to decommit is inserted
  4833. // into the free lists. So it's safe now to check the large
  4834. // block list and flush what is not longer necessary.
  4835. //
  4836. RtlpCheckLargeCache(Heap);
  4837. #endif // NTOS_KERNEL_RUNTIME
  4838. //
  4839. // And return to our caller
  4840. //
  4841. return;
  4842. }
  4843. //
  4844. // Declared in heappriv.h
  4845. //
  4846. VOID
  4847. RtlpInsertFreeBlock (
  4848. IN PHEAP Heap,
  4849. IN PHEAP_FREE_ENTRY FreeBlock,
  4850. IN SIZE_T FreeSize
  4851. )
  4852. /*++
  4853. Routine Description:
  4854. This routines take a piece of committed memory and adds to the
  4855. the appropriate free lists for the heap. If necessary this
  4856. routine will divide up the free block to sizes that fit
  4857. on the free list
  4858. Arguments:
  4859. Heap - Supplies a pointer to the owning heap
  4860. FreeBlock - Supplies a pointer to the block being freed
  4861. FreeSize - Supplies the size, in bytes, of the block being freed
  4862. Return Value:
  4863. None.
  4864. --*/
  4865. {
  4866. USHORT PreviousSize, Size;
  4867. UCHAR Flags;
  4868. UCHAR SegmentIndex;
  4869. PHEAP_SEGMENT Segment;
  4870. RTL_PAGED_CODE();
  4871. //
  4872. // Get the size of the previous block, the index of the segment
  4873. // containing this block, and the flags specific to the block
  4874. //
  4875. PreviousSize = FreeBlock->PreviousSize;
  4876. SegmentIndex = FreeBlock->SegmentIndex;
  4877. Segment = Heap->Segments[ SegmentIndex ];
  4878. Flags = FreeBlock->Flags;
  4879. //
  4880. // Adjust the total amount free in the heap
  4881. //
  4882. Heap->TotalFreeSize += FreeSize;
  4883. //
  4884. // Now, while there is still something left to add to the free list
  4885. // we'll process the information
  4886. //
  4887. while (FreeSize != 0) {
  4888. //
  4889. // If the size is too big for our free lists then we'll
  4890. // chop it down.
  4891. //
  4892. if (FreeSize > (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
  4893. Size = HEAP_MAXIMUM_BLOCK_SIZE;
  4894. //
  4895. // This little adjustment is so that we don't have a remainder
  4896. // that is too small to be useful on the next iteration
  4897. // through the loop
  4898. //
  4899. if (FreeSize == ((ULONG)HEAP_MAXIMUM_BLOCK_SIZE + 1)) {
  4900. Size -= 16;
  4901. }
  4902. //
  4903. // Guarantee that Last entry does not get set in this
  4904. // block.
  4905. //
  4906. FreeBlock->Flags = 0;
  4907. } else {
  4908. Size = (USHORT)FreeSize;
  4909. //
  4910. // This could propagate the last entry flag
  4911. //
  4912. FreeBlock->Flags = Flags;
  4913. }
  4914. //
  4915. // Update the block sizes and then insert this
  4916. // block into a free list
  4917. //
  4918. FreeBlock->PreviousSize = PreviousSize;
  4919. FreeBlock->SegmentIndex = SegmentIndex;
  4920. FreeBlock->Size = Size;
  4921. RtlpInsertFreeBlockDirect( Heap, FreeBlock, Size );
  4922. //
  4923. // Note the size of what we just freed, and then update
  4924. // our state information for the next time through the
  4925. // loop
  4926. //
  4927. PreviousSize = Size;
  4928. FreeSize -= Size;
  4929. //
  4930. // Update the last entry in segment, if necessary
  4931. //
  4932. if (FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
  4933. PHEAP_SEGMENT Segment;
  4934. Segment = Heap->Segments[ FreeBlock->SegmentIndex ];
  4935. Segment->LastEntryInSegment = (PHEAP_ENTRY)FreeBlock;
  4936. }
  4937. FreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + Size);
  4938. //
  4939. // Check if we're done with the free block based on the
  4940. // segment information, otherwise go back up and check size
  4941. // Note that is means that we can get called with a very
  4942. // large size and still work.
  4943. //
  4944. if ((PHEAP_ENTRY)FreeBlock >= Segment->LastValidEntry) {
  4945. return;
  4946. }
  4947. }
  4948. //
  4949. // If the block we're freeing did not think it was the last entry
  4950. // then tell the next block our real size.
  4951. //
  4952. if (!(Flags & HEAP_ENTRY_LAST_ENTRY)) {
  4953. FreeBlock->PreviousSize = PreviousSize;
  4954. }
  4955. //
  4956. // And return to our caller
  4957. //
  4958. return;
  4959. }
  4960. //
  4961. // Declared in heappriv.h
  4962. //
  4963. PHEAP_ENTRY_EXTRA
  4964. RtlpGetExtraStuffPointer (
  4965. PHEAP_ENTRY BusyBlock
  4966. )
  4967. /*++
  4968. Routine Description:
  4969. This routine calculates where the extra stuff record will be given
  4970. the busy block and returns a pointer to it. The caller must have
  4971. already checked that the entry extra field is present
  4972. Arguments:
  4973. BusyBlock - Supplies the busy block whose extra stuff we are seeking
  4974. Return Value:
  4975. PHEAP_ENTRY_EXTRA - returns a pointer to the extra stuff record.
  4976. --*/
  4977. {
  4978. ULONG AllocationIndex;
  4979. RTL_PAGED_CODE();
  4980. //
  4981. // On big blocks the extra stuff is automatically part of the
  4982. // block
  4983. //
  4984. if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  4985. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  4986. VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  4987. return &VirtualAllocBlock->ExtraStuff;
  4988. } else {
  4989. //
  4990. // On non big blocks the extra stuff follows immediately after
  4991. // the allocation itself.
  4992. //
  4993. // We do some funny math here because the busy block
  4994. // stride is 8 bytes we know we can stride it by its
  4995. // index minus one to get to the end of the allocation
  4996. //
  4997. AllocationIndex = BusyBlock->Size;
  4998. return (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
  4999. }
  5000. }
  5001. //
  5002. // Declared in heappriv.h
  5003. //
  5004. SIZE_T
  5005. RtlpGetSizeOfBigBlock (
  5006. IN PHEAP_ENTRY BusyBlock
  5007. )
  5008. /*++
  5009. Routine Description:
  5010. This routine returns the size, in bytes, of the big allocation block
  5011. Arguments:
  5012. BusyBlock - Supplies a pointer to the block being queried
  5013. Return Value:
  5014. SIZE_T - Returns the size, in bytes, that was allocated to the big
  5015. block
  5016. --*/
  5017. {
  5018. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  5019. RTL_PAGED_CODE();
  5020. //
  5021. // Get a pointer to the block header itself
  5022. //
  5023. VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
  5024. //
  5025. // The size allocated to the block is actually the difference between the
  5026. // commit size stored in the virtual alloc block and the size stored in
  5027. // in the block.
  5028. //
  5029. return VirtualAllocBlock->CommitSize - BusyBlock->Size;
  5030. }
  5031. //
  5032. // Declared in heappriv.h
  5033. //
  5034. BOOLEAN
  5035. RtlpCheckBusyBlockTail (
  5036. IN PHEAP_ENTRY BusyBlock
  5037. )
  5038. /*++
  5039. Routine Description:
  5040. This routine checks to see if the bytes beyond the user specified
  5041. allocation have been modified. It does this by checking for a tail
  5042. fill pattern
  5043. Arguments:
  5044. BusyBlock - Supplies the heap block being queried
  5045. Return Value:
  5046. BOOLEAN - TRUE if the tail is still okay and FALSE otherwise
  5047. --*/
  5048. {
  5049. PCHAR Tail;
  5050. SIZE_T Size, cbEqual;
  5051. RTL_PAGED_CODE();
  5052. //
  5053. // Compute the user allocated size of the input heap block
  5054. //
  5055. if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
  5056. Size = RtlpGetSizeOfBigBlock( BusyBlock );
  5057. } else {
  5058. Size = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) - BusyBlock->UnusedBytes;
  5059. }
  5060. //
  5061. // Compute a pointer to the tail of the input block. This would
  5062. // be the space right after the user allocated portion
  5063. //
  5064. Tail = (PCHAR)(BusyBlock + 1) + Size;
  5065. //
  5066. // Check if the tail fill pattern is still there
  5067. //
  5068. cbEqual = RtlCompareMemory( Tail,
  5069. CheckHeapFillPattern,
  5070. CHECK_HEAP_TAIL_SIZE );
  5071. //
  5072. // If the number we get back isn't equal to the tail size then
  5073. // someone modified the block beyond its user specified allocation
  5074. // size
  5075. //
  5076. if (cbEqual != CHECK_HEAP_TAIL_SIZE) {
  5077. //
  5078. // Do some debug printing
  5079. //
  5080. HeapDebugPrint(( "Heap block at %p modified at %p past requested size of %lx\n",
  5081. BusyBlock,
  5082. Tail + cbEqual,
  5083. Size ));
  5084. HeapDebugBreak( BusyBlock );
  5085. //
  5086. // And tell our caller there was an error
  5087. //
  5088. return FALSE;
  5089. } else {
  5090. //
  5091. // And return to our caller that the tail is fine
  5092. //
  5093. return TRUE;
  5094. }
  5095. }
  5096. //
  5097. // Nondedicated free list optimization
  5098. // The index is active only in the USER MODE HEAP
  5099. //
  5100. #ifndef NTOS_KERNEL_RUNTIME
  5101. //
  5102. // RtlpSizeToAllocIndex is used to convert from a size (in heap allocation units)
  5103. // to an index into the array
  5104. //
  5105. #define RtlpSizeToAllocIndex(HI,S) \
  5106. (( (ULONG)((S) - HEAP_MAXIMUM_FREELISTS) >= (HI)->ArraySize) ? ((HI)->ArraySize - 1) : \
  5107. ((S) - HEAP_MAXIMUM_FREELISTS))
  5108. VOID
  5109. RtlpInitializeListIndex(
  5110. IN PHEAP Heap
  5111. )
  5112. /*++
  5113. Routine Description:
  5114. This routine initialize the index for large blocks. It can be called any time
  5115. during the execution. The function assumes the heap lock is acquired.
  5116. Arguments:
  5117. Heap - Supplies a pointer to the heap being manipulated
  5118. Return Value:
  5119. None
  5120. --*/
  5121. {
  5122. PHEAP_INDEX HeapIndex = NULL;
  5123. ULONG i;
  5124. SIZE_T CommitSize;
  5125. NTSTATUS Status;
  5126. ULONG_PTR ArraySize;
  5127. UINT64 _HeapPerfStartTimer;
  5128. //
  5129. // Check if we already have an index
  5130. //
  5131. if ( Heap->LargeBlocksIndex == NULL) {
  5132. //
  5133. // Determine the number of entries into the index.
  5134. // For a heap with high usage, the most of blocks into the
  5135. // non-dedicated list should be smaller than block
  5136. // decommit threshold + one page
  5137. //
  5138. ArraySize = Heap->DeCommitFreeBlockThreshold + (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT) - HEAP_MAXIMUM_FREELISTS;
  5139. //
  5140. // The statement bellow is just a sanity round up of the array size.
  5141. // Basically for current heap constants this is not necessary:
  5142. // DeCommitFreeBlockThreshold == 512
  5143. // PAGE_SIZE >> HEAP_GRANULARITY_SHIFT == 512
  5144. // So the ArraySize == 1024 is 32 aligned
  5145. //
  5146. ArraySize = ROUND_UP_TO_POWER2( ArraySize, 32 );
  5147. //
  5148. // Determine the amount of memory we need from OS
  5149. //
  5150. CommitSize = sizeof(HEAP_INDEX) +
  5151. ArraySize * sizeof(PHEAP_FREE_ENTRY) +
  5152. ArraySize / 8;
  5153. CommitSize = ROUND_UP_TO_POWER2( CommitSize, PAGE_SIZE );
  5154. Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
  5155. (PVOID *)&HeapIndex,
  5156. 0,
  5157. &CommitSize,
  5158. MEM_RESERVE | MEM_COMMIT,
  5159. PAGE_READWRITE
  5160. );
  5161. if ( NT_SUCCESS(Status) ) {
  5162. //
  5163. // Here the allocation succeed. We need to
  5164. // initialize the index structures
  5165. //
  5166. PLIST_ENTRY Head, Next;
  5167. //
  5168. // Initialize the array fields
  5169. //
  5170. HeapIndex->ArraySize = (ULONG)ArraySize;
  5171. HeapIndex->VirtualMemorySize = (ULONG)CommitSize;
  5172. //
  5173. // The FreeListHints will be immediately after the index structure
  5174. //
  5175. HeapIndex->FreeListHints = (PHEAP_FREE_ENTRY *)((PUCHAR)HeapIndex + sizeof(HEAP_INDEX));
  5176. //
  5177. // The bitmap is placed after the array with hints to
  5178. // free blocks
  5179. //
  5180. HeapIndex->u.FreeListsInUseBytes = (PUCHAR)(HeapIndex->FreeListHints + ArraySize);
  5181. HeapIndex->LargeBlocksCacheDepth = 0;
  5182. if (RtlpDisableHeapLookaside & HEAP_COMPAT_DISABLE_LARGECACHE) {
  5183. HeapIndex->LargeBlocksCacheSequence = 0;
  5184. } else {
  5185. HeapIndex->LargeBlocksCacheSequence = 1;
  5186. }
  5187. //
  5188. // Save the original non-dedicated list from the heap
  5189. //
  5190. Head = &Heap->FreeLists[ 0 ];
  5191. Next = Head->Flink;
  5192. //
  5193. // Walk the non-dedicated list and insert each block found
  5194. // there into the new structures
  5195. //
  5196. while (Head != Next) {
  5197. PHEAP_FREE_ENTRY FreeEntry;
  5198. ULONG AllocIndex;
  5199. //
  5200. // Get the free block from the old list
  5201. //
  5202. FreeEntry = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5203. //
  5204. // Save the next link. The insertion into the new
  5205. // structure will destroy it
  5206. //
  5207. Next = Next->Flink;
  5208. //
  5209. // Insert the block into the large blocks array
  5210. //
  5211. AllocIndex = RtlpSizeToAllocIndex( HeapIndex, FreeEntry->Size );
  5212. if ( !HeapIndex->FreeListHints[ AllocIndex ] ) {
  5213. HeapIndex->FreeListHints[ AllocIndex ] = FreeEntry;
  5214. SET_INDEX_BIT( HeapIndex, AllocIndex );
  5215. }
  5216. if (AllocIndex == (HeapIndex->ArraySize - 1)) {
  5217. HeapIndex->LargeBlocksCacheDepth += 1;
  5218. }
  5219. }
  5220. HeapIndex->LargeBlocksCacheMaxDepth = HeapIndex->LargeBlocksCacheDepth;
  5221. HeapIndex->LargeBlocksCacheMinDepth = HeapIndex->LargeBlocksCacheDepth;
  5222. HeapIndex->CacheStats.Committs = 0;
  5223. HeapIndex->CacheStats.Decommitts = 0;
  5224. HeapIndex->CacheStats.LargestDepth = HeapIndex->LargeBlocksCacheDepth;
  5225. HeapIndex->CacheStats.LargestRequiredDepth = 0;
  5226. NtQueryPerformanceCounter( (PLARGE_INTEGER)&_HeapPerfStartTimer , (PLARGE_INTEGER)&HeapIndex->PerfData.CountFrequence);
  5227. //
  5228. // Initialize the LargeBlocksIndex with the new created structure
  5229. //
  5230. Heap->LargeBlocksIndex = HeapIndex;
  5231. //
  5232. // validate the index if HEAP_VALIDATE_INDEX is defined
  5233. // (Debug - test only)
  5234. //
  5235. RtlpValidateNonDedicatedList( Heap );
  5236. }
  5237. }
  5238. }
  5239. PLIST_ENTRY
  5240. RtlpFindEntry (
  5241. IN PHEAP Heap,
  5242. IN ULONG Size
  5243. )
  5244. /*++
  5245. Routine Description:
  5246. The function search the first block into the non-dedicated list
  5247. greater or equal with the given size.
  5248. Arguments:
  5249. Heap - Supplies a pointer to the heap being manipulated
  5250. Size - The size in heap units we're looking for
  5251. Return Value:
  5252. Return the list entry for the block which match the search criteria.
  5253. If the search fails simple returns the Non-dedicated list header.
  5254. --*/
  5255. {
  5256. PHEAP_INDEX HeapIndex = NULL;
  5257. ULONG LookupBitmapUlongIndex;
  5258. ULONG LastValidIndex;
  5259. ULONG CrtBitmapUlong;
  5260. PULONG UlongArray;
  5261. PHEAP_FREE_ENTRY FreeEntry = NULL;
  5262. PLIST_ENTRY Head, Next;
  5263. PHEAP_FREE_ENTRY LastBlock, FirstBlock;
  5264. ULONG AllocIndex;
  5265. Head = &Heap->FreeLists[0];
  5266. Next = Head->Blink;
  5267. //
  5268. // Check if the list is empty. Return the list head if it is.
  5269. //
  5270. if (Head == Next) {
  5271. return Head;
  5272. }
  5273. //
  5274. // Compare if the largest block into the free list is smaller
  5275. // than the requested size.
  5276. //
  5277. LastBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5278. if (LastBlock->Size < Size) {
  5279. //
  5280. // we don't have a block available for our request
  5281. //
  5282. return Head;
  5283. }
  5284. //
  5285. // If the block is smaller or equal with the first free block we'll
  5286. // return the first block into the list w/o searching the index
  5287. //
  5288. FirstBlock = CONTAINING_RECORD( Head->Flink, HEAP_FREE_ENTRY, FreeList );
  5289. if (Size <= FirstBlock->Size) {
  5290. //
  5291. // Return the first block then.
  5292. //
  5293. return Head->Flink;
  5294. }
  5295. //
  5296. // At this point we have a block that must be somewhere in
  5297. // the middle of the list. We'll use the index to locate it.
  5298. //
  5299. HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5300. AllocIndex = RtlpSizeToAllocIndex(HeapIndex, Size);
  5301. //
  5302. // We'll try first into the last sublist
  5303. //
  5304. if ( AllocIndex == (HeapIndex->ArraySize - 1) ) {
  5305. FreeEntry = HeapIndex->FreeListHints[ AllocIndex ];
  5306. Next = &FreeEntry->FreeList;
  5307. while ( Head != Next ) {
  5308. FreeEntry = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5309. if (FreeEntry->Size >= Size) {
  5310. return &FreeEntry->FreeList;
  5311. }
  5312. Next = Next->Flink;
  5313. }
  5314. }
  5315. //
  5316. // Calculate the starting index into the bitmap array
  5317. //
  5318. LookupBitmapUlongIndex = AllocIndex >> 5;
  5319. //
  5320. // Determine the last index into the ULONG bitmap where the
  5321. // lookup must stop
  5322. //
  5323. LastValidIndex = (HeapIndex->ArraySize >> 5) - 1;
  5324. UlongArray = HeapIndex->u.FreeListsInUseUlong + LookupBitmapUlongIndex;
  5325. CrtBitmapUlong = *UlongArray;
  5326. //
  5327. // Mask off the bits in the first ULONG that represent allocations
  5328. // smaller than we need.
  5329. //
  5330. CrtBitmapUlong = CrtBitmapUlong & ~((1 << ((ULONG) Size & 0x1f)) - 1);
  5331. //
  5332. // Loop through the ULONG bitmap until we'll find something
  5333. // not empty
  5334. //
  5335. while ( !CrtBitmapUlong &&
  5336. (LookupBitmapUlongIndex <= LastValidIndex) ) {
  5337. CrtBitmapUlong = *(++UlongArray);
  5338. LookupBitmapUlongIndex++;
  5339. }
  5340. //
  5341. // Sanity checking if we found something.
  5342. // The test for smaller block and larger block should
  5343. // guarantee we found something in the loop above
  5344. //
  5345. if ( !CrtBitmapUlong ) {
  5346. HeapDebugPrint(( "Index not found into the bitmap %08lx\n", Size ));
  5347. // DbgBreakPoint();
  5348. return Head;
  5349. }
  5350. //
  5351. // Determine the position within bitmap where the bit is set.
  5352. // This is the index into the hints array
  5353. //
  5354. LookupBitmapUlongIndex = (LookupBitmapUlongIndex << 5) +
  5355. RtlFindFirstSetRightMember( CrtBitmapUlong );
  5356. //
  5357. // Return the list entry for the block we found
  5358. //
  5359. FreeEntry = HeapIndex->FreeListHints[ LookupBitmapUlongIndex ];
  5360. return &FreeEntry->FreeList;
  5361. }
  5362. VOID
  5363. RtlpFlushLargestCacheBlock (
  5364. IN PHEAP Heap
  5365. )
  5366. {
  5367. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5368. if ((HeapIndex != NULL) &&
  5369. (HeapIndex->LargeBlocksCacheSequence != 0) ) {
  5370. PLIST_ENTRY Head, Next;
  5371. PHEAP_FREE_ENTRY FreeBlock;
  5372. Head = &Heap->FreeLists[ 0 ];
  5373. Next = Head->Blink;
  5374. if (Head != Next) {
  5375. ULONG PrevSeq = HeapIndex->LargeBlocksCacheSequence;
  5376. //
  5377. // Lock the cache operations
  5378. //
  5379. HeapIndex->LargeBlocksCacheSequence = 0;
  5380. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5381. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  5382. FreeBlock->Flags |= HEAP_ENTRY_BUSY;
  5383. Heap->TotalFreeSize -= FreeBlock->Size;
  5384. RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)FreeBlock, FreeBlock->Size );
  5385. //
  5386. // Unlock the cache
  5387. //
  5388. HeapIndex->LargeBlocksCacheSequence = PrevSeq;
  5389. RtlpValidateNonDedicatedList(Heap);
  5390. }
  5391. }
  5392. }
  5393. VOID
  5394. RtlpFlushCacheContents (
  5395. IN PHEAP Heap
  5396. )
  5397. {
  5398. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5399. if ((HeapIndex != NULL) &&
  5400. (HeapIndex->LargeBlocksCacheSequence != 0) ) {
  5401. LONG NewDepth = HeapIndex->LargeBlocksCacheMaxDepth - HeapIndex->LargeBlocksCacheMinDepth;
  5402. if ( (HeapIndex->LargeBlocksCacheDepth > NewDepth) ) {
  5403. PLIST_ENTRY Head, Next;
  5404. LIST_ENTRY ListToFree;
  5405. PHEAP_FREE_ENTRY FreeBlock;
  5406. LONG BlocksToFree = HeapIndex->LargeBlocksCacheDepth - NewDepth;
  5407. LONG RemainingBlocks = HeapIndex->LargeBlocksCacheDepth;
  5408. if (HeapIndex->LargeBlocksCacheMaxDepth > HeapIndex->CacheStats.LargestDepth) {
  5409. HeapIndex->CacheStats.LargestDepth = HeapIndex->LargeBlocksCacheMaxDepth;
  5410. }
  5411. if (NewDepth > HeapIndex->CacheStats.LargestRequiredDepth) {
  5412. HeapIndex->CacheStats.LargestRequiredDepth = NewDepth;
  5413. }
  5414. //
  5415. // Get the last hint for this specific size from the index
  5416. //
  5417. FreeBlock = HeapIndex->FreeListHints[ HeapIndex->ArraySize - 1 ];
  5418. if (FreeBlock == NULL) {
  5419. DbgPrint("No free blocks in the cache but the depth is not 0 %ld\n",
  5420. HeapIndex->LargeBlocksCacheDepth);
  5421. return;
  5422. }
  5423. //
  5424. // Lock the cache operations
  5425. //
  5426. HeapIndex->LargeBlocksCacheSequence = 0;
  5427. Head = &Heap->FreeLists[ 0 ];
  5428. Next = &FreeBlock->FreeList;
  5429. InitializeListHead(&ListToFree);
  5430. while (Head != Next) {
  5431. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5432. Next = Next->Flink;
  5433. if ( (((SIZE_T)FreeBlock->Size) << HEAP_GRANULARITY_SHIFT) > Heap->DeCommitFreeBlockThreshold) {
  5434. if ((FreeBlock->Flags & HEAP_ENTRY_SETTABLE_FLAG3)
  5435. ||
  5436. (BlocksToFree >= RemainingBlocks) ) {
  5437. RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
  5438. InsertTailList(&ListToFree, &FreeBlock->FreeList);
  5439. FreeBlock->Flags |= HEAP_ENTRY_BUSY;
  5440. Heap->TotalFreeSize -= FreeBlock->Size;
  5441. BlocksToFree -= 1;
  5442. } else {
  5443. FreeBlock->Flags |= HEAP_ENTRY_SETTABLE_FLAG3;
  5444. }
  5445. }
  5446. RemainingBlocks -= 1;
  5447. }
  5448. Head = &ListToFree;
  5449. Next = ListToFree.Flink;
  5450. while (Head != Next) {
  5451. FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5452. RemoveEntryList(&FreeBlock->FreeList);
  5453. Next = ListToFree.Flink;
  5454. RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)FreeBlock, FreeBlock->Size );
  5455. }
  5456. }
  5457. HeapIndex->LargeBlocksCacheMaxDepth = HeapIndex->LargeBlocksCacheDepth;
  5458. HeapIndex->LargeBlocksCacheMinDepth = HeapIndex->LargeBlocksCacheDepth;
  5459. HeapIndex->LargeBlocksCacheSequence = 1;
  5460. RtlpValidateNonDedicatedList(Heap);
  5461. }
  5462. }
  5463. VOID
  5464. RtlpUpdateIndexRemoveBlock (
  5465. IN PHEAP Heap,
  5466. IN PHEAP_FREE_ENTRY FreeEntry
  5467. )
  5468. /*++
  5469. Routine Description:
  5470. This function is called each time a free block is removed from
  5471. non-dedicated list. This should update the heap index to reflect the change.
  5472. NOTE : This function must be called before the freeentry is
  5473. actually removed from the non-dedicated list
  5474. Arguments:
  5475. Heap - Supplies a pointer to the heap being manipulated
  5476. FreeEntry - The removed free block
  5477. Return Value:
  5478. None
  5479. --*/
  5480. {
  5481. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5482. ULONG Size = FreeEntry->Size;
  5483. //
  5484. // It updates the index only for large sizes
  5485. // (over 1024 bytes)
  5486. //
  5487. if (Size >= HEAP_MAXIMUM_FREELISTS) {
  5488. //
  5489. // Update the length of the nondedicated list
  5490. // This is happening even if the index isn't created
  5491. //
  5492. Heap->NonDedicatedListLength -= 1;
  5493. //
  5494. // If we have an index, we need to update the index structures
  5495. //
  5496. if (HeapIndex) {
  5497. PHEAP_FREE_ENTRY PrevBlock;
  5498. ULONG AllocIndex = RtlpSizeToAllocIndex( HeapIndex, Size );
  5499. PLIST_ENTRY Flink = FreeEntry->FreeList.Flink;
  5500. //
  5501. // Get the next block into the list. Set it to NULL
  5502. // if this is the last element here
  5503. //
  5504. PHEAP_FREE_ENTRY NextFreeBlock = (Flink == &Heap->FreeLists[ 0 ]) ?
  5505. NULL :
  5506. CONTAINING_RECORD( Flink, HEAP_FREE_ENTRY, FreeList );
  5507. //
  5508. // Get the last hint for this specific size from the index
  5509. //
  5510. PrevBlock = HeapIndex->FreeListHints[ AllocIndex ];
  5511. if ( PrevBlock == FreeEntry) {
  5512. //
  5513. // The free block being removed is actually the hint for that
  5514. // specific size. We need then to update
  5515. //
  5516. if (AllocIndex < (HeapIndex->ArraySize - 1)) {
  5517. //
  5518. // If the next block has the same size with the current one
  5519. // we need to update only the hint pointer
  5520. //
  5521. if ( NextFreeBlock &&
  5522. (NextFreeBlock->Size == Size) ) {
  5523. HeapIndex->FreeListHints[ AllocIndex ] = NextFreeBlock;
  5524. } else {
  5525. //
  5526. // There is no other block with this size, so we need
  5527. // set the hint to NULL and clear the appropriate bit
  5528. //
  5529. HeapIndex->FreeListHints[ AllocIndex ] = NULL;
  5530. CLEAR_INDEX_BIT( HeapIndex, AllocIndex );
  5531. }
  5532. } else {
  5533. //
  5534. // We are here because this is the last hint from the array
  5535. // This list may contain free blocks with different sizes.
  5536. //
  5537. if (NextFreeBlock) {
  5538. //
  5539. // We have an other block larger than this one
  5540. // We move the hint to that pointer
  5541. //
  5542. HeapIndex->FreeListHints[ AllocIndex ] = NextFreeBlock;
  5543. } else {
  5544. //
  5545. // This was the last block within the non-dedicated list
  5546. // Clear the hint pointer and the appropriate bit.
  5547. //
  5548. HeapIndex->FreeListHints[ AllocIndex ] = NULL;
  5549. CLEAR_INDEX_BIT( HeapIndex, AllocIndex );
  5550. }
  5551. }
  5552. }
  5553. if (AllocIndex == (HeapIndex->ArraySize - 1)) {
  5554. HeapIndex->LargeBlocksCacheDepth -= 1;
  5555. if (HeapIndex->LargeBlocksCacheDepth < 0) {
  5556. DbgPrint(("Invalid Cache depth\n"));
  5557. }
  5558. if (HeapIndex->LargeBlocksCacheSequence != 0) {
  5559. HeapIndex->LargeBlocksCacheSequence += 1;
  5560. if (HeapIndex->LargeBlocksCacheDepth < HeapIndex->LargeBlocksCacheMinDepth) {
  5561. HeapIndex->LargeBlocksCacheMinDepth = HeapIndex->LargeBlocksCacheDepth;
  5562. }
  5563. }
  5564. }
  5565. }
  5566. }
  5567. }
  5568. VOID
  5569. RtlpUpdateIndexInsertBlock (
  5570. IN PHEAP Heap,
  5571. IN PHEAP_FREE_ENTRY FreeEntry
  5572. )
  5573. /*++
  5574. Routine Description:
  5575. This function is called each time a free block is inserted into
  5576. non-dedicated list. This should update the heap index to reflect the change.
  5577. NOTE : This function must be called AFTER the free entry is
  5578. actually inserted into the non-dedicated list
  5579. Arguments:
  5580. Heap - Supplies a pointer to the heap being manipulated
  5581. FreeEntry - The new inserted free block free block
  5582. Return Value:
  5583. None
  5584. --*/
  5585. {
  5586. PHEAP_INDEX HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5587. //
  5588. // we only have something to do if the size is over the dedicated list
  5589. // range
  5590. //
  5591. if ( FreeEntry->Size >= HEAP_MAXIMUM_FREELISTS ) {
  5592. //
  5593. // Update the non-dedicated list length
  5594. //
  5595. Heap->NonDedicatedListLength += 1;
  5596. if ( HeapIndex ) {
  5597. //
  5598. // We have an index for this list. we need to to some
  5599. // extra-work to maintain it
  5600. //
  5601. PHEAP_FREE_ENTRY PrevBlock;
  5602. ULONG AllocIndex = RtlpSizeToAllocIndex( HeapIndex, FreeEntry->Size );
  5603. //
  5604. // Get the original hint stored into the index
  5605. //
  5606. PrevBlock = HeapIndex->FreeListHints[ AllocIndex ];
  5607. //
  5608. // If the hint before was NULL, or we are adding a new
  5609. // block smaller or equal with the previous one, we need
  5610. // to update the hint pointer
  5611. //
  5612. if ( (!PrevBlock) ||
  5613. (FreeEntry->Size <= PrevBlock->Size) ) {
  5614. HeapIndex->FreeListHints[ AllocIndex ] = FreeEntry;
  5615. }
  5616. //
  5617. // If this is the first time we set a hint for that size
  5618. // we need to set the busy bit into the bitmap
  5619. //
  5620. if ( !PrevBlock ) {
  5621. SET_INDEX_BIT( HeapIndex, AllocIndex );
  5622. }
  5623. if ( AllocIndex == (HeapIndex->ArraySize - 1) ) {
  5624. HeapIndex->LargeBlocksCacheDepth += 1;
  5625. if (HeapIndex->LargeBlocksCacheSequence != 0) {
  5626. HeapIndex->LargeBlocksCacheSequence += 1;
  5627. if (HeapIndex->LargeBlocksCacheDepth > HeapIndex->LargeBlocksCacheMaxDepth) {
  5628. HeapIndex->LargeBlocksCacheMaxDepth = HeapIndex->LargeBlocksCacheDepth;
  5629. }
  5630. }
  5631. }
  5632. } else if ( Heap->NonDedicatedListLength >= HEAP_INDEX_THRESHOLD ) {
  5633. //
  5634. // We don't have an index, but we have enough blocks into the
  5635. // non-dedicated list. We need to create an index right now,
  5636. // to help us in search further
  5637. //
  5638. RtlpInitializeListIndex( Heap );
  5639. }
  5640. }
  5641. }
  5642. //
  5643. // Additional debug - test code
  5644. //
  5645. #ifdef HEAP_VALIDATE_INDEX
  5646. BOOLEAN
  5647. RtlpGetBitState(
  5648. IN PHEAP_INDEX HeapIndex,
  5649. IN ULONG Bit
  5650. )
  5651. /*++
  5652. Routine Description:
  5653. Utility routine which tests the given bit from the bitmap
  5654. Arguments:
  5655. Heap - Supplies a pointer to the heapindex being manipulated
  5656. Bit - The bit to be tested
  5657. Return Value:
  5658. TRUE if the bit is 1 and 0 otherwise
  5659. --*/
  5660. {
  5661. ULONG _Index_;
  5662. ULONG _Bit_;
  5663. _Index_ = Bit >> 3;
  5664. _Bit_ = (1 << (Bit & 7));
  5665. return (((HeapIndex)->u.FreeListsInUseBytes[ _Index_ ] & _Bit_)) != 0;
  5666. }
  5667. BOOLEAN
  5668. RtlpValidateNonDedicatedList (
  5669. IN PHEAP Heap
  5670. )
  5671. /*++
  5672. Routine Description:
  5673. Utility routine which validate the index and non-dedicated lists
  5674. structures
  5675. Arguments:
  5676. Heap - Supplies a pointer to the heapindex being manipulated
  5677. Return Value:
  5678. TRUE validation succeeds
  5679. --*/
  5680. {
  5681. PHEAP_INDEX HeapIndex = NULL;
  5682. PLIST_ENTRY Head, Next;
  5683. ULONG PreviousSize = 0;
  5684. ULONG PreviousIndex = 0;
  5685. LONG LargeBlocksCount = 0;
  5686. HeapIndex = (PHEAP_INDEX)Heap->LargeBlocksIndex;
  5687. //
  5688. // we only do validation if we have a heap index created
  5689. //
  5690. if (HeapIndex) {
  5691. Head = &Heap->FreeLists[ 0 ];
  5692. Next = Head->Flink;
  5693. //
  5694. // Loop through the free blocks placed into the non-dedicated list
  5695. //
  5696. while (Head != Next) {
  5697. PHEAP_FREE_ENTRY FreeEntry;
  5698. ULONG AllocIndex;
  5699. //
  5700. // Get the free block from the old list
  5701. //
  5702. FreeEntry = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
  5703. //
  5704. // Test if the blocks are in the proper order (ascending)
  5705. //
  5706. if (PreviousSize > FreeEntry->Size) {
  5707. HeapDebugPrint(( "Invalid Block order %08lx - %08lx, %08lx\n",
  5708. FreeEntry,
  5709. FreeEntry->Size,
  5710. PreviousSize ));
  5711. DbgBreakPoint();
  5712. }
  5713. //
  5714. // Get the appropriate index for the current block
  5715. //
  5716. AllocIndex = RtlpSizeToAllocIndex( HeapIndex, FreeEntry->Size );
  5717. if (AllocIndex == (HeapIndex->ArraySize - 1)) {
  5718. LargeBlocksCount += 1;
  5719. }
  5720. if (PreviousSize != FreeEntry->Size) {
  5721. ULONG i;
  5722. //
  5723. // We are here only for the first block of a given size
  5724. //
  5725. //
  5726. // We need to have all hints NULL between two adjacent
  5727. // free blocks of different sizes
  5728. //
  5729. for (i = PreviousIndex + 1; i < AllocIndex; i++) {
  5730. //
  5731. // Report an error if there is a hint, but that block doesn't
  5732. // exists into the non-dedicated list
  5733. //
  5734. if (HeapIndex->FreeListHints[i]) {
  5735. DbgPrint( "Free block missing %lx, %08lx\n",
  5736. i,
  5737. HeapIndex->FreeListHints[i]
  5738. );
  5739. DbgBreakPoint();
  5740. }
  5741. //
  5742. // Reports an error if there is a bit set for a size
  5743. // not inserted into the non-dedicated list
  5744. //
  5745. if ( RtlpGetBitState(HeapIndex, i) ) {
  5746. DbgPrint("Invalid bit state. Must be 0 %lx\n", i);
  5747. DbgBreakPoint();
  5748. }
  5749. }
  5750. //
  5751. // we are here for the first block of this size. So the hint
  5752. // should point to this block
  5753. //
  5754. if ( (AllocIndex < HeapIndex->ArraySize - 1) &&
  5755. (HeapIndex->FreeListHints[ AllocIndex ] != FreeEntry)) {
  5756. DbgPrint( "Invalid index %lx for block %08lx (%08lx)\n",
  5757. AllocIndex,
  5758. HeapIndex->FreeListHints[AllocIndex],
  5759. FreeEntry);
  5760. DbgBreakPoint();
  5761. }
  5762. //
  5763. // We have a block into the non-dedicated list so we need to have
  5764. // the appropriate bit set
  5765. //
  5766. if ( !RtlpGetBitState( HeapIndex, AllocIndex ) ) {
  5767. DbgPrint("Invalid bit state. Must be 1 %lx\n", i);
  5768. DbgBreakPoint();
  5769. }
  5770. }
  5771. //
  5772. // Save the next link. The insertion into the new
  5773. // structure will destroy it
  5774. //
  5775. Next = Next->Flink;
  5776. PreviousSize = FreeEntry->Size;
  5777. PreviousIndex = AllocIndex;
  5778. }
  5779. if (LargeBlocksCount != HeapIndex->LargeBlocksCacheDepth) {
  5780. DbgPrint("Invalid Cache depth %ld. Should be %ld\n",
  5781. HeapIndex->LargeBlocksCacheDepth,
  5782. LargeBlocksCount);
  5783. }
  5784. }
  5785. return TRUE;
  5786. }
  5787. #endif // HEAP_VALIDATE_INDEX
  5788. #endif // NTOS_KERNEL_RUNTIME
  5789. #ifndef NTOS_KERNEL_RUNTIME
  5790. SIZE_T
  5791. GetUCBytes(
  5792. IN PHEAP Heap,
  5793. IN OUT SIZE_T *ReservedSpace,
  5794. IN OUT PULONG NoOfUCRs
  5795. )
  5796. /*++
  5797. Routine Description:
  5798. Utility routine which computes Uncommited Bytes in a Heap
  5799. Arguments:
  5800. Heap - Supplies a pointer to the heap
  5801. ReservedSpace - Pointer to Reserved space which is computed
  5802. NoOfUCRs - Pointer to NoOfUCRs which is computed
  5803. Return Value:
  5804. Total number of UCBytes.
  5805. --*/
  5806. {
  5807. SIZE_T UCBytes = 0;
  5808. PHEAP_SEGMENT pSegment;
  5809. LONG SegmentIndex;
  5810. for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  5811. pSegment = Heap->Segments[ SegmentIndex ];
  5812. if (pSegment) {
  5813. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
  5814. (*ReservedSpace) += pSegment->NumberOfPages;
  5815. (*NoOfUCRs) += pSegment->NumberOfUnCommittedRanges;
  5816. for( UnCommittedRange = pSegment->UnCommittedRanges;
  5817. UnCommittedRange;
  5818. UnCommittedRange = UnCommittedRange->Next ) {
  5819. UCBytes += UnCommittedRange->Size;
  5820. }
  5821. }
  5822. }
  5823. return UCBytes;
  5824. }
  5825. #endif // NTOS_KERNEL_RUNTIME