PageRenderTime 403ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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(CommittedAddress, PAGE_SIZE);
  1251. /* Calculate uncommitted address value */
  1252. CommitSize = MemoryInfo.RegionSize;
  1253. TotalSize = CommitSize;
  1254. UncommittedAddress = (PCHAR)Addr + CommitSize;
  1255. /* Check if uncommitted address is reserved */
  1256. Status = ZwQueryVirtualMemory(NtCurrentProcess(),
  1257. UncommittedAddress,
  1258. MemoryBasicInformation,
  1259. &MemoryInfo,
  1260. sizeof(MemoryInfo),
  1261. NULL);
  1262. if (NT_SUCCESS(Status) &&
  1263. MemoryInfo.State == MEM_RESERVE)
  1264. {
  1265. /* It is, so add it up to the reserve size */
  1266. TotalSize += MemoryInfo.RegionSize;
  1267. }
  1268. }
  1269. else
  1270. {
  1271. /* It's not committed, inform following code that a commit is necessary */
  1272. CommitSize = PAGE_SIZE;
  1273. UncommittedAddress = Addr;
  1274. }
  1275. }
  1276. /* Mark this as a user-committed mem */
  1277. HeapSegmentFlags = HEAP_USER_ALLOCATED;
  1278. Heap = (PHEAP)Addr;
  1279. }
  1280. else
  1281. {
  1282. /* Check commit routine */
  1283. if (Parameters->CommitRoutine) return NULL;
  1284. /* Reserve memory */
  1285. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1286. (PVOID *)&Heap,
  1287. 0,
  1288. &TotalSize,
  1289. MEM_RESERVE,
  1290. PAGE_READWRITE);
  1291. if (!NT_SUCCESS(Status))
  1292. {
  1293. DPRINT1("Failed to reserve memory with status 0x%08x\n", Status);
  1294. return NULL;
  1295. }
  1296. /* Set base addresses */
  1297. CommittedAddress = Heap;
  1298. UncommittedAddress = Heap;
  1299. }
  1300. /* Check if we need to commit something */
  1301. if (CommittedAddress == UncommittedAddress)
  1302. {
  1303. /* Commit the required size */
  1304. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1305. &CommittedAddress,
  1306. 0,
  1307. &CommitSize,
  1308. MEM_COMMIT,
  1309. PAGE_READWRITE);
  1310. DPRINT("Committed %d bytes at base %p\n", CommitSize, CommittedAddress);
  1311. if (!NT_SUCCESS(Status))
  1312. {
  1313. DPRINT1("Failure, Status 0x%08X\n", Status);
  1314. /* Release memory if it was reserved */
  1315. if (!Addr) ZwFreeVirtualMemory(NtCurrentProcess(),
  1316. (PVOID *)&Heap,
  1317. &TotalSize,
  1318. MEM_RELEASE);
  1319. return NULL;
  1320. }
  1321. /* Calculate new uncommitted address */
  1322. UncommittedAddress = (PCHAR)UncommittedAddress + CommitSize;
  1323. }
  1324. /* Initialize the heap */
  1325. Status = RtlpInitializeHeap(Heap, Flags, Lock, Parameters);
  1326. if (!NT_SUCCESS(Status))
  1327. {
  1328. DPRINT1("Failed to initialize heap (%x)\n", Status);
  1329. return NULL;
  1330. }
  1331. /* Initialize heap's first segment */
  1332. Status = RtlpInitializeHeapSegment(Heap, (PHEAP_SEGMENT) (Heap), 0, HeapSegmentFlags, TotalSize, CommitSize);
  1333. if (!NT_SUCCESS(Status))
  1334. {
  1335. DPRINT1("Failed to initialize heap segment (%x)\n", Status);
  1336. return NULL;
  1337. }
  1338. DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap, CommitSize, TotalSize);
  1339. /* Add heap to process list in case of usermode heap */
  1340. if (RtlpGetMode() == UserMode)
  1341. {
  1342. RtlpAddHeapToProcessList(Heap);
  1343. // FIXME: What about lookasides?
  1344. }
  1345. return Heap;
  1346. }
  1347. /***********************************************************************
  1348. * RtlDestroyHeap
  1349. * RETURNS
  1350. * TRUE: Success
  1351. * FALSE: Failure
  1352. *
  1353. * @implemented
  1354. *
  1355. * RETURNS
  1356. * Success: A NULL HANDLE, if heap is NULL or it was destroyed
  1357. * Failure: The Heap handle, if heap is the process heap.
  1358. */
  1359. HANDLE NTAPI
  1360. RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */
  1361. {
  1362. PHEAP Heap = (PHEAP)HeapPtr;
  1363. PLIST_ENTRY Current;
  1364. PHEAP_UCR_SEGMENT UcrSegment;
  1365. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
  1366. PVOID BaseAddress;
  1367. SIZE_T Size;
  1368. LONG i;
  1369. PHEAP_SEGMENT Segment;
  1370. if (!HeapPtr) return NULL;
  1371. /* Call page heap routine if required */
  1372. if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpPageHeapDestroy(HeapPtr);
  1373. /* Call special heap */
  1374. if (RtlpHeapIsSpecial(Heap->Flags))
  1375. {
  1376. if (!RtlDebugDestroyHeap(Heap)) return HeapPtr;
  1377. }
  1378. /* Check for a process heap */
  1379. if (RtlpGetMode() == UserMode &&
  1380. HeapPtr == NtCurrentPeb()->ProcessHeap) return HeapPtr;
  1381. /* Free up all big allocations */
  1382. Current = Heap->VirtualAllocdBlocks.Flink;
  1383. while (Current != &Heap->VirtualAllocdBlocks)
  1384. {
  1385. VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
  1386. BaseAddress = (PVOID)VirtualEntry;
  1387. Current = Current->Flink;
  1388. Size = 0;
  1389. ZwFreeVirtualMemory(NtCurrentProcess(),
  1390. &BaseAddress,
  1391. &Size,
  1392. MEM_RELEASE);
  1393. }
  1394. /* Delete tags and remove heap from the process heaps list in user mode */
  1395. if (RtlpGetMode() == UserMode)
  1396. {
  1397. // FIXME DestroyTags
  1398. RtlpRemoveHeapFromProcessList(Heap);
  1399. }
  1400. /* Delete the heap lock */
  1401. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  1402. {
  1403. /* Delete it if it wasn't user allocated */
  1404. if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED))
  1405. RtlDeleteHeapLock(Heap->LockVariable);
  1406. /* Clear out the lock variable */
  1407. Heap->LockVariable = NULL;
  1408. }
  1409. /* Free UCR segments if any were created */
  1410. Current = Heap->UCRSegments.Flink;
  1411. while(Current != &Heap->UCRSegments)
  1412. {
  1413. UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry);
  1414. /* Advance to the next descriptor */
  1415. Current = Current->Flink;
  1416. BaseAddress = (PVOID)UcrSegment;
  1417. Size = 0;
  1418. /* Release that memory */
  1419. ZwFreeVirtualMemory(NtCurrentProcess(),
  1420. &BaseAddress,
  1421. &Size,
  1422. MEM_RELEASE);
  1423. }
  1424. /* Go through segments and destroy them */
  1425. for (i = HEAP_SEGMENTS - 1; i >= 0; i--)
  1426. {
  1427. Segment = Heap->Segments[i];
  1428. if (Segment) RtlpDestroyHeapSegment(Segment);
  1429. }
  1430. return NULL;
  1431. }
  1432. PHEAP_ENTRY NTAPI
  1433. RtlpSplitEntry(PHEAP Heap,
  1434. ULONG Flags,
  1435. PHEAP_FREE_ENTRY FreeBlock,
  1436. SIZE_T AllocationSize,
  1437. SIZE_T Index,
  1438. SIZE_T Size)
  1439. {
  1440. PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
  1441. UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
  1442. PHEAP_ENTRY InUseEntry;
  1443. SIZE_T FreeSize;
  1444. /* Add extra flags in case of settable user value feature is requested,
  1445. or there is a tag (small or normal) or there is a request to
  1446. capture stack backtraces */
  1447. if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
  1448. Heap->PseudoTagEntries)
  1449. {
  1450. /* Add flag which means that the entry will have extra stuff attached */
  1451. EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
  1452. /* NB! AllocationSize is already adjusted by RtlAllocateHeap */
  1453. }
  1454. /* Add settable user flags, if any */
  1455. EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
  1456. /* Save flags, update total free size */
  1457. FreeFlags = FreeBlock->Flags;
  1458. Heap->TotalFreeSize -= FreeBlock->Size;
  1459. /* Make this block an in-use one */
  1460. InUseEntry = (PHEAP_ENTRY)FreeBlock;
  1461. InUseEntry->Flags = EntryFlags;
  1462. InUseEntry->SmallTagIndex = 0;
  1463. /* Calculate the extra amount */
  1464. FreeSize = InUseEntry->Size - Index;
  1465. /* Update it's size fields (we don't need their data anymore) */
  1466. InUseEntry->Size = (USHORT)Index;
  1467. InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
  1468. /* If there is something to split - do the split */
  1469. if (FreeSize != 0)
  1470. {
  1471. /* Don't split if resulting entry can't contain any payload data
  1472. (i.e. being just HEAP_ENTRY_SIZE) */
  1473. if (FreeSize == 1)
  1474. {
  1475. /* Increase sizes of the in-use entry */
  1476. InUseEntry->Size++;
  1477. InUseEntry->UnusedBytes += sizeof(HEAP_ENTRY);
  1478. }
  1479. else
  1480. {
  1481. /* Calculate a pointer to the new entry */
  1482. SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
  1483. /* Initialize it */
  1484. SplitBlock->Flags = FreeFlags;
  1485. SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
  1486. SplitBlock->Size = (USHORT)FreeSize;
  1487. SplitBlock->PreviousSize = (USHORT)Index;
  1488. /* Check if it's the last entry */
  1489. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
  1490. {
  1491. /* Insert it to the free list if it's the last entry */
  1492. RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
  1493. Heap->TotalFreeSize += FreeSize;
  1494. }
  1495. else
  1496. {
  1497. /* Not so easy - need to update next's previous size too */
  1498. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  1499. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
  1500. {
  1501. SplitBlock2->PreviousSize = (USHORT)FreeSize;
  1502. RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
  1503. Heap->TotalFreeSize += FreeSize;
  1504. }
  1505. else
  1506. {
  1507. /* Even more complex - the next entry is free, so we can merge them into one! */
  1508. SplitBlock->Flags = SplitBlock2->Flags;
  1509. /* Remove that next entry */
  1510. RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
  1511. /* Update sizes */
  1512. FreeSize += SplitBlock2->Size;
  1513. Heap->TotalFreeSize -= SplitBlock2->Size;
  1514. if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
  1515. {
  1516. /* Insert it back */
  1517. SplitBlock->Size = (USHORT)FreeSize;
  1518. /* Don't forget to update previous size of the next entry! */
  1519. if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
  1520. {
  1521. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  1522. }
  1523. /* Actually insert it */
  1524. RtlpInsertFreeBlockHelper(Heap, SplitBlock, (USHORT)FreeSize, FALSE);
  1525. /* Update total size */
  1526. Heap->TotalFreeSize += FreeSize;
  1527. }
  1528. else
  1529. {
  1530. /* Resulting block is quite big */
  1531. RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
  1532. }
  1533. }
  1534. }
  1535. /* Reset flags of the free entry */
  1536. FreeFlags = 0;
  1537. }
  1538. }
  1539. /* Set last entry flag */
  1540. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
  1541. InUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
  1542. return InUseEntry;
  1543. }
  1544. PVOID NTAPI
  1545. RtlpAllocateNonDedicated(PHEAP Heap,
  1546. ULONG Flags,
  1547. SIZE_T Size,
  1548. SIZE_T AllocationSize,
  1549. SIZE_T Index,
  1550. BOOLEAN HeapLocked)
  1551. {
  1552. PLIST_ENTRY FreeListHead, Next;
  1553. PHEAP_FREE_ENTRY FreeBlock;
  1554. PHEAP_ENTRY InUseEntry;
  1555. PHEAP_ENTRY_EXTRA Extra;
  1556. EXCEPTION_RECORD ExceptionRecord;
  1557. /* Go through the zero list to find a place where to insert the new entry */
  1558. FreeListHead = &Heap->FreeLists[0];
  1559. /* Start from the largest block to reduce time */
  1560. Next = FreeListHead->Blink;
  1561. if (FreeListHead != Next)
  1562. {
  1563. FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
  1564. if (FreeBlock->Size >= Index)
  1565. {
  1566. /* Our request is smaller than the largest entry in the zero list */
  1567. /* Go through the list to find insertion place */
  1568. Next = FreeListHead->Flink;
  1569. while (FreeListHead != Next)
  1570. {
  1571. FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
  1572. if (FreeBlock->Size >= Index)
  1573. {
  1574. /* Found minimally fitting entry. Proceed to either using it as it is
  1575. or splitting it to two entries */
  1576. RemoveEntryList(&FreeBlock->FreeList);
  1577. /* Split it */
  1578. InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
  1579. /* Release the lock */
  1580. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1581. /* Zero memory if that was requested */
  1582. if (Flags & HEAP_ZERO_MEMORY)
  1583. RtlZeroMemory(InUseEntry + 1, Size);
  1584. else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  1585. {
  1586. /* Fill this block with a special pattern */
  1587. RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
  1588. }
  1589. /* Fill tail of the block with a special pattern too if requested */
  1590. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
  1591. {
  1592. RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
  1593. InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
  1594. }
  1595. /* Prepare extra if it's present */
  1596. if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  1597. {
  1598. Extra = RtlpGetExtraStuffPointer(InUseEntry);
  1599. RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
  1600. // TODO: Tagging
  1601. }
  1602. /* Return pointer to the */
  1603. return InUseEntry + 1;
  1604. }
  1605. /* Advance to the next entry */
  1606. Next = Next->Flink;
  1607. }
  1608. }
  1609. }
  1610. /* Extend the heap, 0 list didn't have anything suitable */
  1611. FreeBlock = RtlpExtendHeap(Heap, AllocationSize);
  1612. /* Use the new biggest entry we've got */
  1613. if (FreeBlock)
  1614. {
  1615. RemoveEntryList(&FreeBlock->FreeList);
  1616. /* Split it */
  1617. InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
  1618. /* Release the lock */
  1619. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1620. /* Zero memory if that was requested */
  1621. if (Flags & HEAP_ZERO_MEMORY)
  1622. RtlZeroMemory(InUseEntry + 1, Size);
  1623. else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  1624. {
  1625. /* Fill this block with a special pattern */
  1626. RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
  1627. }
  1628. /* Fill tail of the block with a special pattern too if requested */
  1629. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
  1630. {
  1631. RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
  1632. InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
  1633. }
  1634. /* Prepare extra if it's present */
  1635. if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  1636. {
  1637. Extra = RtlpGetExtraStuffPointer(InUseEntry);
  1638. RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
  1639. // TODO: Tagging
  1640. }
  1641. /* Return pointer to the */
  1642. return InUseEntry + 1;
  1643. }
  1644. /* Really unfortunate, out of memory condition */
  1645. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
  1646. /* Generate an exception */
  1647. if (Flags & HEAP_GENERATE_EXCEPTIONS)
  1648. {
  1649. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  1650. ExceptionRecord.ExceptionRecord = NULL;
  1651. ExceptionRecord.NumberParameters = 1;
  1652. ExceptionRecord.ExceptionFlags = 0;
  1653. ExceptionRecord.ExceptionInformation[0] = AllocationSize;
  1654. RtlRaiseException(&ExceptionRecord);
  1655. }
  1656. /* Release the lock */
  1657. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1658. DPRINT1("HEAP: Allocation failed!\n");
  1659. DPRINT1("Flags %x\n", Heap->Flags);
  1660. return NULL;
  1661. }
  1662. /***********************************************************************
  1663. * HeapAlloc (KERNEL32.334)
  1664. * RETURNS
  1665. * Pointer to allocated memory block
  1666. * NULL: Failure
  1667. * 0x7d030f60--invalid flags in RtlHeapAllocate
  1668. * @implemented
  1669. */
  1670. PVOID NTAPI
  1671. RtlAllocateHeap(IN PVOID HeapPtr,
  1672. IN ULONG Flags,
  1673. IN SIZE_T Size)
  1674. {
  1675. PHEAP Heap = (PHEAP)HeapPtr;
  1676. PULONG FreeListsInUse;
  1677. ULONG FreeListsInUseUlong;
  1678. SIZE_T AllocationSize;
  1679. SIZE_T Index, InUseIndex, i;
  1680. PLIST_ENTRY FreeListHead;
  1681. PHEAP_ENTRY InUseEntry;
  1682. PHEAP_FREE_ENTRY FreeBlock;
  1683. UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
  1684. EXCEPTION_RECORD ExceptionRecord;
  1685. BOOLEAN HeapLocked = FALSE;
  1686. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock = NULL;
  1687. PHEAP_ENTRY_EXTRA Extra;
  1688. NTSTATUS Status;
  1689. /* Force flags */
  1690. Flags |= Heap->ForceFlags;
  1691. /* Call special heap */
  1692. if (RtlpHeapIsSpecial(Flags))
  1693. return RtlDebugAllocateHeap(Heap, Flags, Size);
  1694. /* Check for the maximum size */
  1695. if (Size >= 0x80000000)
  1696. {
  1697. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
  1698. DPRINT1("HEAP: Allocation failed!\n");
  1699. return NULL;
  1700. }
  1701. if (Flags & (HEAP_CREATE_ENABLE_TRACING |
  1702. HEAP_CREATE_ALIGN_16))
  1703. {
  1704. DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags);
  1705. }
  1706. //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
  1707. /* Calculate allocation size and index */
  1708. if (Size)
  1709. AllocationSize = Size;
  1710. else
  1711. AllocationSize = 1;
  1712. AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
  1713. /* Add extra flags in case of settable user value feature is requested,
  1714. or there is a tag (small or normal) or there is a request to
  1715. capture stack backtraces */
  1716. if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
  1717. Heap->PseudoTagEntries)
  1718. {
  1719. /* Add flag which means that the entry will have extra stuff attached */
  1720. EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
  1721. /* Account for extra stuff size */
  1722. AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
  1723. }
  1724. /* Add settable user flags, if any */
  1725. EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
  1726. Index = AllocationSize >> HEAP_ENTRY_SHIFT;
  1727. /* Acquire the lock if necessary */
  1728. if (!(Flags & HEAP_NO_SERIALIZE))
  1729. {
  1730. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  1731. HeapLocked = TRUE;
  1732. }
  1733. /* Depending on the size, the allocation is going to be done from dedicated,
  1734. non-dedicated lists or a virtual block of memory */
  1735. if (Index < HEAP_FREELISTS)
  1736. {
  1737. FreeListHead = &Heap->FreeLists[Index];
  1738. if (!IsListEmpty(FreeListHead))
  1739. {
  1740. /* There is a free entry in this list */
  1741. FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
  1742. HEAP_FREE_ENTRY,
  1743. FreeList);
  1744. /* Save flags and remove the free entry */
  1745. FreeFlags = FreeBlock->Flags;
  1746. RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
  1747. /* Update the total free size of the heap */
  1748. Heap->TotalFreeSize -= Index;
  1749. /* Initialize this block */
  1750. InUseEntry = (PHEAP_ENTRY)FreeBlock;
  1751. InUseEntry->Flags = EntryFlags | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
  1752. InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
  1753. InUseEntry->SmallTagIndex = 0;
  1754. }
  1755. else
  1756. {
  1757. /* Find smallest free block which this request could fit in */
  1758. InUseIndex = Index >> 5;
  1759. FreeListsInUse = &Heap->u.FreeListsInUseUlong[InUseIndex];
  1760. /* This bit magic disables all sizes which are less than the requested allocation size */
  1761. FreeListsInUseUlong = *FreeListsInUse++ & ~((1 << ((ULONG)Index & 0x1f)) - 1);
  1762. /* If size is definitily more than our lists - go directly to the non-dedicated one */
  1763. if (InUseIndex > 3)
  1764. return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
  1765. /* Go through the list */
  1766. for (i = InUseIndex; i < 4; i++)
  1767. {
  1768. if (FreeListsInUseUlong)
  1769. {
  1770. FreeListHead = &Heap->FreeLists[i * 32];
  1771. break;
  1772. }
  1773. if (i < 3) FreeListsInUseUlong = *FreeListsInUse++;
  1774. }
  1775. /* Nothing found, search in the non-dedicated list */
  1776. if (i == 4)
  1777. return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
  1778. /* That list is found, now calculate exact block */
  1779. FreeListHead += RtlpFindLeastSetBit(FreeListsInUseUlong);
  1780. /* Take this entry and remove it from the list of free blocks */
  1781. FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
  1782. HEAP_FREE_ENTRY,
  1783. FreeList);
  1784. RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
  1785. /* Split it */
  1786. InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
  1787. }
  1788. /* Release the lock */
  1789. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1790. /* Zero memory if that was requested */
  1791. if (Flags & HEAP_ZERO_MEMORY)
  1792. RtlZeroMemory(InUseEntry + 1, Size);
  1793. else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  1794. {
  1795. /* Fill this block with a special pattern */
  1796. RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
  1797. }
  1798. /* Fill tail of the block with a special pattern too if requested */
  1799. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
  1800. {
  1801. RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
  1802. InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
  1803. }
  1804. /* Prepare extra if it's present */
  1805. if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  1806. {
  1807. Extra = RtlpGetExtraStuffPointer(InUseEntry);
  1808. RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
  1809. // TODO: Tagging
  1810. }
  1811. /* User data starts right after the entry's header */
  1812. return InUseEntry + 1;
  1813. }
  1814. else if (Index <= Heap->VirtualMemoryThreshold)
  1815. {
  1816. /* The block is too large for dedicated lists, but fine for a non-dedicated one */
  1817. return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
  1818. }
  1819. else if (Heap->Flags & HEAP_GROWABLE)
  1820. {
  1821. /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
  1822. AllocationSize += sizeof(HEAP_VIRTUAL_ALLOC_ENTRY) - sizeof(HEAP_ENTRY);
  1823. Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
  1824. (PVOID *)&VirtualBlock,
  1825. 0,
  1826. &AllocationSize,
  1827. MEM_COMMIT,
  1828. PAGE_READWRITE);
  1829. if (!NT_SUCCESS(Status))
  1830. {
  1831. // Set STATUS!
  1832. /* Release the lock */
  1833. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1834. DPRINT1("HEAP: Allocation failed!\n");
  1835. return NULL;
  1836. }
  1837. /* Initialize the newly allocated block */
  1838. VirtualBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
  1839. VirtualBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT;
  1840. VirtualBlock->CommitSize = AllocationSize;
  1841. VirtualBlock->ReserveSize = AllocationSize;
  1842. /* Insert it into the list of virtual allocations */
  1843. InsertTailList(&Heap->VirtualAllocdBlocks, &VirtualBlock->Entry);
  1844. /* Release the lock */
  1845. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1846. /* Return pointer to user data */
  1847. return VirtualBlock + 1;
  1848. }
  1849. /* Generate an exception */
  1850. if (Flags & HEAP_GENERATE_EXCEPTIONS)
  1851. {
  1852. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  1853. ExceptionRecord.ExceptionRecord = NULL;
  1854. ExceptionRecord.NumberParameters = 1;
  1855. ExceptionRecord.ExceptionFlags = 0;
  1856. ExceptionRecord.ExceptionInformation[0] = AllocationSize;
  1857. RtlRaiseException(&ExceptionRecord);
  1858. }
  1859. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL);
  1860. /* Release the lock */
  1861. if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
  1862. DPRINT1("HEAP: Allocation failed!\n");
  1863. return NULL;
  1864. }
  1865. /***********************************************************************
  1866. * HeapFree (KERNEL32.338)
  1867. * RETURNS
  1868. * TRUE: Success
  1869. * FALSE: Failure
  1870. *
  1871. * @implemented
  1872. */
  1873. BOOLEAN NTAPI RtlFreeHeap(
  1874. HANDLE HeapPtr, /* [in] Handle of heap */
  1875. ULONG Flags, /* [in] Heap freeing flags */
  1876. PVOID Ptr /* [in] Address of memory to free */
  1877. )
  1878. {
  1879. PHEAP Heap;
  1880. PHEAP_ENTRY HeapEntry;
  1881. USHORT TagIndex = 0;
  1882. SIZE_T BlockSize;
  1883. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
  1884. BOOLEAN Locked = FALSE;
  1885. NTSTATUS Status;
  1886. /* Freeing NULL pointer is a legal operation */
  1887. if (!Ptr) return TRUE;
  1888. /* Get pointer to the heap and force flags */
  1889. Heap = (PHEAP)HeapPtr;
  1890. Flags |= Heap->ForceFlags;
  1891. /* Call special heap */
  1892. if (RtlpHeapIsSpecial(Flags))
  1893. return RtlDebugFreeHeap(Heap, Flags, Ptr);
  1894. /* Lock if necessary */
  1895. if (!(Flags & HEAP_NO_SERIALIZE))
  1896. {
  1897. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  1898. Locked = TRUE;
  1899. }
  1900. /* Get pointer to the heap entry */
  1901. HeapEntry = (PHEAP_ENTRY)Ptr - 1;
  1902. /* Check this entry, fail if it's invalid */
  1903. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY) ||
  1904. (((ULONG_PTR)Ptr & 0x7) != 0) ||
  1905. (HeapEntry->SegmentOffset >= HEAP_SEGMENTS))
  1906. {
  1907. /* This is an invalid block */
  1908. DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr);
  1909. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  1910. /* Release the heap lock */
  1911. if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
  1912. return FALSE;
  1913. }
  1914. if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  1915. {
  1916. /* Big allocation */
  1917. VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
  1918. /* Remove it from the list */
  1919. RemoveEntryList(&VirtualEntry->Entry);
  1920. // TODO: Tagging
  1921. BlockSize = 0;
  1922. Status = ZwFreeVirtualMemory(NtCurrentProcess(),
  1923. (PVOID *)&VirtualEntry,
  1924. &BlockSize,
  1925. MEM_RELEASE);
  1926. if (!NT_SUCCESS(Status))
  1927. {
  1928. DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
  1929. Status, Heap, Ptr, VirtualEntry);
  1930. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
  1931. }
  1932. }
  1933. else
  1934. {
  1935. /* Normal allocation */
  1936. BlockSize = HeapEntry->Size;
  1937. // TODO: Tagging
  1938. /* Coalesce in kernel mode, and in usermode if it's not disabled */
  1939. if (RtlpGetMode() == KernelMode ||
  1940. (RtlpGetMode() == UserMode && !(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)))
  1941. {
  1942. HeapEntry = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks(Heap,
  1943. (PHEAP_FREE_ENTRY)HeapEntry,
  1944. &BlockSize,
  1945. FALSE);
  1946. }
  1947. /* If there is no need to decommit the block - put it into a free list */
  1948. if (BlockSize < Heap->DeCommitFreeBlockThreshold ||
  1949. (Heap->TotalFreeSize + BlockSize < Heap->DeCommitTotalFreeThreshold))
  1950. {
  1951. /* Check if it needs to go to a 0 list */
  1952. if (BlockSize > HEAP_MAX_BLOCK_SIZE)
  1953. {
  1954. /* General-purpose 0 list */
  1955. RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
  1956. }
  1957. else
  1958. {
  1959. /* Usual free list */
  1960. RtlpInsertFreeBlockHelper(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize, FALSE);
  1961. /* Assert sizes are consistent */
  1962. if (!(HeapEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
  1963. {
  1964. ASSERT((HeapEntry + BlockSize)->PreviousSize == BlockSize);
  1965. }
  1966. /* Increase the free size */
  1967. Heap->TotalFreeSize += BlockSize;
  1968. }
  1969. if (RtlpGetMode() == UserMode &&
  1970. TagIndex != 0)
  1971. {
  1972. // FIXME: Tagging
  1973. UNIMPLEMENTED;
  1974. }
  1975. }
  1976. else
  1977. {
  1978. /* Decommit this block */
  1979. RtlpDeCommitFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
  1980. }
  1981. }
  1982. /* Release the heap lock */
  1983. if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
  1984. return TRUE;
  1985. }
  1986. BOOLEAN NTAPI
  1987. RtlpGrowBlockInPlace (IN PHEAP Heap,
  1988. IN ULONG Flags,
  1989. IN PHEAP_ENTRY InUseEntry,
  1990. IN SIZE_T Size,
  1991. IN SIZE_T Index)
  1992. {
  1993. UCHAR EntryFlags, RememberFlags;
  1994. PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry;
  1995. SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0;
  1996. PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
  1997. /* We can't grow beyond specified threshold */
  1998. if (Index > Heap->VirtualMemoryThreshold)
  1999. return FALSE;
  2000. /* Get entry flags */
  2001. EntryFlags = InUseEntry->Flags;
  2002. /* Get the next free entry */
  2003. FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
  2004. if (EntryFlags & HEAP_ENTRY_LAST_ENTRY)
  2005. {
  2006. /* There is no next block, just uncommitted space. Calculate how much is needed */
  2007. FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT;
  2008. FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
  2009. /* Find and commit those pages */
  2010. FreeEntry = RtlpFindAndCommitPages(Heap,
  2011. Heap->Segments[InUseEntry->SegmentOffset],
  2012. &FreeSize,
  2013. FreeEntry);
  2014. /* Fail if it failed... */
  2015. if (!FreeEntry) return FALSE;
  2016. /* It was successful, perform coalescing */
  2017. FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
  2018. FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
  2019. /* Check if it's enough */
  2020. if (FreeSize + InUseEntry->Size < Index)
  2021. {
  2022. /* Still not enough */
  2023. RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
  2024. Heap->TotalFreeSize += FreeSize;
  2025. return FALSE;
  2026. }
  2027. /* Remember flags of this free entry */
  2028. RememberFlags = FreeEntry->Flags;
  2029. /* Sum up sizes */
  2030. FreeSize += InUseEntry->Size;
  2031. }
  2032. else
  2033. {
  2034. /* The next block indeed exists. Check if it's free or in use */
  2035. if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
  2036. /* Next entry is free, check if it can fit the block we need */
  2037. FreeSize = InUseEntry->Size + FreeEntry->Size;
  2038. if (FreeSize < Index) return FALSE;
  2039. /* Remember flags of this free entry */
  2040. RememberFlags = FreeEntry->Flags;
  2041. /* Remove this block from the free list */
  2042. RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
  2043. Heap->TotalFreeSize -= FreeEntry->Size;
  2044. }
  2045. PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
  2046. FreeSize -= Index;
  2047. /* Don't produce too small blocks */
  2048. if (FreeSize <= 2)
  2049. {
  2050. Index += FreeSize;
  2051. FreeSize = 0;
  2052. }
  2053. /* Process extra stuff */
  2054. if (RememberFlags & HEAP_ENTRY_EXTRA_PRESENT)
  2055. {
  2056. /* Calculate pointers */
  2057. OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
  2058. NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
  2059. /* Copy contents */
  2060. *NewExtra = *OldExtra;
  2061. // FIXME Tagging
  2062. }
  2063. /* Update sizes */
  2064. InUseEntry->Size = (USHORT)Index;
  2065. InUseEntry->UnusedBytes = (UCHAR)((Index << HEAP_ENTRY_SHIFT) - Size);
  2066. /* Check if there is a free space remaining after merging those blocks */
  2067. if (!FreeSize)
  2068. {
  2069. /* Update flags and sizes */
  2070. InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
  2071. /* Either update previous size of the next entry or mark it as a last
  2072. entry in the segment*/
  2073. if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
  2074. (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size;
  2075. }
  2076. else
  2077. {
  2078. /* Complex case, we need to split the block to give unused free space
  2079. back to the heap */
  2080. UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
  2081. UnusedEntry->PreviousSize = (USHORT)Index;
  2082. UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
  2083. /* Update the following block or set the last entry in the segment */
  2084. if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
  2085. {
  2086. /* Set flags and size */
  2087. UnusedEntry->Flags = RememberFlags;
  2088. UnusedEntry->Size = (USHORT)FreeSize;
  2089. /* Insert it to the heap and update total size */
  2090. RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
  2091. Heap->TotalFreeSize += FreeSize;
  2092. }
  2093. else
  2094. {
  2095. /* There is a block after this one */
  2096. FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
  2097. if (FollowingEntry->Flags & HEAP_ENTRY_BUSY)
  2098. {
  2099. /* Update flags and set size of the unused space entry */
  2100. UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY);
  2101. UnusedEntry->Size = (USHORT)FreeSize;
  2102. /* Update previous size of the following entry */
  2103. FollowingEntry->PreviousSize = (USHORT)FreeSize;
  2104. /* Insert it to the heap and update total free size */
  2105. RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
  2106. Heap->TotalFreeSize += FreeSize;
  2107. }
  2108. else
  2109. {
  2110. /* That following entry is also free, what a fortune! */
  2111. RememberFlags = FollowingEntry->Flags;
  2112. /* Remove it */
  2113. RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE, FALSE);
  2114. Heap->TotalFreeSize -= FollowingEntry->Size;
  2115. /* And make up a new combined block */
  2116. FreeSize += FollowingEntry->Size;
  2117. UnusedEntry->Flags = RememberFlags;
  2118. /* Check where to put it */
  2119. if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
  2120. {
  2121. /* Fine for a dedicated list */
  2122. UnusedEntry->Size = (USHORT)FreeSize;
  2123. if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
  2124. ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = (USHORT)FreeSize;
  2125. /* Insert it back and update total size */
  2126. RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
  2127. Heap->TotalFreeSize += FreeSize;
  2128. }
  2129. else
  2130. {
  2131. /* The block is very large, leave all the hassle to the insertion routine */
  2132. RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize);
  2133. }
  2134. }
  2135. }
  2136. }
  2137. /* Properly "zero out" (and fill!) the space */
  2138. if (Flags & HEAP_ZERO_MEMORY)
  2139. {
  2140. RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize);
  2141. }
  2142. else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  2143. {
  2144. /* Calculate tail part which we need to fill */
  2145. TailPart = PrevSize & (sizeof(ULONG) - 1);
  2146. /* "Invert" it as usual */
  2147. if (TailPart) TailPart = 4 - TailPart;
  2148. if (Size > (PrevSize + TailPart))
  2149. AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
  2150. if (AddedSize)
  2151. {
  2152. RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart,
  2153. AddedSize,
  2154. ARENA_INUSE_FILLER);
  2155. }
  2156. }
  2157. /* Fill the new tail */
  2158. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
  2159. {
  2160. RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
  2161. HEAP_ENTRY_SIZE,
  2162. HEAP_TAIL_FILL);
  2163. }
  2164. /* Copy user settable flags */
  2165. InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
  2166. InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
  2167. /* Return success */
  2168. return TRUE;
  2169. }
  2170. PHEAP_ENTRY_EXTRA NTAPI
  2171. RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry)
  2172. {
  2173. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
  2174. /* Check if it's a big block */
  2175. if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2176. {
  2177. VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
  2178. /* Return a pointer to the extra stuff*/
  2179. return &VirtualEntry->ExtraStuff;
  2180. }
  2181. else
  2182. {
  2183. /* This is a usual entry, which means extra stuff follows this block */
  2184. return (PHEAP_ENTRY_EXTRA)(HeapEntry + HeapEntry->Size - 1);
  2185. }
  2186. }
  2187. /***********************************************************************
  2188. * RtlReAllocateHeap
  2189. * PARAMS
  2190. * Heap [in] Handle of heap block
  2191. * Flags [in] Heap reallocation flags
  2192. * Ptr, [in] Address of memory to reallocate
  2193. * Size [in] Number of bytes to reallocate
  2194. *
  2195. * RETURNS
  2196. * Pointer to reallocated memory block
  2197. * NULL: Failure
  2198. * 0x7d030f60--invalid flags in RtlHeapAllocate
  2199. * @implemented
  2200. */
  2201. PVOID NTAPI
  2202. RtlReAllocateHeap(HANDLE HeapPtr,
  2203. ULONG Flags,
  2204. PVOID Ptr,
  2205. SIZE_T Size)
  2206. {
  2207. PHEAP Heap = (PHEAP)HeapPtr;
  2208. PHEAP_ENTRY InUseEntry, NewInUseEntry;
  2209. PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
  2210. SIZE_T AllocationSize, FreeSize, DecommitSize;
  2211. BOOLEAN HeapLocked = FALSE;
  2212. PVOID NewBaseAddress;
  2213. PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
  2214. SIZE_T OldSize, Index, OldIndex;
  2215. UCHAR FreeFlags;
  2216. NTSTATUS Status;
  2217. PVOID DecommitBase;
  2218. SIZE_T RemainderBytes, ExtraSize;
  2219. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2220. EXCEPTION_RECORD ExceptionRecord;
  2221. /* Return success in case of a null pointer */
  2222. if (!Ptr)
  2223. {
  2224. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS);
  2225. return NULL;
  2226. }
  2227. /* Force heap flags */
  2228. Flags |= Heap->ForceFlags;
  2229. /* Call special heap */
  2230. if (RtlpHeapIsSpecial(Flags))
  2231. return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size);
  2232. /* Make sure size is valid */
  2233. if (Size >= 0x80000000)
  2234. {
  2235. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
  2236. return NULL;
  2237. }
  2238. /* Calculate allocation size and index */
  2239. if (Size)
  2240. AllocationSize = Size;
  2241. else
  2242. AllocationSize = 1;
  2243. AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
  2244. /* Add up extra stuff, if it is present anywhere */
  2245. if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) ||
  2246. (Flags & HEAP_EXTRA_FLAGS_MASK) ||
  2247. Heap->PseudoTagEntries)
  2248. {
  2249. AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
  2250. }
  2251. /* Acquire the lock if necessary */
  2252. if (!(Flags & HEAP_NO_SERIALIZE))
  2253. {
  2254. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  2255. HeapLocked = TRUE;
  2256. Flags &= ~HEAP_NO_SERIALIZE;
  2257. }
  2258. /* Get the pointer to the in-use entry */
  2259. InUseEntry = (PHEAP_ENTRY)Ptr - 1;
  2260. /* If that entry is not really in-use, we have a problem */
  2261. if (!(InUseEntry->Flags & HEAP_ENTRY_BUSY))
  2262. {
  2263. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  2264. /* Release the lock and return */
  2265. if (HeapLocked)
  2266. RtlLeaveHeapLock(Heap->LockVariable);
  2267. return Ptr;
  2268. }
  2269. if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2270. {
  2271. /* This is a virtually allocated block. Get its size */
  2272. OldSize = RtlpGetSizeOfBigBlock(InUseEntry);
  2273. /* Convert it to an index */
  2274. OldIndex = (OldSize + InUseEntry->Size) >> HEAP_ENTRY_SHIFT;
  2275. /* Calculate new allocation size and round it to the page size */
  2276. AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
  2277. AllocationSize = ROUND_UP(AllocationSize, PAGE_SIZE);
  2278. }
  2279. else
  2280. {
  2281. /* Usual entry */
  2282. OldIndex = InUseEntry->Size;
  2283. OldSize = (OldIndex << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
  2284. }
  2285. /* Calculate new index */
  2286. Index = AllocationSize >> HEAP_ENTRY_SHIFT;
  2287. /* Check for 4 different scenarios (old size, new size, old index, new index) */
  2288. if (Index <= OldIndex)
  2289. {
  2290. /* Difference must be greater than 1, adjust if it's not so */
  2291. if (Index + 1 == OldIndex)
  2292. {
  2293. Index++;
  2294. AllocationSize += sizeof(HEAP_ENTRY);
  2295. }
  2296. /* Calculate new size */
  2297. if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2298. {
  2299. /* Simple in case of a virtual alloc - just an unused size */
  2300. InUseEntry->Size = (USHORT)(AllocationSize - Size);
  2301. }
  2302. else if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  2303. {
  2304. /* There is extra stuff, take it into account */
  2305. OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
  2306. NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
  2307. *NewExtra = *OldExtra;
  2308. // FIXME Tagging, TagIndex
  2309. /* Update unused bytes count */
  2310. InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
  2311. }
  2312. else
  2313. {
  2314. // FIXME Tagging, SmallTagIndex
  2315. InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
  2316. }
  2317. /* If new size is bigger than the old size */
  2318. if (Size > OldSize)
  2319. {
  2320. /* Zero out that additional space if required */
  2321. if (Flags & HEAP_ZERO_MEMORY)
  2322. {
  2323. RtlZeroMemory((PCHAR)Ptr + OldSize, Size - OldSize);
  2324. }
  2325. else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
  2326. {
  2327. /* Fill it on free if required */
  2328. RemainderBytes = OldSize & (sizeof(ULONG) - 1);
  2329. if (RemainderBytes)
  2330. RemainderBytes = 4 - RemainderBytes;
  2331. if (Size > (OldSize + RemainderBytes))
  2332. {
  2333. /* Calculate actual amount of extra bytes to fill */
  2334. ExtraSize = (Size - (OldSize + RemainderBytes)) & ~(sizeof(ULONG) - 1);
  2335. /* Fill them if there are any */
  2336. if (ExtraSize != 0)
  2337. {
  2338. RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + OldSize + RemainderBytes,
  2339. ExtraSize,
  2340. ARENA_INUSE_FILLER);
  2341. }
  2342. }
  2343. }
  2344. }
  2345. /* Fill tail of the heap entry if required */
  2346. if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
  2347. {
  2348. RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
  2349. HEAP_ENTRY_SIZE,
  2350. HEAP_TAIL_FILL);
  2351. }
  2352. /* Check if the difference is significant or not */
  2353. if (Index != OldIndex)
  2354. {
  2355. /* Save flags */
  2356. FreeFlags = InUseEntry->Flags & ~HEAP_ENTRY_BUSY;
  2357. if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2358. {
  2359. /* This is a virtual block allocation */
  2360. VirtualAllocBlock = CONTAINING_RECORD(InUseEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
  2361. // FIXME Tagging!
  2362. DecommitBase = (PCHAR)VirtualAllocBlock + AllocationSize;
  2363. DecommitSize = (OldIndex << HEAP_ENTRY_SHIFT) - AllocationSize;
  2364. /* Release the memory */
  2365. Status = ZwFreeVirtualMemory(NtCurrentProcess(),
  2366. (PVOID *)&DecommitBase,
  2367. &DecommitSize,
  2368. MEM_RELEASE);
  2369. if (!NT_SUCCESS(Status))
  2370. {
  2371. DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase, DecommitSize, Status);
  2372. }
  2373. else
  2374. {
  2375. /* Otherwise reduce the commit size */
  2376. VirtualAllocBlock->CommitSize -= DecommitSize;
  2377. }
  2378. }
  2379. else
  2380. {
  2381. /* Reduce size of the block and possibly split it */
  2382. SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
  2383. /* Initialize this entry */
  2384. SplitBlock->Flags = FreeFlags;
  2385. SplitBlock->PreviousSize = (USHORT)Index;
  2386. SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
  2387. /* Remember free size */
  2388. FreeSize = InUseEntry->Size - Index;
  2389. /* Set new size */
  2390. InUseEntry->Size = (USHORT)Index;
  2391. InUseEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
  2392. /* Is that the last entry */
  2393. if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
  2394. {
  2395. /* Set its size and insert it to the list */
  2396. SplitBlock->Size = (USHORT)FreeSize;
  2397. RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
  2398. /* Update total free size */
  2399. Heap->TotalFreeSize += FreeSize;
  2400. }
  2401. else
  2402. {
  2403. /* Get the block after that one */
  2404. SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
  2405. if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
  2406. {
  2407. /* It's in use, add it here*/
  2408. SplitBlock->Size = (USHORT)FreeSize;
  2409. /* Update previous size of the next entry */
  2410. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  2411. /* Insert it to the list */
  2412. RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
  2413. /* Update total size */
  2414. Heap->TotalFreeSize += FreeSize;
  2415. }
  2416. else
  2417. {
  2418. /* Next entry is free, so merge with it */
  2419. SplitBlock->Flags = SplitBlock2->Flags;
  2420. /* Remove it, update total size */
  2421. RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
  2422. Heap->TotalFreeSize -= SplitBlock2->Size;
  2423. /* Calculate total free size */
  2424. FreeSize += SplitBlock2->Size;
  2425. if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
  2426. {
  2427. SplitBlock->Size = (USHORT)FreeSize;
  2428. if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
  2429. {
  2430. /* Update previous size of the next entry */
  2431. ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
  2432. }
  2433. /* Insert the new one back and update total size */
  2434. RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
  2435. Heap->TotalFreeSize += FreeSize;
  2436. }
  2437. else
  2438. {
  2439. /* Just add it */
  2440. RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
  2441. }
  2442. }
  2443. }
  2444. }
  2445. }
  2446. }
  2447. else
  2448. {
  2449. /* We're growing the block */
  2450. if ((InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
  2451. !RtlpGrowBlockInPlace(Heap, Flags, InUseEntry, Size, Index))
  2452. {
  2453. /* Growing in place failed, so growing out of place */
  2454. if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
  2455. {
  2456. DPRINT1("Realloc in place failed, but it was the only option\n");
  2457. Ptr = NULL;
  2458. }
  2459. else
  2460. {
  2461. /* Clear tag bits */
  2462. Flags &= ~HEAP_TAG_MASK;
  2463. /* Process extra stuff */
  2464. if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  2465. {
  2466. /* Preserve user settable flags */
  2467. Flags &= ~HEAP_SETTABLE_USER_FLAGS;
  2468. Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
  2469. /* Get pointer to the old extra data */
  2470. OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
  2471. /* Save tag index if it was set */
  2472. if (OldExtra->TagIndex &&
  2473. !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG))
  2474. {
  2475. Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT;
  2476. }
  2477. }
  2478. else if (InUseEntry->SmallTagIndex)
  2479. {
  2480. /* Take small tag index into account */
  2481. Flags |= InUseEntry->SmallTagIndex << HEAP_TAG_SHIFT;
  2482. }
  2483. /* Allocate new block from the heap */
  2484. NewBaseAddress = RtlAllocateHeap(HeapPtr,
  2485. Flags & ~HEAP_ZERO_MEMORY,
  2486. Size);
  2487. /* Proceed if it didn't fail */
  2488. if (NewBaseAddress)
  2489. {
  2490. /* Get new entry pointer */
  2491. NewInUseEntry = (PHEAP_ENTRY)NewBaseAddress - 1;
  2492. /* Process extra stuff if it exists */
  2493. if (NewInUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  2494. {
  2495. NewExtra = RtlpGetExtraStuffPointer(NewInUseEntry);
  2496. if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  2497. {
  2498. OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
  2499. NewExtra->Settable = OldExtra->Settable;
  2500. }
  2501. else
  2502. {
  2503. RtlZeroMemory(NewExtra, sizeof(*NewExtra));
  2504. }
  2505. }
  2506. /* Copy actual user bits */
  2507. if (Size < OldSize)
  2508. RtlMoveMemory(NewBaseAddress, Ptr, Size);
  2509. else
  2510. RtlMoveMemory(NewBaseAddress, Ptr, OldSize);
  2511. /* Zero remaining part if required */
  2512. if (Size > OldSize &&
  2513. (Flags & HEAP_ZERO_MEMORY))
  2514. {
  2515. RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize);
  2516. }
  2517. /* Free the old block */
  2518. RtlFreeHeap(HeapPtr, Flags, Ptr);
  2519. }
  2520. Ptr = NewBaseAddress;
  2521. }
  2522. }
  2523. }
  2524. /* Did resizing fail? */
  2525. if (!Ptr && (Flags & HEAP_GENERATE_EXCEPTIONS))
  2526. {
  2527. /* Generate an exception if required */
  2528. ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
  2529. ExceptionRecord.ExceptionRecord = NULL;
  2530. ExceptionRecord.NumberParameters = 1;
  2531. ExceptionRecord.ExceptionFlags = 0;
  2532. ExceptionRecord.ExceptionInformation[0] = AllocationSize;
  2533. RtlRaiseException(&ExceptionRecord);
  2534. }
  2535. /* Release the heap lock if it was acquired */
  2536. if (HeapLocked)
  2537. RtlLeaveHeapLock(Heap->LockVariable);
  2538. return Ptr;
  2539. }
  2540. /***********************************************************************
  2541. * RtlCompactHeap
  2542. *
  2543. * @unimplemented
  2544. */
  2545. ULONG NTAPI
  2546. RtlCompactHeap(HANDLE Heap,
  2547. ULONG Flags)
  2548. {
  2549. UNIMPLEMENTED;
  2550. return 0;
  2551. }
  2552. /***********************************************************************
  2553. * RtlLockHeap
  2554. * Attempts to acquire the critical section object for a specified heap.
  2555. *
  2556. * PARAMS
  2557. * Heap [in] Handle of heap to lock for exclusive access
  2558. *
  2559. * RETURNS
  2560. * TRUE: Success
  2561. * FALSE: Failure
  2562. *
  2563. * @implemented
  2564. */
  2565. BOOLEAN NTAPI
  2566. RtlLockHeap(IN HANDLE HeapPtr)
  2567. {
  2568. PHEAP Heap = (PHEAP)HeapPtr;
  2569. // FIXME Check for special heap
  2570. /* Check if it's really a heap */
  2571. if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
  2572. /* Lock if it's lockable */
  2573. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  2574. {
  2575. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  2576. }
  2577. return TRUE;
  2578. }
  2579. /***********************************************************************
  2580. * RtlUnlockHeap
  2581. * Releases ownership of the critical section object.
  2582. *
  2583. * PARAMS
  2584. * Heap [in] Handle to the heap to unlock
  2585. *
  2586. * RETURNS
  2587. * TRUE: Success
  2588. * FALSE: Failure
  2589. *
  2590. * @implemented
  2591. */
  2592. BOOLEAN NTAPI
  2593. RtlUnlockHeap(HANDLE HeapPtr)
  2594. {
  2595. PHEAP Heap = (PHEAP)HeapPtr;
  2596. // FIXME Check for special heap
  2597. /* Check if it's really a heap */
  2598. if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
  2599. /* Unlock if it's lockable */
  2600. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  2601. {
  2602. RtlLeaveHeapLock(Heap->LockVariable);
  2603. }
  2604. return TRUE;
  2605. }
  2606. /***********************************************************************
  2607. * RtlSizeHeap
  2608. * PARAMS
  2609. * Heap [in] Handle of heap
  2610. * Flags [in] Heap size control flags
  2611. * Ptr [in] Address of memory to return size for
  2612. *
  2613. * RETURNS
  2614. * Size in bytes of allocated memory
  2615. * 0xffffffff: Failure
  2616. *
  2617. * @implemented
  2618. */
  2619. SIZE_T NTAPI
  2620. RtlSizeHeap(
  2621. HANDLE HeapPtr,
  2622. ULONG Flags,
  2623. PVOID Ptr
  2624. )
  2625. {
  2626. PHEAP Heap = (PHEAP)HeapPtr;
  2627. PHEAP_ENTRY HeapEntry;
  2628. SIZE_T EntrySize;
  2629. // FIXME This is a hack around missing SEH support!
  2630. if (!Heap)
  2631. {
  2632. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE);
  2633. return (SIZE_T)-1;
  2634. }
  2635. /* Force flags */
  2636. Flags |= Heap->ForceFlags;
  2637. /* Call special heap */
  2638. if (RtlpHeapIsSpecial(Flags))
  2639. return RtlDebugSizeHeap(Heap, Flags, Ptr);
  2640. /* Get the heap entry pointer */
  2641. HeapEntry = (PHEAP_ENTRY)Ptr - 1;
  2642. /* Return -1 if that entry is free */
  2643. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
  2644. {
  2645. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  2646. return (SIZE_T)-1;
  2647. }
  2648. /* Get size of this block depending if it's a usual or a big one */
  2649. if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2650. {
  2651. EntrySize = RtlpGetSizeOfBigBlock(HeapEntry);
  2652. }
  2653. else
  2654. {
  2655. /* Calculate it */
  2656. EntrySize = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
  2657. }
  2658. /* Return calculated size */
  2659. return EntrySize;
  2660. }
  2661. BOOLEAN NTAPI
  2662. RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry)
  2663. {
  2664. SIZE_T Size, Result;
  2665. PCHAR TailPart;
  2666. /* Calculate size */
  2667. if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
  2668. Size = RtlpGetSizeOfBigBlock(HeapEntry);
  2669. else
  2670. Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
  2671. /* Calculate pointer to the tail part of the block */
  2672. TailPart = (PCHAR)(HeapEntry + 1) + Size;
  2673. /* Compare tail pattern */
  2674. Result = RtlCompareMemory(TailPart,
  2675. FillPattern,
  2676. HEAP_ENTRY_SIZE);
  2677. if (Result != HEAP_ENTRY_SIZE)
  2678. {
  2679. DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result);
  2680. return FALSE;
  2681. }
  2682. /* All is fine */
  2683. return TRUE;
  2684. }
  2685. BOOLEAN NTAPI
  2686. RtlpValidateHeapHeaders(
  2687. PHEAP Heap,
  2688. BOOLEAN Recalculate)
  2689. {
  2690. // We skip header validation for now
  2691. return TRUE;
  2692. }
  2693. BOOLEAN NTAPI
  2694. RtlpValidateHeapEntry(
  2695. PHEAP Heap,
  2696. PHEAP_ENTRY HeapEntry)
  2697. {
  2698. BOOLEAN BigAllocation, EntryFound = FALSE;
  2699. PHEAP_SEGMENT Segment;
  2700. ULONG SegmentOffset;
  2701. /* Perform various consistency checks of this entry */
  2702. if (!HeapEntry) goto invalid_entry;
  2703. if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry;
  2704. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry;
  2705. BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC;
  2706. Segment = Heap->Segments[HeapEntry->SegmentOffset];
  2707. if (BigAllocation &&
  2708. (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock)))
  2709. goto invalid_entry;
  2710. if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS ||
  2711. !Segment ||
  2712. HeapEntry < Segment->FirstEntry ||
  2713. HeapEntry >= Segment->LastValidEntry))
  2714. goto invalid_entry;
  2715. if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) &&
  2716. !RtlpCheckInUsePattern(HeapEntry))
  2717. goto invalid_entry;
  2718. /* Checks are done, if this is a virtual entry, that's all */
  2719. if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE;
  2720. /* Go through segments and check if this entry fits into any of them */
  2721. for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
  2722. {
  2723. Segment = Heap->Segments[SegmentOffset];
  2724. if (!Segment) continue;
  2725. if ((HeapEntry >= Segment->FirstEntry) &&
  2726. (HeapEntry < Segment->LastValidEntry))
  2727. {
  2728. /* Got it */
  2729. EntryFound = TRUE;
  2730. break;
  2731. }
  2732. }
  2733. /* Return our result of finding entry in the segments */
  2734. return EntryFound;
  2735. invalid_entry:
  2736. DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap);
  2737. return FALSE;
  2738. }
  2739. BOOLEAN NTAPI
  2740. RtlpValidateHeapSegment(
  2741. PHEAP Heap,
  2742. PHEAP_SEGMENT Segment,
  2743. UCHAR SegmentOffset,
  2744. PULONG FreeEntriesCount,
  2745. PSIZE_T TotalFreeSize,
  2746. PSIZE_T TagEntries,
  2747. PSIZE_T PseudoTagEntries)
  2748. {
  2749. PHEAP_UCR_DESCRIPTOR UcrDescriptor;
  2750. PLIST_ENTRY UcrEntry;
  2751. SIZE_T ByteSize, Size, Result;
  2752. PHEAP_ENTRY CurrentEntry;
  2753. ULONG UnCommittedPages;
  2754. ULONG UnCommittedRanges;
  2755. ULONG PreviousSize;
  2756. UnCommittedPages = 0;
  2757. UnCommittedRanges = 0;
  2758. if (IsListEmpty(&Segment->UCRSegmentList))
  2759. {
  2760. UcrEntry = NULL;
  2761. UcrDescriptor = NULL;
  2762. }
  2763. else
  2764. {
  2765. UcrEntry = Segment->UCRSegmentList.Flink;
  2766. UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
  2767. }
  2768. if (Segment->BaseAddress == Heap)
  2769. CurrentEntry = &Heap->Entry;
  2770. else
  2771. CurrentEntry = &Segment->Entry;
  2772. while (CurrentEntry < Segment->LastValidEntry)
  2773. {
  2774. if (UcrDescriptor &&
  2775. ((PVOID)CurrentEntry >= UcrDescriptor->Address))
  2776. {
  2777. DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
  2778. CurrentEntry, UcrDescriptor->Address,
  2779. (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
  2780. return FALSE;
  2781. }
  2782. PreviousSize = 0;
  2783. while (CurrentEntry < Segment->LastValidEntry)
  2784. {
  2785. if (PreviousSize != CurrentEntry->PreviousSize)
  2786. {
  2787. DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
  2788. CurrentEntry, CurrentEntry->PreviousSize, PreviousSize);
  2789. return FALSE;
  2790. }
  2791. PreviousSize = CurrentEntry->Size;
  2792. Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT;
  2793. if (CurrentEntry->Flags & HEAP_ENTRY_BUSY)
  2794. {
  2795. if (TagEntries)
  2796. {
  2797. UNIMPLEMENTED;
  2798. }
  2799. /* Check fill pattern */
  2800. if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)
  2801. {
  2802. if (!RtlpCheckInUsePattern(CurrentEntry))
  2803. return FALSE;
  2804. }
  2805. }
  2806. else
  2807. {
  2808. /* The entry is free, increase free entries count and total free size */
  2809. *FreeEntriesCount = *FreeEntriesCount + 1;
  2810. *TotalFreeSize += CurrentEntry->Size;
  2811. if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) &&
  2812. (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
  2813. {
  2814. ByteSize = Size - sizeof(HEAP_FREE_ENTRY);
  2815. if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) &&
  2816. (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA)))
  2817. {
  2818. ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
  2819. }
  2820. Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1),
  2821. ByteSize,
  2822. ARENA_FREE_FILLER);
  2823. if (Result != ByteSize)
  2824. {
  2825. DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
  2826. CurrentEntry,
  2827. (PCHAR)(CurrentEntry + 1) + Result);
  2828. return FALSE;
  2829. }
  2830. }
  2831. }
  2832. if (CurrentEntry->SegmentOffset != SegmentOffset)
  2833. {
  2834. DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset);
  2835. return FALSE;
  2836. }
  2837. /* Check if it's the last entry */
  2838. if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY)
  2839. {
  2840. CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
  2841. if (!UcrDescriptor)
  2842. {
  2843. /* Check if it's not really the last one */
  2844. if (CurrentEntry != Segment->LastValidEntry)
  2845. {
  2846. DPRINT1("HEAP: Heap entry %p is not last block in segment (%x)\n", CurrentEntry, Segment->LastValidEntry);
  2847. return FALSE;
  2848. }
  2849. }
  2850. else if (CurrentEntry != UcrDescriptor->Address)
  2851. {
  2852. DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
  2853. CurrentEntry, UcrDescriptor->Address);
  2854. return FALSE;
  2855. }
  2856. else
  2857. {
  2858. UnCommittedPages += (ULONG)(UcrDescriptor->Size / PAGE_SIZE);
  2859. UnCommittedRanges++;
  2860. CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
  2861. /* Go to the next UCR descriptor */
  2862. UcrEntry = UcrEntry->Flink;
  2863. if (UcrEntry == &Segment->UCRSegmentList)
  2864. {
  2865. UcrEntry = NULL;
  2866. UcrDescriptor = NULL;
  2867. }
  2868. else
  2869. {
  2870. UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
  2871. }
  2872. }
  2873. break;
  2874. }
  2875. /* Advance to the next entry */
  2876. CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
  2877. }
  2878. }
  2879. /* Check total numbers of UCP and UCR */
  2880. if (Segment->NumberOfUnCommittedPages != UnCommittedPages)
  2881. {
  2882. DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
  2883. Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages);
  2884. return FALSE;
  2885. }
  2886. if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges)
  2887. {
  2888. DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
  2889. Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges);
  2890. return FALSE;
  2891. }
  2892. return TRUE;
  2893. }
  2894. BOOLEAN NTAPI
  2895. RtlpValidateHeap(PHEAP Heap,
  2896. BOOLEAN ForceValidation)
  2897. {
  2898. PHEAP_SEGMENT Segment;
  2899. BOOLEAN EmptyList;
  2900. UCHAR SegmentOffset;
  2901. SIZE_T Size, TotalFreeSize;
  2902. ULONG PreviousSize;
  2903. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  2904. PLIST_ENTRY ListHead, NextEntry;
  2905. PHEAP_FREE_ENTRY FreeEntry;
  2906. ULONG FreeBlocksCount, FreeListEntriesCount;
  2907. /* Check headers */
  2908. if (!RtlpValidateHeapHeaders(Heap, FALSE))
  2909. return FALSE;
  2910. /* Skip validation if it's not needed */
  2911. if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED))
  2912. return TRUE;
  2913. /* Check free lists bitmaps */
  2914. FreeListEntriesCount = 0;
  2915. ListHead = &Heap->FreeLists[0];
  2916. for (Size = 0; Size < HEAP_FREELISTS; Size++)
  2917. {
  2918. if (Size)
  2919. {
  2920. /* This is a dedicated list. Check if it's empty */
  2921. EmptyList = IsListEmpty(ListHead);
  2922. if (Heap->u.FreeListsInUseBytes[Size >> 3] & (1 << (Size & 7)))
  2923. {
  2924. if (EmptyList)
  2925. {
  2926. DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size);
  2927. return FALSE;
  2928. }
  2929. }
  2930. else
  2931. {
  2932. if (!EmptyList)
  2933. {
  2934. DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size);
  2935. return FALSE;
  2936. }
  2937. }
  2938. }
  2939. /* Now check this list entries */
  2940. NextEntry = ListHead->Flink;
  2941. PreviousSize = 0;
  2942. while (ListHead != NextEntry)
  2943. {
  2944. FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList);
  2945. NextEntry = NextEntry->Flink;
  2946. /* If there is an in-use entry in a free list - that's quite a big problem */
  2947. if (FreeEntry->Flags & HEAP_ENTRY_BUSY)
  2948. {
  2949. DPRINT1("HEAP: %x-dedicated list free element %x is marked in-use\n", Size, FreeEntry);
  2950. return FALSE;
  2951. }
  2952. /* Check sizes according to that specific list's size */
  2953. if ((Size == 0) && (FreeEntry->Size < HEAP_FREELISTS))
  2954. {
  2955. DPRINT1("HEAP: Non dedicated list free element %x has size %x which would fit a dedicated list\n", FreeEntry, FreeEntry->Size);
  2956. return FALSE;
  2957. }
  2958. else if (Size && (FreeEntry->Size != Size))
  2959. {
  2960. DPRINT1("HEAP: %x-dedicated list free element %x has incorrect size %x\n", Size, FreeEntry, FreeEntry->Size);
  2961. return FALSE;
  2962. }
  2963. else if ((Size == 0) && (FreeEntry->Size < PreviousSize))
  2964. {
  2965. DPRINT1("HEAP: Non dedicated list free element %x is not put in order\n", FreeEntry);
  2966. return FALSE;
  2967. }
  2968. /* Remember previous size*/
  2969. PreviousSize = FreeEntry->Size;
  2970. /* Add up to the total amount of free entries */
  2971. FreeListEntriesCount++;
  2972. }
  2973. /* Go to the head of the next free list */
  2974. ListHead++;
  2975. }
  2976. /* Check big allocations */
  2977. ListHead = &Heap->VirtualAllocdBlocks;
  2978. NextEntry = ListHead->Flink;
  2979. while (ListHead != NextEntry)
  2980. {
  2981. VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
  2982. /* We can only check the fill pattern */
  2983. if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN)
  2984. {
  2985. if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock))
  2986. return FALSE;
  2987. }
  2988. NextEntry = NextEntry->Flink;
  2989. }
  2990. /* Check all segments */
  2991. FreeBlocksCount = 0;
  2992. TotalFreeSize = 0;
  2993. for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
  2994. {
  2995. Segment = Heap->Segments[SegmentOffset];
  2996. /* Go to the next one if there is no segment */
  2997. if (!Segment) continue;
  2998. if (!RtlpValidateHeapSegment(Heap,
  2999. Segment,
  3000. SegmentOffset,
  3001. &FreeBlocksCount,
  3002. &TotalFreeSize,
  3003. NULL,
  3004. NULL))
  3005. {
  3006. return FALSE;
  3007. }
  3008. }
  3009. if (FreeListEntriesCount != FreeBlocksCount)
  3010. {
  3011. DPRINT1("HEAP: Free blocks count in arena (%d) does not match free blocks number in the free lists (%d)\n", FreeBlocksCount, FreeListEntriesCount);
  3012. return FALSE;
  3013. }
  3014. if (Heap->TotalFreeSize != TotalFreeSize)
  3015. {
  3016. DPRINT1("HEAP: Total size of free blocks in arena (%d) does not equal to the one in heap header (%d)\n", TotalFreeSize, Heap->TotalFreeSize);
  3017. return FALSE;
  3018. }
  3019. return TRUE;
  3020. }
  3021. /***********************************************************************
  3022. * RtlValidateHeap
  3023. * Validates a specified heap.
  3024. *
  3025. * PARAMS
  3026. * Heap [in] Handle to the heap
  3027. * Flags [in] Bit flags that control access during operation
  3028. * Block [in] Optional pointer to memory block to validate
  3029. *
  3030. * NOTES
  3031. * Flags is ignored.
  3032. *
  3033. * RETURNS
  3034. * TRUE: Success
  3035. * FALSE: Failure
  3036. *
  3037. * @implemented
  3038. */
  3039. BOOLEAN NTAPI RtlValidateHeap(
  3040. HANDLE HeapPtr,
  3041. ULONG Flags,
  3042. PVOID Block
  3043. )
  3044. {
  3045. PHEAP Heap = (PHEAP)HeapPtr;
  3046. BOOLEAN HeapLocked = FALSE;
  3047. BOOLEAN HeapValid;
  3048. /* Check for page heap */
  3049. if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
  3050. return RtlpDebugPageHeapValidate(HeapPtr, Flags, Block);
  3051. /* Check signature */
  3052. if (Heap->Signature != HEAP_SIGNATURE)
  3053. {
  3054. DPRINT1("HEAP: Signature %x is invalid for heap %p\n", Heap->Signature, Heap);
  3055. return FALSE;
  3056. }
  3057. /* Force flags */
  3058. Flags = Heap->ForceFlags;
  3059. /* Acquire the lock if necessary */
  3060. if (!(Flags & HEAP_NO_SERIALIZE))
  3061. {
  3062. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  3063. HeapLocked = TRUE;
  3064. }
  3065. /* Either validate whole heap or just one entry */
  3066. if (!Block)
  3067. HeapValid = RtlpValidateHeap(Heap, TRUE);
  3068. else
  3069. HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1);
  3070. /* Unlock if it's lockable */
  3071. if (HeapLocked)
  3072. {
  3073. RtlLeaveHeapLock(Heap->LockVariable);
  3074. }
  3075. return HeapValid;
  3076. }
  3077. VOID
  3078. RtlInitializeHeapManager(VOID)
  3079. {
  3080. PPEB Peb;
  3081. /* Get PEB */
  3082. Peb = RtlGetCurrentPeb();
  3083. /* Initialize heap-related fields of PEB */
  3084. Peb->NumberOfHeaps = 0;
  3085. /* Initialize the process heaps list protecting lock */
  3086. RtlInitializeCriticalSection(&RtlpProcessHeapsListLock);
  3087. }
  3088. /*
  3089. * @implemented
  3090. */
  3091. NTSTATUS NTAPI
  3092. RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine,
  3093. PVOID lParam)
  3094. {
  3095. UNIMPLEMENTED;
  3096. return STATUS_NOT_IMPLEMENTED;
  3097. }
  3098. /*
  3099. * @implemented
  3100. */
  3101. ULONG NTAPI
  3102. RtlGetProcessHeaps(ULONG count,
  3103. HANDLE *heaps)
  3104. {
  3105. UNIMPLEMENTED;
  3106. return 0;
  3107. }
  3108. /*
  3109. * @implemented
  3110. */
  3111. BOOLEAN NTAPI
  3112. RtlValidateProcessHeaps(VOID)
  3113. {
  3114. UNIMPLEMENTED;
  3115. return TRUE;
  3116. }
  3117. /*
  3118. * @unimplemented
  3119. */
  3120. BOOLEAN NTAPI
  3121. RtlZeroHeap(
  3122. IN PVOID HeapHandle,
  3123. IN ULONG Flags
  3124. )
  3125. {
  3126. UNIMPLEMENTED;
  3127. return FALSE;
  3128. }
  3129. /*
  3130. * @implemented
  3131. */
  3132. BOOLEAN
  3133. NTAPI
  3134. RtlSetUserValueHeap(IN PVOID HeapHandle,
  3135. IN ULONG Flags,
  3136. IN PVOID BaseAddress,
  3137. IN PVOID UserValue)
  3138. {
  3139. PHEAP Heap = (PHEAP)HeapHandle;
  3140. PHEAP_ENTRY HeapEntry;
  3141. PHEAP_ENTRY_EXTRA Extra;
  3142. BOOLEAN HeapLocked = FALSE, ValueSet = FALSE;
  3143. /* Force flags */
  3144. Flags |= Heap->Flags;
  3145. /* Call special heap */
  3146. if (RtlpHeapIsSpecial(Flags))
  3147. return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue);
  3148. /* Lock if it's lockable */
  3149. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  3150. {
  3151. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  3152. HeapLocked = TRUE;
  3153. }
  3154. /* Get a pointer to the entry */
  3155. HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
  3156. /* If it's a free entry - return error */
  3157. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
  3158. {
  3159. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  3160. /* Release the heap lock if it was acquired */
  3161. if (HeapLocked)
  3162. RtlLeaveHeapLock(Heap->LockVariable);
  3163. return FALSE;
  3164. }
  3165. /* Check if this entry has an extra stuff associated with it */
  3166. if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  3167. {
  3168. /* Use extra to store the value */
  3169. Extra = RtlpGetExtraStuffPointer(HeapEntry);
  3170. Extra->Settable = (ULONG_PTR)UserValue;
  3171. /* Indicate that value was set */
  3172. ValueSet = TRUE;
  3173. }
  3174. /* Release the heap lock if it was acquired */
  3175. if (HeapLocked)
  3176. RtlLeaveHeapLock(Heap->LockVariable);
  3177. return ValueSet;
  3178. }
  3179. /*
  3180. * @implemented
  3181. */
  3182. BOOLEAN
  3183. NTAPI
  3184. RtlSetUserFlagsHeap(IN PVOID HeapHandle,
  3185. IN ULONG Flags,
  3186. IN PVOID BaseAddress,
  3187. IN ULONG UserFlagsReset,
  3188. IN ULONG UserFlagsSet)
  3189. {
  3190. PHEAP Heap = (PHEAP)HeapHandle;
  3191. PHEAP_ENTRY HeapEntry;
  3192. BOOLEAN HeapLocked = FALSE;
  3193. /* Force flags */
  3194. Flags |= Heap->Flags;
  3195. /* Call special heap */
  3196. if (RtlpHeapIsSpecial(Flags))
  3197. return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
  3198. /* Lock if it's lockable */
  3199. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  3200. {
  3201. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  3202. HeapLocked = TRUE;
  3203. }
  3204. /* Get a pointer to the entry */
  3205. HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
  3206. /* If it's a free entry - return error */
  3207. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
  3208. {
  3209. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  3210. /* Release the heap lock if it was acquired */
  3211. if (HeapLocked)
  3212. RtlLeaveHeapLock(Heap->LockVariable);
  3213. return FALSE;
  3214. }
  3215. /* Set / reset flags */
  3216. HeapEntry->Flags &= ~(UserFlagsReset >> 4);
  3217. HeapEntry->Flags |= (UserFlagsSet >> 4);
  3218. /* Release the heap lock if it was acquired */
  3219. if (HeapLocked)
  3220. RtlLeaveHeapLock(Heap->LockVariable);
  3221. return TRUE;
  3222. }
  3223. /*
  3224. * @implemented
  3225. */
  3226. BOOLEAN
  3227. NTAPI
  3228. RtlGetUserInfoHeap(IN PVOID HeapHandle,
  3229. IN ULONG Flags,
  3230. IN PVOID BaseAddress,
  3231. OUT PVOID *UserValue,
  3232. OUT PULONG UserFlags)
  3233. {
  3234. PHEAP Heap = (PHEAP)HeapHandle;
  3235. PHEAP_ENTRY HeapEntry;
  3236. PHEAP_ENTRY_EXTRA Extra;
  3237. BOOLEAN HeapLocked = FALSE;
  3238. /* Force flags */
  3239. Flags |= Heap->Flags;
  3240. /* Call special heap */
  3241. if (RtlpHeapIsSpecial(Flags))
  3242. return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags);
  3243. /* Lock if it's lockable */
  3244. if (!(Heap->Flags & HEAP_NO_SERIALIZE))
  3245. {
  3246. RtlEnterHeapLock(Heap->LockVariable, TRUE);
  3247. HeapLocked = TRUE;
  3248. }
  3249. /* Get a pointer to the entry */
  3250. HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
  3251. /* If it's a free entry - return error */
  3252. if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
  3253. {
  3254. RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
  3255. /* Release the heap lock if it was acquired */
  3256. if (HeapLocked)
  3257. RtlLeaveHeapLock(Heap->LockVariable);
  3258. return FALSE;
  3259. }
  3260. /* Check if this entry has an extra stuff associated with it */
  3261. if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
  3262. {
  3263. /* Get pointer to extra data */
  3264. Extra = RtlpGetExtraStuffPointer(HeapEntry);
  3265. /* Pass user value */
  3266. if (UserValue)
  3267. *UserValue = (PVOID)Extra->Settable;
  3268. /* Decode and return user flags */
  3269. if (UserFlags)
  3270. *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
  3271. }
  3272. /* Release the heap lock if it was acquired */
  3273. if (HeapLocked)
  3274. RtlLeaveHeapLock(Heap->LockVariable);
  3275. return TRUE;
  3276. }
  3277. /*
  3278. * @unimplemented
  3279. */
  3280. NTSTATUS
  3281. NTAPI
  3282. RtlUsageHeap(IN HANDLE Heap,
  3283. IN ULONG Flags,
  3284. OUT PRTL_HEAP_USAGE Usage)
  3285. {
  3286. /* TODO */
  3287. UNIMPLEMENTED;
  3288. return STATUS_NOT_IMPLEMENTED;
  3289. }
  3290. PWSTR
  3291. NTAPI
  3292. RtlQueryTagHeap(IN PVOID HeapHandle,
  3293. IN ULONG Flags,
  3294. IN USHORT TagIndex,
  3295. IN BOOLEAN ResetCounters,
  3296. OUT PRTL_HEAP_TAG_INFO HeapTagInfo)
  3297. {
  3298. /* TODO */
  3299. UNIMPLEMENTED;
  3300. return NULL;
  3301. }
  3302. ULONG
  3303. NTAPI
  3304. RtlExtendHeap(IN HANDLE Heap,
  3305. IN ULONG Flags,
  3306. IN PVOID P,
  3307. IN SIZE_T Size)
  3308. {
  3309. /* TODO */
  3310. UNIMPLEMENTED;
  3311. return 0;
  3312. }
  3313. ULONG
  3314. NTAPI
  3315. RtlCreateTagHeap(IN HANDLE HeapHandle,
  3316. IN ULONG Flags,
  3317. IN PWSTR TagName,
  3318. IN PWSTR TagSubName)
  3319. {
  3320. /* TODO */
  3321. UNIMPLEMENTED;
  3322. return 0;
  3323. }
  3324. NTSTATUS
  3325. NTAPI
  3326. RtlWalkHeap(IN HANDLE HeapHandle,
  3327. IN PVOID HeapEntry)
  3328. {
  3329. UNIMPLEMENTED;
  3330. return STATUS_NOT_IMPLEMENTED;
  3331. }
  3332. PVOID
  3333. NTAPI
  3334. RtlProtectHeap(IN PVOID HeapHandle,
  3335. IN BOOLEAN ReadOnly)
  3336. {
  3337. UNIMPLEMENTED;
  3338. return NULL;
  3339. }
  3340. NTSTATUS
  3341. NTAPI
  3342. RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
  3343. IN HEAP_INFORMATION_CLASS HeapInformationClass,
  3344. IN PVOID HeapInformation,
  3345. IN SIZE_T HeapInformationLength)
  3346. {
  3347. /* Setting heap information is not really supported except for enabling LFH */
  3348. if (HeapInformationClass == 0) return STATUS_SUCCESS;
  3349. /* Check buffer length */
  3350. if (HeapInformationLength < sizeof(ULONG))
  3351. {
  3352. /* The provided buffer is too small */
  3353. return STATUS_BUFFER_TOO_SMALL;
  3354. }
  3355. /* Check for a special magic value for enabling LFH */
  3356. if (*(PULONG)HeapInformation == 2)
  3357. {
  3358. DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
  3359. return STATUS_SUCCESS;
  3360. }
  3361. return STATUS_UNSUCCESSFUL;
  3362. }
  3363. NTSTATUS
  3364. NTAPI
  3365. RtlQueryHeapInformation(HANDLE HeapHandle,
  3366. HEAP_INFORMATION_CLASS HeapInformationClass,
  3367. PVOID HeapInformation OPTIONAL,
  3368. SIZE_T HeapInformationLength OPTIONAL,
  3369. PSIZE_T ReturnLength OPTIONAL)
  3370. {
  3371. PHEAP Heap = (PHEAP)HeapHandle;
  3372. /* Only HeapCompatibilityInformation is supported */
  3373. if (HeapInformationClass != HeapCompatibilityInformation)
  3374. return STATUS_UNSUCCESSFUL;
  3375. /* Set result length */
  3376. if (ReturnLength) *ReturnLength = sizeof(ULONG);
  3377. /* Check buffer length */
  3378. if (HeapInformationLength < sizeof(ULONG))
  3379. {
  3380. /* It's too small, return needed length */
  3381. return STATUS_BUFFER_TOO_SMALL;
  3382. }
  3383. /* Return front end heap type */
  3384. *(PULONG)HeapInformation = Heap->FrontEndHeapType;
  3385. return STATUS_SUCCESS;
  3386. }
  3387. NTSTATUS
  3388. NTAPI
  3389. RtlMultipleAllocateHeap(IN PVOID HeapHandle,
  3390. IN ULONG Flags,
  3391. IN SIZE_T Size,
  3392. IN ULONG Count,
  3393. OUT PVOID *Array)
  3394. {
  3395. UNIMPLEMENTED;
  3396. return 0;
  3397. }
  3398. NTSTATUS
  3399. NTAPI
  3400. RtlMultipleFreeHeap(IN PVOID HeapHandle,
  3401. IN ULONG Flags,
  3402. IN ULONG Count,
  3403. OUT PVOID *Array)
  3404. {
  3405. UNIMPLEMENTED;
  3406. return 0;
  3407. }
  3408. /* EOF */