PageRenderTime 66ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/rtl/heap.c

https://bitbucket.org/arty/arty-newcc-reactos
C | 4085 lines | 2677 code | 677 blank | 731 comment | 487 complexity | 2d9689f7851593f090adde3d9a2c87c8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-3.0, CC-BY-SA-3.0, AGPL-3.0, GPL-3.0, CPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. /* COPYRIGHT: See COPYING in the top level directory
  2. * PROJECT: ReactOS system libraries
  3. * FILE: lib/rtl/heap.c
  4. * PURPOSE: RTL Heap backend allocator
  5. * PROGRAMMERS: Copyright 2010 Aleksey Bragin
  6. */
  7. /* Useful references:
  8. http://msdn.microsoft.com/en-us/library/ms810466.aspx
  9. http://msdn.microsoft.com/en-us/library/ms810603.aspx
  10. http://www.securitylab.ru/analytics/216376.php
  11. http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
  12. http://www.phreedom.org/research/exploits/asn1-bitstring/
  13. http://illmatics.com/Understanding_the_LFH.pdf
  14. http://www.alex-ionescu.com/?p=18
  15. */
  16. /* INCLUDES *****************************************************************/
  17. #include <rtl.h>
  18. #include <heap.h>
  19. #define NDEBUG
  20. #include <debug.h>
  21. RTL_CRITICAL_SECTION RtlpProcessHeapsListLock;
  22. /* Bitmaps stuff */
  23. /* How many least significant bits are clear */
  24. UCHAR RtlpBitsClearLow[] =
  25. {
  26. 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  27. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  28. 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  29. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  30. 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  31. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  32. 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  33. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  34. 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  35. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  36. 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  37. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  38. 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  39. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  40. 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
  41. 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
  42. };
  43. UCHAR FORCEINLINE
  44. RtlpFindLeastSetBit(ULONG Bits)
  45. {
  46. if (Bits & 0xFFFF)
  47. {
  48. if (Bits & 0xFF)
  49. return RtlpBitsClearLow[Bits & 0xFF]; /* Lowest byte */
  50. else
  51. return RtlpBitsClearLow[(Bits >> 8) & 0xFF] + 8; /* 2nd byte */
  52. }
  53. else
  54. {
  55. if ((Bits >> 16) & 0xFF)
  56. return RtlpBitsClearLow[(Bits >> 16) & 0xFF] + 16; /* 3rd byte */
  57. else
  58. return RtlpBitsClearLow[(Bits >> 24) & 0xFF] + 24; /* Highest byte */
  59. }
  60. }
  61. /* Maximum size of a tail-filling pattern used for compare operation */
  62. UCHAR FillPattern[HEAP_ENTRY_SIZE] =
  63. {
  64. HEAP_TAIL_FILL,
  65. HEAP_TAIL_FILL,
  66. HEAP_TAIL_FILL,
  67. HEAP_TAIL_FILL,
  68. HEAP_TAIL_FILL,
  69. HEAP_TAIL_FILL,
  70. HEAP_TAIL_FILL,
  71. HEAP_TAIL_FILL
  72. };
  73. /* FUNCTIONS *****************************************************************/
  74. NTSTATUS NTAPI
  75. RtlpInitializeHeap(OUT PHEAP Heap,
  76. IN ULONG Flags,
  77. IN PHEAP_LOCK Lock OPTIONAL,
  78. IN PRTL_HEAP_PARAMETERS Parameters)
  79. {
  80. ULONG NumUCRs = 8;
  81. ULONG Index;
  82. SIZE_T HeaderSize;
  83. NTSTATUS Status;
  84. PHEAP_UCR_DESCRIPTOR UcrDescriptor;
  85. /* Preconditions */
  86. ASSERT(Heap != NULL);
  87. ASSERT(Parameters != NULL);
  88. ASSERT(!(Flags & HEAP_LOCK_USER_ALLOCATED));
  89. ASSERT(!(Flags & HEAP_NO_SERIALIZE) || (Lock == NULL)); /* HEAP_NO_SERIALIZE => no lock */
  90. /* Start out with the size of a plain Heap header */
  91. HeaderSize = ROUND_UP(sizeof(HEAP), sizeof(HEAP_ENTRY));
  92. /* Check if space needs to be added for the Heap Lock */
  93. if (!(Flags & HEAP_NO_SERIALIZE))
  94. {
  95. if (Lock != NULL)
  96. /* The user manages the Heap Lock */
  97. Flags |= HEAP_LOCK_USER_ALLOCATED;
  98. else
  99. if (RtlpGetMode() == UserMode)
  100. {
  101. /* In user mode, the Heap Lock trails the Heap header */
  102. Lock = (PHEAP_LOCK) ((ULONG_PTR) (Heap) + HeaderSize);
  103. HeaderSize += ROUND_UP(sizeof(HEAP_LOCK), sizeof(HEAP_ENTRY));
  104. }
  105. }
  106. /* Add space for the initial Heap UnCommitted Range Descriptor list */
  107. UcrDescriptor = (PHEAP_UCR_DESCRIPTOR) ((ULONG_PTR) (Heap) + HeaderSize);
  108. HeaderSize += ROUND_UP(NumUCRs * sizeof(HEAP_UCR_DESCRIPTOR), sizeof(HEAP_ENTRY));
  109. /* Sanity check */
  110. ASSERT(HeaderSize <= PAGE_SIZE);
  111. /* Initialise the Heap Entry header containing the Heap header */
  112. Heap->Entry.Size = (USHORT)(HeaderSize >> HEAP_ENTRY_SHIFT);
  113. Heap->Entry.Flags = HEAP_ENTRY_BUSY;
  114. Heap->Entry.SmallTagIndex = LOBYTE(Heap->Entry.Size) ^ HIBYTE(Heap->Entry.Size) ^ Heap->Entry.Flags;
  115. Heap->Entry.PreviousSize = 0;
  116. Heap->Entry.SegmentOffset = 0;
  117. Heap->Entry.UnusedBytes = 0;
  118. /* Initialise the Heap header */
  119. Heap->Signature = HEAP_SIGNATURE;
  120. Heap->Flags = Flags;
  121. Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE |
  122. HEAP_GENERATE_EXCEPTIONS |
  123. HEAP_ZERO_MEMORY |
  124. HEAP_REALLOC_IN_PLACE_ONLY |
  125. HEAP_VALIDATE_PARAMETERS_ENABLED |
  126. HEAP_VALIDATE_ALL_ENABLED |
  127. HEAP_TAIL_CHECKING_ENABLED |
  128. HEAP_CREATE_ALIGN_16 |
  129. HEAP_FREE_CHECKING_ENABLED));
  130. /* Initialise the Heap parameters */
  131. Heap->VirtualMemoryThreshold = ROUND_UP(Parameters->VirtualMemoryThreshold, sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
  132. Heap->SegmentReserve = Parameters->SegmentReserve;
  133. Heap->SegmentCommit = Parameters->SegmentCommit;
  134. Heap->DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_ENTRY_SHIFT;
  135. Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_ENTRY_SHIFT;
  136. Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize;
  137. Heap->CommitRoutine = Parameters->CommitRoutine;
  138. /* Initialise the Heap validation info */
  139. Heap->HeaderValidateCopy = NULL;
  140. Heap->HeaderValidateLength = (USHORT)HeaderSize;
  141. /* Initialise the Heap Lock */
  142. if (!(Flags & HEAP_NO_SERIALIZE) && !(Flags & HEAP_LOCK_USER_ALLOCATED))
  143. {
  144. Status = RtlInitializeHeapLock(&Lock);
  145. if (!NT_SUCCESS(Status))
  146. return Status;
  147. }
  148. Heap->LockVariable = Lock;
  149. /* Initialise the Heap alignment info */
  150. if (Flags & HEAP_CREATE_ALIGN_16)
  151. {
  152. Heap->AlignMask = (ULONG) ~15;
  153. Heap->AlignRound = 15 + sizeof(HEAP_ENTRY);
  154. }
  155. else
  156. {
  157. Heap->AlignMask = (ULONG) ~(sizeof(HEAP_ENTRY) - 1);
  158. Heap->AlignRound = 2 * sizeof(HEAP_ENTRY) - 1;
  159. }
  160. if (Flags & HEAP_TAIL_CHECKING_ENABLED)
  161. Heap->AlignRound += sizeof(HEAP_ENTRY);
  162. /* Initialise the Heap Segment list */
  163. for (Index = 0; Index < HEAP_SEGMENTS; ++Index)
  164. Heap->Segments[Index] = NULL;
  165. /* Initialise the Heap Free Heap Entry lists */
  166. for (Index = 0; Index < HEAP_FREELISTS; ++Index)
  167. InitializeListHead(&Heap->FreeLists[Index]);
  168. /* Initialise the Heap Virtual Allocated Blocks list */
  169. InitializeListHead(&Heap->VirtualAllocdBlocks);
  170. /* Initialise the Heap UnCommitted Region lists */
  171. InitializeListHead(&Heap->UCRSegments);
  172. InitializeListHead(&Heap->UCRList);
  173. /* Register the initial Heap UnCommitted Region Descriptors */
  174. for (Index = 0; Index < NumUCRs; ++Index)
  175. InsertTailList(&Heap->UCRList, &UcrDescriptor[Index].ListEntry);
  176. return STATUS_SUCCESS;
  177. }
  178. VOID FORCEINLINE
  179. RtlpSetFreeListsBit(PHEAP Heap,
  180. PHEAP_FREE_ENTRY FreeEntry)
  181. {
  182. ULONG Index, Bit;
  183. ASSERT(FreeEntry->Size < HEAP_FREELISTS);
  184. /* Calculate offset in the free list bitmap */
  185. Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
  186. Bit = 1 << (FreeEntry->Size & 7);
  187. /* Assure it's not already set */
  188. ASSERT((Heap->u.FreeListsInUseBytes[Index] & Bit) == 0);
  189. /* Set it */
  190. Heap->u.FreeListsInUseBytes[Index] |= Bit;
  191. }
  192. VOID FORCEINLINE
  193. RtlpClearFreeListsBit(PHEAP Heap,
  194. PHEAP_FREE_ENTRY FreeEntry)
  195. {
  196. ULONG Index, Bit;
  197. ASSERT(FreeEntry->Size < HEAP_FREELISTS);
  198. /* Calculate offset in the free list bitmap */
  199. Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
  200. Bit = 1 << (FreeEntry->Size & 7);
  201. /* Assure it was set and the corresponding free list is empty */
  202. ASSERT(Heap->u.FreeListsInUseBytes[Index] & Bit);
  203. ASSERT(IsListEmpty(&Heap->FreeLists[FreeEntry->Size]));
  204. /* Clear it */
  205. Heap->u.FreeListsInUseBytes[Index] ^= Bit;
  206. }
  207. VOID NTAPI
  208. RtlpInsertFreeBlockHelper(PHEAP Heap,
  209. PHEAP_FREE_ENTRY FreeEntry,
  210. SIZE_T BlockSize,
  211. BOOLEAN NoFill)
  212. {
  213. PLIST_ENTRY FreeListHead, Current;
  214. PHEAP_FREE_ENTRY CurrentEntry;
  215. ASSERT(FreeEntry->Size == BlockSize);
  216. /* Fill if it's not denied */
  217. if (!NoFill)
  218. {
  219. FreeEntry->Flags &= ~(HEAP_ENTRY_FILL_PATTERN |
  220. HEAP_ENTRY_EXTRA_PRESENT |
  221. HEAP_ENTRY_BUSY);
  222. if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  223. {
  224. RtlFillMemoryUlong((PCHAR)(FreeEntry + 1),
  225. (BlockSize << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry),
  226. ARENA_FREE_FILLER);
  227. FreeEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
  228. }
  229. }
  230. else
  231. {
  232. /* Clear out all flags except the last entry one */
  233. FreeEntry->Flags &= HEAP_ENTRY_LAST_ENTRY;
  234. }
  235. /* Insert it either into dedicated or non-dedicated list */
  236. if (BlockSize < HEAP_FREELISTS)
  237. {
  238. /* Dedicated list */
  239. FreeListHead = &Heap->FreeLists[BlockSize];
  240. if (IsListEmpty(FreeListHead))
  241. {
  242. RtlpSetFreeListsBit(Heap, FreeEntry);
  243. }
  244. }
  245. else
  246. {
  247. /* Non-dedicated one */
  248. FreeListHead = &Heap->FreeLists[0];
  249. Current = FreeListHead->Flink;
  250. /* Find a position where to insert it to (the list must be sorted) */
  251. while (FreeListHead != Current)
  252. {
  253. CurrentEntry = CONTAINING_RECORD(Current, HEAP_FREE_ENTRY, FreeList);
  254. if (BlockSize <= CurrentEntry->Size)
  255. break;
  256. Current = Current->Flink;
  257. }
  258. FreeListHead = Current;
  259. }
  260. /* Actually insert it into the list */
  261. InsertTailList(FreeListHead, &FreeEntry->FreeList);
  262. }
  263. VOID NTAPI
  264. RtlpInsertFreeBlock(PHEAP Heap,
  265. PHEAP_FREE_ENTRY FreeEntry,
  266. SIZE_T BlockSize)
  267. {
  268. USHORT Size, PreviousSize;
  269. UCHAR SegmentOffset, Flags;
  270. PHEAP_SEGMENT Segment;
  271. DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize);
  272. /* Increase the free size counter */
  273. Heap->TotalFreeSize += BlockSize;
  274. /* Remember certain values */
  275. Flags = FreeEntry->Flags;
  276. PreviousSize = FreeEntry->PreviousSize;
  277. SegmentOffset = FreeEntry->SegmentOffset;
  278. Segment = Heap->Segments[SegmentOffset];
  279. /* Process it */
  280. while (BlockSize)
  281. {
  282. /* Check for the max size */
  283. if (BlockSize > HEAP_MAX_BLOCK_SIZE)
  284. {
  285. Size = HEAP_MAX_BLOCK_SIZE;
  286. /* Special compensation if it goes above limit just by 1 */
  287. if (BlockSize == (HEAP_MAX_BLOCK_SIZE + 1))
  288. Size -= 16;
  289. FreeEntry->Flags = 0;
  290. }
  291. else
  292. {
  293. Size = (USHORT)BlockSize;
  294. FreeEntry->Flags = Flags;
  295. }
  296. /* Change its size and insert it into a free list */
  297. FreeEntry->Size = Size;
  298. FreeEntry->PreviousSize = PreviousSize;
  299. FreeEntry->SegmentOffset = SegmentOffset;
  300. /* Call a helper to actually insert the block */
  301. RtlpInsertFreeBlockHelper(Heap, FreeEntry, Size, FALSE);
  302. /* Update sizes */
  303. PreviousSize = Size;
  304. BlockSize -= Size;
  305. /* Go to the next entry */
  306. FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
  307. /* Check if that's all */
  308. if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) return;
  309. }
  310. /* Update previous size if needed */
  311. if (!(Flags & HEAP_ENTRY_LAST_ENTRY))
  312. FreeEntry->PreviousSize = PreviousSize;
  313. }
  314. VOID NTAPI
  315. RtlpRemoveFreeBlock(PHEAP Heap,
  316. PHEAP_FREE_ENTRY FreeEntry,
  317. BOOLEAN Dedicated,
  318. BOOLEAN NoFill)
  319. {
  320. SIZE_T Result, RealSize;
  321. /* Remove the free block and update the freelists bitmap */
  322. if (RemoveEntryList(&FreeEntry->FreeList) &&
  323. (Dedicated || (!Dedicated && FreeEntry->Size < HEAP_FREELISTS)))
  324. {
  325. RtlpClearFreeListsBit(Heap, FreeEntry);
  326. }
  327. /* Fill with pattern if necessary */
  328. if (!NoFill &&
  329. (FreeEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
  330. {
  331. RealSize = (FreeEntry->Size << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry);
  332. /* Deduct extra stuff from block's real size */
  333. if (FreeEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT &&
  334. RealSize > sizeof(HEAP_FREE_ENTRY_EXTRA))
  335. {
  336. RealSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
  337. }
  338. /* Check if the free filler is intact */
  339. Result = RtlCompareMemoryUlong((PCHAR)(FreeEntry + 1),
  340. RealSize,
  341. ARENA_FREE_FILLER);
  342. if (Result != RealSize)
  343. {
  344. DPRINT1("Free heap block %p modified at %p after it was freed\n",
  345. FreeEntry,
  346. (PCHAR)(FreeEntry + 1) + Result);
  347. }
  348. }
  349. }
  350. SIZE_T NTAPI
  351. RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry)
  352. {
  353. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
  354. /* Get pointer to the containing record */
  355. VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
  356. /* Restore the real size */
  357. return VirtualEntry->CommitSize - HeapEntry->Size;
  358. }
  359. PHEAP_UCR_DESCRIPTOR NTAPI
  360. RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment)
  361. {
  362. PLIST_ENTRY Entry;
  363. PHEAP_UCR_DESCRIPTOR UcrDescriptor;
  364. PHEAP_UCR_SEGMENT UcrSegment;
  365. PHEAP Heap = Segment->Heap;
  366. SIZE_T ReserveSize = 16 * PAGE_SIZE;
  367. SIZE_T CommitSize = 1 * PAGE_SIZE;
  368. NTSTATUS Status;
  369. DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment);
  370. /* Check if we have unused UCRs */
  371. if (IsListEmpty(&Heap->UCRList))
  372. {
  373. /* Get a pointer to the first UCR segment */
  374. UcrSegment = CONTAINING_RECORD(Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry);
  375. /* Check the list of UCR segments */
  376. if (IsListEmpty(&Heap->UCRSegments) ||
  377. UcrSegment->ReservedSize == UcrSegment->CommittedSize)
  378. {
  379. /* We need to create a new one. Reserve 16 pages for it */
  380. UcrSegment = NULL;
  381. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  382. (PVOID *)&UcrSegment,
  383. 0,
  384. &ReserveSize,
  385. MEM_RESERVE,
  386. PAGE_READWRITE);
  387. if (!NT_SUCCESS(Status)) return NULL;
  388. /* Commit one page */
  389. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  390. (PVOID *)&UcrSegment,
  391. 0,
  392. &CommitSize,
  393. MEM_COMMIT,
  394. PAGE_READWRITE);
  395. if (!NT_SUCCESS(Status))
  396. {
  397. /* Release reserved memory */
  398. ZwFreeVirtualMemory(NtCurrentProcess(),
  399. (PVOID *)&UcrDescriptor,
  400. &ReserveSize,
  401. MEM_RELEASE);
  402. return NULL;
  403. }
  404. /* Set it's data */
  405. UcrSegment->ReservedSize = ReserveSize;
  406. UcrSegment->CommittedSize = CommitSize;
  407. /* Add it to the head of the list */
  408. InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry);
  409. /* Get a pointer to the first available UCR descriptor */
  410. UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1);
  411. }
  412. else
  413. {
  414. /* It's possible to use existing UCR segment. Commit one more page */
  415. UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize);
  416. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  417. (PVOID *)&UcrDescriptor,
  418. 0,
  419. &CommitSize,
  420. MEM_COMMIT,
  421. PAGE_READWRITE);
  422. if (!NT_SUCCESS(Status)) return NULL;
  423. /* Update sizes */
  424. UcrSegment->CommittedSize += CommitSize;
  425. }
  426. /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
  427. while ((PCHAR)UcrDescriptor < ((PCHAR)UcrSegment + UcrSegment->CommittedSize))
  428. {
  429. InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry);
  430. UcrDescriptor++;
  431. }
  432. }
  433. /* There are unused UCRs, just get the first one */
  434. Entry = RemoveHeadList(&Heap->UCRList);
  435. UcrDescriptor = CONTAINING_RECORD(Entry, HEAP_UCR_DESCRIPTOR, ListEntry);
  436. return UcrDescriptor;
  437. }
  438. VOID NTAPI
  439. RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment,
  440. PHEAP_UCR_DESCRIPTOR UcrDescriptor)
  441. {
  442. /* Zero it out */
  443. UcrDescriptor->Address = NULL;
  444. UcrDescriptor->Size = 0;
  445. /* Put it into the heap's list of unused UCRs */
  446. InsertHeadList(&Segment->Heap->UCRList, &UcrDescriptor->ListEntry);
  447. }
  448. VOID NTAPI
  449. RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment,
  450. ULONG_PTR Address,
  451. SIZE_T Size)
  452. {
  453. PLIST_ENTRY Current;
  454. PHEAP_UCR_DESCRIPTOR UcrDescriptor;
  455. DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment, Address, Size);
  456. /* Go through the list of UCR descriptors, they are sorted from lowest address
  457. to the highest */
  458. Current = Segment->UCRSegmentList.Flink;
  459. while(Current != &Segment->UCRSegmentList)
  460. {
  461. UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
  462. if ((ULONG_PTR)UcrDescriptor->Address > Address)
  463. {
  464. /* Check for a really lucky case */
  465. if ((Address + Size) == (ULONG_PTR)UcrDescriptor->Address)
  466. {
  467. /* Exact match */
  468. UcrDescriptor->Address = (PVOID)Address;
  469. UcrDescriptor->Size += Size;
  470. return;
  471. }
  472. /* We found the block after which the new one should go */
  473. break;
  474. }
  475. else if (((ULONG_PTR)UcrDescriptor->Address + UcrDescriptor->Size) == Address)
  476. {
  477. /* Modify this entry */
  478. Address = (ULONG_PTR)UcrDescriptor->Address;
  479. Size += UcrDescriptor->Size;
  480. /* Advance to the next descriptor */
  481. Current = Current->Flink;
  482. /* Remove the current descriptor from the list and destroy it */
  483. RemoveEntryList(&UcrDescriptor->SegmentEntry);
  484. RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
  485. Segment->NumberOfUnCommittedRanges--;
  486. }
  487. else
  488. {
  489. /* Advance to the next descriptor */
  490. Current = Current->Flink;
  491. }
  492. }
  493. /* Create a new UCR descriptor */
  494. UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
  495. if (!UcrDescriptor) return;
  496. UcrDescriptor->Address = (PVOID)Address;
  497. UcrDescriptor->Size = Size;
  498. /* "Current" is the descriptor after which our one should go */
  499. InsertTailList(Current, &UcrDescriptor->SegmentEntry);
  500. DPRINT("Added segment UCR with base %p, size 0x%x\n", Address, Size);
  501. /* Increase counters */
  502. Segment->NumberOfUnCommittedRanges++;
  503. }
  504. PHEAP_FREE_ENTRY NTAPI
  505. RtlpFindAndCommitPages(PHEAP Heap,
  506. PHEAP_SEGMENT Segment,
  507. PSIZE_T Size,
  508. PVOID AddressRequested)
  509. {
  510. PLIST_ENTRY Current;
  511. ULONG_PTR Address = 0;
  512. PHEAP_UCR_DESCRIPTOR UcrDescriptor, PreviousUcr = NULL;
  513. PHEAP_ENTRY FirstEntry, LastEntry;
  514. NTSTATUS Status;
  515. DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap, Segment, *Size, Address);
  516. /* Go through UCRs in a segment */
  517. Current = Segment->UCRSegmentList.Flink;
  518. while(Current != &Segment->UCRSegmentList)
  519. {
  520. UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
  521. /* Check if we can use that one right away */
  522. if (UcrDescriptor->Size >= *Size &&
  523. (UcrDescriptor->Address == AddressRequested || !AddressRequested))
  524. {
  525. /* Get the address */
  526. Address = (ULONG_PTR)UcrDescriptor->Address;
  527. /* Commit it */
  528. if (Heap->CommitRoutine)
  529. {
  530. Status = Heap->CommitRoutine(Heap, (PVOID *)&Address, Size);
  531. }
  532. else
  533. {
  534. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  535. (PVOID *)&Address,
  536. 0,
  537. Size,
  538. MEM_COMMIT,
  539. PAGE_READWRITE);
  540. }
  541. DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size, Address, UcrDescriptor->Size);
  542. /* Fail in unsuccessful case */
  543. if (!NT_SUCCESS(Status))
  544. {
  545. DPRINT1("Committing page failed with status 0x%08X\n", Status);
  546. return NULL;
  547. }
  548. /* Update tracking numbers */
  549. Segment->NumberOfUnCommittedPages -= (ULONG)(*Size / PAGE_SIZE);
  550. /* Calculate first and last entries */
  551. FirstEntry = (PHEAP_ENTRY)Address;
  552. /* Go through the entries to find the last one */
  553. if (PreviousUcr)
  554. LastEntry = (PHEAP_ENTRY)((ULONG_PTR)PreviousUcr->Address + PreviousUcr->Size);
  555. else
  556. LastEntry = &Segment->Entry;
  557. while (!(LastEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  558. {
  559. ASSERT(LastEntry->Size != 0);
  560. LastEntry += LastEntry->Size;
  561. }
  562. ASSERT((LastEntry + LastEntry->Size) == FirstEntry);
  563. /* Unmark it as a last entry */
  564. LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
  565. /* Update UCR descriptor */
  566. UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size);
  567. UcrDescriptor->Size -= *Size;
  568. DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
  569. UcrDescriptor, UcrDescriptor->Address, UcrDescriptor->Size);
  570. /* Set various first entry fields */
  571. FirstEntry->SegmentOffset = LastEntry->SegmentOffset;
  572. FirstEntry->Size = (USHORT)(*Size >> HEAP_ENTRY_SHIFT);
  573. FirstEntry->PreviousSize = LastEntry->Size;
  574. /* Check if anything left in this UCR */
  575. if (UcrDescriptor->Size == 0)
  576. {
  577. /* It's fully exhausted */
  578. /* Check if this is the end of the segment */
  579. if(UcrDescriptor->Address == Segment->LastValidEntry)
  580. {
  581. FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  582. }
  583. else
  584. {
  585. FirstEntry->Flags = 0;
  586. /* Update field of next entry */
  587. ASSERT((FirstEntry + FirstEntry->Size)->PreviousSize == 0);
  588. (FirstEntry + FirstEntry->Size)->PreviousSize = FirstEntry->Size;
  589. }
  590. /* This UCR needs to be removed because it became useless */
  591. RemoveEntryList(&UcrDescriptor->SegmentEntry);
  592. RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
  593. Segment->NumberOfUnCommittedRanges--;
  594. }
  595. else
  596. {
  597. FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  598. }
  599. /* We're done */
  600. return (PHEAP_FREE_ENTRY)FirstEntry;
  601. }
  602. /* Advance to the next descriptor */
  603. PreviousUcr = UcrDescriptor;
  604. Current = Current->Flink;
  605. }
  606. return NULL;
  607. }
  608. VOID NTAPI
  609. RtlpDeCommitFreeBlock(PHEAP Heap,
  610. PHEAP_FREE_ENTRY FreeEntry,
  611. SIZE_T Size)
  612. {
  613. PHEAP_SEGMENT Segment;
  614. PHEAP_ENTRY PrecedingInUseEntry = NULL, NextInUseEntry = NULL;
  615. PHEAP_FREE_ENTRY NextFreeEntry;
  616. PHEAP_UCR_DESCRIPTOR UcrDescriptor;
  617. SIZE_T PrecedingSize, NextSize, DecommitSize;
  618. ULONG_PTR DecommitBase;
  619. NTSTATUS Status;
  620. DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size);
  621. /* We can't decommit if there is a commit routine! */
  622. if (Heap->CommitRoutine)
  623. {
  624. /* Just add it back the usual way */
  625. RtlpInsertFreeBlock(Heap, FreeEntry, Size);
  626. return;
  627. }
  628. /* Get the segment */
  629. Segment = Heap->Segments[FreeEntry->SegmentOffset];
  630. /* Get the preceding entry */
  631. DecommitBase = ROUND_UP(FreeEntry, PAGE_SIZE);
  632. PrecedingSize = (PHEAP_ENTRY)DecommitBase - (PHEAP_ENTRY)FreeEntry;
  633. if (PrecedingSize == 1)
  634. {
  635. /* Just 1 heap entry, increase the base/size */
  636. DecommitBase += PAGE_SIZE;
  637. PrecedingSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
  638. }
  639. else if (FreeEntry->PreviousSize &&
  640. (DecommitBase == (ULONG_PTR)FreeEntry))
  641. {
  642. PrecedingInUseEntry = (PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize;
  643. }
  644. /* Get the next entry */
  645. NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
  646. DecommitSize = ROUND_DOWN(NextFreeEntry, PAGE_SIZE);
  647. NextSize = (PHEAP_ENTRY)NextFreeEntry - (PHEAP_ENTRY)DecommitSize;
  648. if (NextSize == 1)
  649. {
  650. /* Just 1 heap entry, increase the size */
  651. DecommitSize -= PAGE_SIZE;
  652. NextSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
  653. }
  654. else if (NextSize == 0 &&
  655. !(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  656. {
  657. NextInUseEntry = (PHEAP_ENTRY)NextFreeEntry;
  658. }
  659. NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry - NextSize);
  660. /* Calculate real decommit size */
  661. if (DecommitSize > DecommitBase)
  662. {
  663. DecommitSize -= DecommitBase;
  664. }
  665. else
  666. {
  667. /* Nothing to decommit */
  668. RtlpInsertFreeBlock(Heap, FreeEntry, Size);
  669. return;
  670. }
  671. /* A decommit is necessary. Create a UCR descriptor */
  672. UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
  673. if (!UcrDescriptor)
  674. {
  675. DPRINT1("HEAP: Failed to create UCR descriptor\n");
  676. RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize);
  677. return;
  678. }
  679. /* Decommit the memory */
  680. Status = ZwFreeVirtualMemory(NtCurrentProcess(),
  681. (PVOID *)&DecommitBase,
  682. &DecommitSize,
  683. MEM_DECOMMIT);
  684. /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
  685. RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
  686. if (!NT_SUCCESS(Status))
  687. {
  688. RtlpInsertFreeBlock(Heap, FreeEntry, Size);
  689. return;
  690. }
  691. /* Insert uncommitted pages */
  692. RtlpInsertUnCommittedPages(Segment, DecommitBase, DecommitSize);
  693. Segment->NumberOfUnCommittedPages += (ULONG)(DecommitSize / PAGE_SIZE);
  694. if (PrecedingSize)
  695. {
  696. /* Adjust size of this free entry and insert it */
  697. FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  698. FreeEntry->Size = (USHORT)PrecedingSize;
  699. Heap->TotalFreeSize += PrecedingSize;
  700. /* Insert it into the free list */
  701. RtlpInsertFreeBlockHelper(Heap, FreeEntry, PrecedingSize, FALSE);
  702. }
  703. else if (PrecedingInUseEntry)
  704. {
  705. /* Adjust preceding in use entry */
  706. PrecedingInUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
  707. }
  708. /* Now the next one */
  709. if (NextSize)
  710. {
  711. /* Adjust size of this free entry and insert it */
  712. NextFreeEntry->Flags = 0;
  713. NextFreeEntry->PreviousSize = 0;
  714. NextFreeEntry->SegmentOffset = Segment->Entry.SegmentOffset;
  715. NextFreeEntry->Size = (USHORT)NextSize;
  716. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry + NextSize))->PreviousSize = (USHORT)NextSize;
  717. Heap->TotalFreeSize += NextSize;
  718. RtlpInsertFreeBlockHelper(Heap, NextFreeEntry, NextSize, FALSE);
  719. }
  720. else if (NextInUseEntry)
  721. {
  722. NextInUseEntry->PreviousSize = 0;
  723. }
  724. }
  725. NTSTATUS
  726. NTAPI
  727. RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
  728. OUT PHEAP_SEGMENT Segment,
  729. IN UCHAR SegmentIndex,
  730. IN ULONG SegmentFlags,
  731. IN SIZE_T SegmentReserve,
  732. IN SIZE_T SegmentCommit)
  733. {
  734. PHEAP_ENTRY HeapEntry;
  735. /* Preconditions */
  736. ASSERT(Heap != NULL);
  737. ASSERT(Segment != NULL);
  738. ASSERT(SegmentCommit >= PAGE_SIZE);
  739. ASSERT(ROUND_DOWN(SegmentCommit, PAGE_SIZE) == SegmentCommit);
  740. ASSERT(SegmentReserve >= SegmentCommit);
  741. ASSERT(ROUND_DOWN(SegmentReserve, PAGE_SIZE) == SegmentReserve);
  742. DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap, Segment, SegmentIndex, SegmentFlags, SegmentReserve, SegmentCommit);
  743. /* Initialise the Heap Entry header if this is not the first Heap Segment */
  744. if ((PHEAP_SEGMENT) (Heap) != Segment)
  745. {
  746. Segment->Entry.Size = ROUND_UP(sizeof(HEAP_SEGMENT), sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
  747. Segment->Entry.Flags = HEAP_ENTRY_BUSY;
  748. Segment->Entry.SmallTagIndex = LOBYTE(Segment->Entry.Size) ^ HIBYTE(Segment->Entry.Size) ^ Segment->Entry.Flags;
  749. Segment->Entry.PreviousSize = 0;
  750. Segment->Entry.SegmentOffset = SegmentIndex;
  751. Segment->Entry.UnusedBytes = 0;
  752. }
  753. /* Sanity check */
  754. ASSERT((Segment->Entry.Size << HEAP_ENTRY_SHIFT) <= PAGE_SIZE);
  755. /* Initialise the Heap Segment header */
  756. Segment->SegmentSignature = HEAP_SEGMENT_SIGNATURE;
  757. Segment->SegmentFlags = SegmentFlags;
  758. Segment->Heap = Heap;
  759. Heap->Segments[SegmentIndex] = Segment;
  760. /* Initialise the Heap Segment location information */
  761. Segment->BaseAddress = Segment;
  762. Segment->NumberOfPages = (ULONG)(SegmentReserve >> PAGE_SHIFT);
  763. /* Initialise the Heap Entries contained within the Heap Segment */
  764. Segment->FirstEntry = &Segment->Entry + Segment->Entry.Size;
  765. Segment->LastValidEntry = (PHEAP_ENTRY)((ULONG_PTR)Segment + SegmentReserve);
  766. if (((SIZE_T)Segment->Entry.Size << HEAP_ENTRY_SHIFT) < SegmentCommit)
  767. {
  768. HeapEntry = Segment->FirstEntry;
  769. /* Prepare a Free Heap Entry header */
  770. HeapEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
  771. HeapEntry->PreviousSize = Segment->Entry.Size;
  772. HeapEntry->SegmentOffset = SegmentIndex;
  773. /* Register the Free Heap Entry */
  774. RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY) HeapEntry, (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size);
  775. }
  776. /* Initialise the Heap Segment UnCommitted Range information */
  777. Segment->NumberOfUnCommittedPages = (ULONG)((SegmentReserve - SegmentCommit) >> PAGE_SHIFT);
  778. Segment->NumberOfUnCommittedRanges = 0;
  779. InitializeListHead(&Segment->UCRSegmentList);
  780. /* Register the UnCommitted Range of the Heap Segment */
  781. if (Segment->NumberOfUnCommittedPages != 0)
  782. RtlpInsertUnCommittedPages(Segment, (ULONG_PTR) (Segment) + SegmentCommit, SegmentReserve - SegmentCommit);
  783. return STATUS_SUCCESS;
  784. }
  785. VOID NTAPI
  786. RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment)
  787. {
  788. NTSTATUS Status;
  789. PVOID BaseAddress;
  790. SIZE_T Size = 0;
  791. /* Make sure it's not user allocated */
  792. if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return;
  793. BaseAddress = Segment->BaseAddress;
  794. DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress);
  795. /* Release virtual memory */
  796. Status = ZwFreeVirtualMemory(NtCurrentProcess(),
  797. &BaseAddress,
  798. &Size,
  799. MEM_RELEASE);
  800. if (!NT_SUCCESS(Status))
  801. {
  802. DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status);
  803. }
  804. }
  805. /* Usermode only! */
  806. VOID NTAPI
  807. RtlpAddHeapToProcessList(PHEAP Heap)
  808. {
  809. PPEB Peb;
  810. /* Get PEB */
  811. Peb = RtlGetCurrentPeb();
  812. /* Acquire the lock */
  813. RtlEnterCriticalSection(&RtlpProcessHeapsListLock);
  814. //_SEH2_TRY {
  815. /* Check if max number of heaps reached */
  816. if (Peb->NumberOfHeaps == Peb->MaximumNumberOfHeaps)
  817. {
  818. // TODO: Handle this case
  819. ASSERT(FALSE);
  820. }
  821. /* Add the heap to the process heaps */
  822. Peb->ProcessHeaps[Peb->NumberOfHeaps] = Heap;
  823. Peb->NumberOfHeaps++;
  824. Heap->ProcessHeapsListIndex = (USHORT)Peb->NumberOfHeaps;
  825. // } _SEH2_FINALLY {
  826. /* Release the lock */
  827. RtlLeaveCriticalSection(&RtlpProcessHeapsListLock);
  828. // } _SEH2_END
  829. }
  830. /* Usermode only! */
  831. VOID NTAPI
  832. RtlpRemoveHeapFromProcessList(PHEAP Heap)
  833. {
  834. PPEB Peb;
  835. PHEAP *Current, *Next;
  836. ULONG Count;
  837. /* Get PEB */
  838. Peb = RtlGetCurrentPeb();
  839. /* Acquire the lock */
  840. RtlEnterCriticalSection(&RtlpProcessHeapsListLock);
  841. /* Check if we don't need anything to do */
  842. if ((Heap->ProcessHeapsListIndex == 0) ||
  843. (Heap->ProcessHeapsListIndex > Peb->NumberOfHeaps) ||
  844. (Peb->NumberOfHeaps == 0))
  845. {
  846. /* Release the lock */
  847. RtlLeaveCriticalSection(&RtlpProcessHeapsListLock);
  848. return;
  849. }
  850. /* The process actually has more than one heap.
  851. Use classic, lernt from university times algorithm for removing an entry
  852. from a static array */
  853. Current = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
  854. Next = Current + 1;
  855. /* How many items we need to shift to the left */
  856. Count = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
  857. /* Move them all in a loop */
  858. while (--Count)
  859. {
  860. /* Copy it and advance next pointer */
  861. *Current = *Next;
  862. /* Update its index */
  863. (*Current)->ProcessHeapsListIndex -= 1;
  864. /* Advance pointers */
  865. Current++;
  866. Next++;
  867. }
  868. /* Decrease total number of heaps */
  869. Peb->NumberOfHeaps--;
  870. /* Zero last unused item */
  871. Peb->ProcessHeaps[Peb->NumberOfHeaps] = NULL;
  872. Heap->ProcessHeapsListIndex = 0;
  873. /* Release the lock */
  874. RtlLeaveCriticalSection(&RtlpProcessHeapsListLock);
  875. }
  876. PHEAP_FREE_ENTRY NTAPI
  877. RtlpCoalesceHeap(PHEAP Heap)
  878. {
  879. UNIMPLEMENTED;
  880. return NULL;
  881. }
  882. PHEAP_FREE_ENTRY NTAPI
  883. RtlpCoalesceFreeBlocks (PHEAP Heap,
  884. PHEAP_FREE_ENTRY FreeEntry,
  885. PSIZE_T FreeSize,
  886. BOOLEAN Remove)
  887. {
  888. PHEAP_FREE_ENTRY CurrentEntry, NextEntry;
  889. /* Get the previous entry */
  890. CurrentEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize);
  891. /* Check it */
  892. if (CurrentEntry != FreeEntry &&
  893. !(CurrentEntry->Flags & HEAP_ENTRY_BUSY) &&
  894. (*FreeSize + CurrentEntry->Size) <= HEAP_MAX_BLOCK_SIZE)
  895. {
  896. ASSERT(FreeEntry->PreviousSize == CurrentEntry->Size);
  897. /* Remove it if asked for */
  898. if (Remove)
  899. {
  900. RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
  901. Heap->TotalFreeSize -= FreeEntry->Size;
  902. /* Remove it only once! */
  903. Remove = FALSE;
  904. }
  905. /* Remove previous entry too */
  906. RtlpRemoveFreeBlock(Heap, CurrentEntry, FALSE, FALSE);
  907. /* Copy flags */
  908. CurrentEntry->Flags = FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
  909. /* Advance FreeEntry and update sizes */
  910. FreeEntry = CurrentEntry;
  911. *FreeSize = *FreeSize + CurrentEntry->Size;
  912. Heap->TotalFreeSize -= CurrentEntry->Size;
  913. FreeEntry->Size = (USHORT)(*FreeSize);
  914. /* Also update previous size if needed */
  915. if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  916. {
  917. ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
  918. }
  919. }
  920. /* Check the next block if it exists */
  921. if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  922. {
  923. NextEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + *FreeSize);
  924. if (!(NextEntry->Flags & HEAP_ENTRY_BUSY) &&
  925. NextEntry->Size + *FreeSize <= HEAP_MAX_BLOCK_SIZE)
  926. {
  927. ASSERT(*FreeSize == NextEntry->PreviousSize);
  928. /* Remove it if asked for */
  929. if (Remove)
  930. {
  931. RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
  932. Heap->TotalFreeSize -= FreeEntry->Size;
  933. }
  934. /* Copy flags */
  935. FreeEntry->Flags = NextEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
  936. /* Remove next entry now */
  937. RtlpRemoveFreeBlock(Heap, NextEntry, FALSE, FALSE);
  938. /* Update sizes */
  939. *FreeSize = *FreeSize + NextEntry->Size;
  940. Heap->TotalFreeSize -= NextEntry->Size;
  941. FreeEntry->Size = (USHORT)(*FreeSize);
  942. /* Also update previous size if needed */
  943. if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  944. {
  945. ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
  946. }
  947. }
  948. }
  949. return FreeEntry;
  950. }
  951. PHEAP_FREE_ENTRY NTAPI
  952. RtlpExtendHeap(PHEAP Heap,
  953. SIZE_T Size)
  954. {
  955. ULONG Pages;
  956. UCHAR Index, EmptyIndex;
  957. SIZE_T FreeSize, CommitSize, ReserveSize;
  958. PHEAP_SEGMENT Segment;
  959. PHEAP_FREE_ENTRY FreeEntry;
  960. NTSTATUS Status;
  961. DPRINT("RtlpExtendHeap(%p %x)\n", Heap, Size);
  962. /* Calculate amount in pages */
  963. Pages = (ULONG)((Size + PAGE_SIZE - 1) / PAGE_SIZE);
  964. FreeSize = Pages * PAGE_SIZE;
  965. DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages, FreeSize);
  966. /* Find an empty segment */
  967. EmptyIndex = HEAP_SEGMENTS;
  968. for (Index = 0; Index < HEAP_SEGMENTS; Index++)
  969. {
  970. Segment = Heap->Segments[Index];
  971. if (Segment) DPRINT("Segment[%d] %p with NOUCP %x\n", Index, Segment, Segment->NumberOfUnCommittedPages);
  972. /* Check if its size suits us */
  973. if (Segment &&
  974. Pages <= Segment->NumberOfUnCommittedPages)
  975. {
  976. DPRINT("This segment is suitable\n");
  977. /* Commit needed amount */
  978. FreeEntry = RtlpFindAndCommitPages(Heap, Segment, &FreeSize, NULL);
  979. /* Coalesce it with adjacent entries */
  980. if (FreeEntry)
  981. {
  982. FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
  983. FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
  984. RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
  985. return FreeEntry;
  986. }
  987. }
  988. else if (!Segment &&
  989. EmptyIndex == HEAP_SEGMENTS)
  990. {
  991. /* Remember the first unused segment index */
  992. EmptyIndex = Index;
  993. }
  994. }
  995. /* No luck, need to grow the heap */
  996. if ((Heap->Flags & HEAP_GROWABLE) &&
  997. (EmptyIndex != HEAP_SEGMENTS))
  998. {
  999. Segment = NULL;
  1000. /* Reserve the memory */
  1001. if ((Size + PAGE_SIZE) <= Heap->SegmentReserve)
  1002. ReserveSize = Heap->SegmentReserve;
  1003. else
  1004. ReserveSize = Size + PAGE_SIZE;
  1005. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1006. (PVOID)&Segment,
  1007. 0,
  1008. &ReserveSize,
  1009. MEM_RESERVE,
  1010. PAGE_READWRITE);
  1011. /* If it failed, retry again with a half division algorithm */
  1012. while (!NT_SUCCESS(Status) &&
  1013. ReserveSize != Size + PAGE_SIZE)
  1014. {
  1015. ReserveSize /= 2;
  1016. if (ReserveSize < (Size + PAGE_SIZE))
  1017. ReserveSize = Size + PAGE_SIZE;
  1018. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1019. (PVOID)&Segment,
  1020. 0,
  1021. &ReserveSize,
  1022. MEM_RESERVE,
  1023. PAGE_READWRITE);
  1024. }
  1025. /* Proceed only if it's success */
  1026. if (NT_SUCCESS(Status))
  1027. {
  1028. Heap->SegmentReserve += ReserveSize;
  1029. /* Now commit the memory */
  1030. if ((Size + PAGE_SIZE) <= Heap->SegmentCommit)
  1031. CommitSize = Heap->SegmentCommit;
  1032. else
  1033. CommitSize = Size + PAGE_SIZE;
  1034. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1035. (PVOID)&Segment,
  1036. 0,
  1037. &CommitSize,
  1038. MEM_COMMIT,
  1039. PAGE_READWRITE);
  1040. DPRINT("Committed %d bytes at base %p\n", CommitSize, Segment);
  1041. /* Initialize heap segment if commit was successful */
  1042. if (NT_SUCCESS(Status))
  1043. Status = RtlpInitializeHeapSegment(Heap, Segment, EmptyIndex, 0, ReserveSize, CommitSize);
  1044. /* If everything worked - cool */
  1045. if (NT_SUCCESS(Status)) return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
  1046. DPRINT1("Committing failed with status 0x%08X\n", Status);
  1047. /* Nope, we failed. Free memory */
  1048. ZwFreeVirtualMemory(NtCurrentProcess(),
  1049. (PVOID)&Segment,
  1050. &ReserveSize,
  1051. MEM_RELEASE);
  1052. }
  1053. else
  1054. {
  1055. DPRINT1("Reserving failed with status 0x%08X\n", Status);
  1056. }
  1057. }
  1058. if (RtlpGetMode() == UserMode)
  1059. {
  1060. /* If coalescing on free is disabled in usermode, then do it here */
  1061. if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)
  1062. {
  1063. FreeEntry = RtlpCoalesceHeap(Heap);
  1064. /* If it's a suitable one - return it */
  1065. if (FreeEntry &&
  1066. FreeEntry->Size >= Size)
  1067. {
  1068. return FreeEntry;
  1069. }
  1070. }
  1071. }
  1072. return NULL;
  1073. }
  1074. /***********************************************************************
  1075. * RtlCreateHeap
  1076. * RETURNS
  1077. * Handle of heap: Success
  1078. * NULL: Failure
  1079. *
  1080. * @implemented
  1081. */
  1082. HANDLE NTAPI
  1083. RtlCreateHeap(ULONG Flags,
  1084. PVOID Addr,
  1085. SIZE_T TotalSize,
  1086. SIZE_T CommitSize,
  1087. PVOID Lock,
  1088. PRTL_HEAP_PARAMETERS Parameters)
  1089. {
  1090. PVOID CommittedAddress = NULL, UncommittedAddress = NULL;
  1091. PHEAP Heap = NULL;
  1092. RTL_HEAP_PARAMETERS SafeParams = {0};
  1093. PPEB Peb;
  1094. ULONG_PTR MaximumUserModeAddress;
  1095. SYSTEM_BASIC_INFORMATION SystemInformation;
  1096. MEMORY_BASIC_INFORMATION MemoryInfo;
  1097. ULONG NtGlobalFlags = RtlGetNtGlobalFlags();
  1098. ULONG HeapSegmentFlags = 0;
  1099. NTSTATUS Status;
  1100. ULONG MaxBlockSize;
  1101. /* Check for a special heap */
  1102. if (RtlpPageHeapEnabled && !Addr && !Lock)
  1103. {
  1104. Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
  1105. if (Heap) return Heap;
  1106. /* Reset a special Parameters == -1 hack */
  1107. if ((ULONG_PTR)Parameters == (ULONG_PTR)-1)
  1108. Parameters = NULL;
  1109. else
  1110. DPRINT1("Enabling page heap failed\n");
  1111. }
  1112. /* Check validation flags */
  1113. if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS) && (Flags & ~HEAP_CREATE_VALID_MASK))
  1114. {
  1115. DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags);
  1116. Flags &= HEAP_CREATE_VALID_MASK;
  1117. }
  1118. /* TODO: Capture parameters, once we decide to use SEH */
  1119. if (!Parameters) Parameters = &SafeParams;
  1120. /* Check global flags */
  1121. if (NtGlobalFlags & FLG_HEAP_DISABLE_COALESCING)
  1122. Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
  1123. if (NtGlobalFlags & FLG_HEAP_ENABLE_FREE_CHECK)
  1124. Flags |= HEAP_FREE_CHECKING_ENABLED;
  1125. if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK)
  1126. Flags |= HEAP_TAIL_CHECKING_ENABLED;
  1127. if (RtlpGetMode() == UserMode)
  1128. {
  1129. /* Also check these flags if in usermode */
  1130. if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL)
  1131. Flags |= HEAP_VALIDATE_ALL_ENABLED;
  1132. if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS)
  1133. Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
  1134. if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB)
  1135. Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
  1136. /* Get PEB */
  1137. Peb = RtlGetCurrentPeb();
  1138. /* Apply defaults for non-set parameters */
  1139. if (!Parameters->SegmentCommit) Parameters->SegmentCommit = Peb->HeapSegmentCommit;
  1140. if (!Parameters->SegmentReserve) Parameters->SegmentReserve = Peb->HeapSegmentReserve;
  1141. if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
  1142. if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
  1143. }
  1144. else
  1145. {
  1146. /* Apply defaults for non-set parameters */
  1147. #if 0
  1148. if (!Parameters->SegmentCommit) Parameters->SegmentCommit = MmHeapSegmentCommit;
  1149. if (!Parameters->SegmentReserve) Parameters->SegmentReserve = MmHeapSegmentReserve;
  1150. if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
  1151. if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
  1152. #endif
  1153. }
  1154. // FIXME: Move to memory manager
  1155. if (!Parameters->SegmentCommit) Parameters->SegmentCommit = PAGE_SIZE * 2;
  1156. if (!Parameters->SegmentReserve) Parameters->SegmentReserve = 1048576;
  1157. if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = PAGE_SIZE;
  1158. if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = 65536;
  1159. /* Get the max um address */
  1160. Status = ZwQuerySystemInformation(SystemBasicInformation,
  1161. &SystemInformation,
  1162. sizeof(SystemInformation),
  1163. NULL);
  1164. if (!NT_SUCCESS(Status))
  1165. {
  1166. DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status);
  1167. return NULL;
  1168. }
  1169. MaximumUserModeAddress = SystemInformation.MaximumUserModeAddress;
  1170. /* Calculate max alloc size */
  1171. if (!Parameters->MaximumAllocationSize)
  1172. Parameters->MaximumAllocationSize = MaximumUserModeAddress - (ULONG_PTR)0x10000 - PAGE_SIZE;
  1173. MaxBlockSize = 0x80000 - PAGE_SIZE;
  1174. if (!Parameters->VirtualMemoryThreshold ||
  1175. Parameters->VirtualMemoryThreshold > MaxBlockSize)
  1176. {
  1177. Parameters->VirtualMemoryThreshold = MaxBlockSize;
  1178. }
  1179. /* Check reserve/commit sizes and set default values */
  1180. if (!CommitSize)
  1181. {
  1182. CommitSize = PAGE_SIZE;
  1183. if (TotalSize)
  1184. TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
  1185. else
  1186. TotalSize = 64 * PAGE_SIZE;
  1187. }
  1188. else
  1189. {
  1190. /* Round up the commit size to be at least the page size */
  1191. CommitSize = ROUND_UP(CommitSize, PAGE_SIZE);
  1192. if (TotalSize)
  1193. TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
  1194. else
  1195. TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE);
  1196. }
  1197. /* Call special heap */
  1198. if (RtlpHeapIsSpecial(Flags))
  1199. return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
  1200. /* Without serialization, a lock makes no sense */
  1201. if ((Flags & HEAP_NO_SERIALIZE) && (Lock != NULL))
  1202. return NULL;
  1203. /* See if we are already provided with an address for the heap */
  1204. if (Addr)
  1205. {
  1206. if (Parameters->CommitRoutine)
  1207. {
  1208. /* There is a commit routine, so no problem here, check params */
  1209. if ((Flags & HEAP_GROWABLE) ||
  1210. !Parameters->InitialCommit ||
  1211. !Parameters->InitialReserve ||
  1212. (Parameters->InitialCommit > Parameters->InitialReserve))
  1213. {
  1214. /* Fail */
  1215. return NULL;
  1216. }
  1217. /* Calculate committed and uncommitted addresses */
  1218. CommittedAddress = Addr;
  1219. UncommittedAddress = (PCHAR)Addr + Parameters->InitialCommit;
  1220. TotalSize = Parameters->InitialReserve;
  1221. /* Zero the initial page ourselves */
  1222. RtlZeroMemory(CommittedAddress, PAGE_SIZE);
  1223. }
  1224. else
  1225. {
  1226. /* Commit routine is absent, so query how much memory caller reserved */
  1227. Status = ZwQueryVirtualMemory(NtCurrentProcess(),
  1228. Addr,
  1229. MemoryBasicInformation,
  1230. &MemoryInfo,
  1231. sizeof(MemoryInfo),
  1232. NULL);
  1233. if (!NT_SUCCESS(Status))
  1234. {
  1235. DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status);
  1236. return NULL;
  1237. }
  1238. /* Validate it */
  1239. if (MemoryInfo.BaseAddress != Addr ||
  1240. MemoryInfo.State == MEM_FREE)
  1241. {
  1242. return NULL;
  1243. }
  1244. /* Validation checks passed, set committed/uncommitted addresses */
  1245. CommittedAddress = Addr;
  1246. /* Check if it's committed or not */
  1247. if (MemoryInfo.State == MEM_COMMIT)
  1248. {
  1249. /* Zero it out because it's already committed */
  1250. RtlZeroMemory(Co

Large files files are truncated, but you can click here to view the full file