xref: /reactos/sdk/lib/rtl/heap.c (revision f22fa382)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/rtl/heap.c
5  * PURPOSE:         RTL Heap backend allocator
6  * PROGRAMMERS:     Copyright 2010 Aleksey Bragin
7  *                  Copyright 2020 Katayama Hirofumi MZ
8  */
9 
10 /* Useful references:
11    http://msdn.microsoft.com/en-us/library/ms810466.aspx
12    http://msdn.microsoft.com/en-us/library/ms810603.aspx
13    http://www.securitylab.ru/analytics/216376.php
14    http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
15    http://www.phreedom.org/research/exploits/asn1-bitstring/
16    http://illmatics.com/Understanding_the_LFH.pdf
17    http://www.alex-ionescu.com/?p=18
18 */
19 
20 /* INCLUDES *****************************************************************/
21 
22 #include <rtl.h>
23 #include <heap.h>
24 
25 #define NDEBUG
26 #include <debug.h>
27 
28 /* Bitmaps stuff */
29 
30 /* How many least significant bits are clear */
31 UCHAR RtlpBitsClearLow[] =
32 {
33     8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
34     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
35     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
36     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
37     6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
38     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
39     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
40     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
41     7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
42     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
43     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
44     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
45     6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
46     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
47     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
48     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
49 };
50 
51 FORCEINLINE
52 UCHAR
53 RtlpFindLeastSetBit(ULONG Bits)
54 {
55     if (Bits & 0xFFFF)
56     {
57         if (Bits & 0xFF)
58             return RtlpBitsClearLow[Bits & 0xFF]; /* Lowest byte */
59         else
60             return RtlpBitsClearLow[(Bits >> 8) & 0xFF] + 8; /* 2nd byte */
61     }
62     else
63     {
64         if ((Bits >> 16) & 0xFF)
65             return RtlpBitsClearLow[(Bits >> 16) & 0xFF] + 16; /* 3rd byte */
66         else
67             return RtlpBitsClearLow[(Bits >> 24) & 0xFF] + 24; /* Highest byte */
68     }
69 }
70 
71 /* Maximum size of a tail-filling pattern used for compare operation */
72 UCHAR FillPattern[HEAP_ENTRY_SIZE] =
73 {
74     HEAP_TAIL_FILL,
75     HEAP_TAIL_FILL,
76     HEAP_TAIL_FILL,
77     HEAP_TAIL_FILL,
78     HEAP_TAIL_FILL,
79     HEAP_TAIL_FILL,
80     HEAP_TAIL_FILL,
81     HEAP_TAIL_FILL
82 };
83 
84 /* FUNCTIONS *****************************************************************/
85 
86 NTSTATUS NTAPI
87 RtlpInitializeHeap(OUT PHEAP Heap,
88                    IN ULONG Flags,
89                    IN PHEAP_LOCK Lock OPTIONAL,
90                    IN PRTL_HEAP_PARAMETERS Parameters)
91 {
92     ULONG NumUCRs = 8;
93     ULONG Index;
94     SIZE_T HeaderSize;
95     NTSTATUS Status;
96     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
97 
98     /* Preconditions */
99     ASSERT(Heap != NULL);
100     ASSERT(Parameters != NULL);
101     ASSERT(!(Flags & HEAP_LOCK_USER_ALLOCATED));
102     ASSERT(!(Flags & HEAP_NO_SERIALIZE) || (Lock == NULL));  /* HEAP_NO_SERIALIZE => no lock */
103 
104     /* Start out with the size of a plain Heap header */
105     HeaderSize = ROUND_UP(sizeof(HEAP), sizeof(HEAP_ENTRY));
106 
107     /* Check if space needs to be added for the Heap Lock */
108     if (!(Flags & HEAP_NO_SERIALIZE))
109     {
110         if (Lock != NULL)
111             /* The user manages the Heap Lock */
112             Flags |= HEAP_LOCK_USER_ALLOCATED;
113         else
114         if (RtlpGetMode() == UserMode)
115         {
116             /* In user mode, the Heap Lock trails the Heap header */
117             Lock = (PHEAP_LOCK) ((ULONG_PTR) (Heap) + HeaderSize);
118             HeaderSize += ROUND_UP(sizeof(HEAP_LOCK), sizeof(HEAP_ENTRY));
119         }
120     }
121 
122     /* Add space for the initial Heap UnCommitted Range Descriptor list */
123     UcrDescriptor = (PHEAP_UCR_DESCRIPTOR) ((ULONG_PTR) (Heap) + HeaderSize);
124     HeaderSize += ROUND_UP(NumUCRs * sizeof(HEAP_UCR_DESCRIPTOR), sizeof(HEAP_ENTRY));
125 
126     /* Sanity check */
127     ASSERT(HeaderSize <= PAGE_SIZE);
128 
129     /* Initialise the Heap Entry header containing the Heap header */
130     Heap->Entry.Size = (USHORT)(HeaderSize >> HEAP_ENTRY_SHIFT);
131     Heap->Entry.Flags = HEAP_ENTRY_BUSY;
132     Heap->Entry.SmallTagIndex = LOBYTE(Heap->Entry.Size) ^ HIBYTE(Heap->Entry.Size) ^ Heap->Entry.Flags;
133     Heap->Entry.PreviousSize = 0;
134     Heap->Entry.SegmentOffset = 0;
135     Heap->Entry.UnusedBytes = 0;
136 
137     /* Initialise the Heap header */
138     Heap->Signature = HEAP_SIGNATURE;
139     Heap->Flags = Flags;
140     Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE |
141                                  HEAP_GENERATE_EXCEPTIONS |
142                                  HEAP_ZERO_MEMORY |
143                                  HEAP_REALLOC_IN_PLACE_ONLY |
144                                  HEAP_VALIDATE_PARAMETERS_ENABLED |
145                                  HEAP_VALIDATE_ALL_ENABLED |
146                                  HEAP_TAIL_CHECKING_ENABLED |
147                                  HEAP_CREATE_ALIGN_16 |
148                                  HEAP_FREE_CHECKING_ENABLED));
149 
150     /* Initialise the Heap parameters */
151     Heap->VirtualMemoryThreshold = ROUND_UP(Parameters->VirtualMemoryThreshold, sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
152     Heap->SegmentReserve = Parameters->SegmentReserve;
153     Heap->SegmentCommit = Parameters->SegmentCommit;
154     Heap->DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_ENTRY_SHIFT;
155     Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_ENTRY_SHIFT;
156     Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize;
157     Heap->CommitRoutine = Parameters->CommitRoutine;
158 
159     /* Initialise the Heap validation info */
160     Heap->HeaderValidateCopy = NULL;
161     Heap->HeaderValidateLength = (USHORT)HeaderSize;
162 
163     /* Initialise the Heap Lock */
164     if (!(Flags & HEAP_NO_SERIALIZE) && !(Flags & HEAP_LOCK_USER_ALLOCATED))
165     {
166         Status = RtlInitializeHeapLock(&Lock);
167         if (!NT_SUCCESS(Status))
168             return Status;
169     }
170     Heap->LockVariable = Lock;
171 
172     /* Initialise the Heap alignment info */
173     if (Flags & HEAP_CREATE_ALIGN_16)
174     {
175         Heap->AlignMask = (ULONG) ~15;
176         Heap->AlignRound = 15 + sizeof(HEAP_ENTRY);
177     }
178     else
179     {
180         Heap->AlignMask = (ULONG) ~(sizeof(HEAP_ENTRY) - 1);
181         Heap->AlignRound = 2 * sizeof(HEAP_ENTRY) - 1;
182     }
183 
184     if (Flags & HEAP_TAIL_CHECKING_ENABLED)
185         Heap->AlignRound += sizeof(HEAP_ENTRY);
186 
187     /* Initialise the Heap Segment list */
188     for (Index = 0; Index < HEAP_SEGMENTS; ++Index)
189         Heap->Segments[Index] = NULL;
190 
191     /* Initialise the Heap Free Heap Entry lists */
192     for (Index = 0; Index < HEAP_FREELISTS; ++Index)
193         InitializeListHead(&Heap->FreeLists[Index]);
194 
195     /* Initialise the Heap Virtual Allocated Blocks list */
196     InitializeListHead(&Heap->VirtualAllocdBlocks);
197 
198     /* Initialise the Heap UnCommitted Region lists */
199     InitializeListHead(&Heap->UCRSegments);
200     InitializeListHead(&Heap->UCRList);
201 
202     /* Register the initial Heap UnCommitted Region Descriptors */
203     for (Index = 0; Index < NumUCRs; ++Index)
204         InsertTailList(&Heap->UCRList, &UcrDescriptor[Index].ListEntry);
205 
206     return STATUS_SUCCESS;
207 }
208 
209 FORCEINLINE
210 VOID
211 RtlpSetFreeListsBit(PHEAP Heap,
212                     PHEAP_FREE_ENTRY FreeEntry)
213 {
214     ULONG Index, Bit;
215 
216     ASSERT(FreeEntry->Size < HEAP_FREELISTS);
217 
218     /* Calculate offset in the free list bitmap */
219     Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
220     Bit = 1 << (FreeEntry->Size & 7);
221 
222     /* Assure it's not already set */
223     ASSERT((Heap->u.FreeListsInUseBytes[Index] & Bit) == 0);
224 
225     /* Set it */
226     Heap->u.FreeListsInUseBytes[Index] |= Bit;
227 }
228 
229 FORCEINLINE
230 VOID
231 RtlpClearFreeListsBit(PHEAP Heap,
232                       PHEAP_FREE_ENTRY FreeEntry)
233 {
234     ULONG Index, Bit;
235 
236     ASSERT(FreeEntry->Size < HEAP_FREELISTS);
237 
238     /* Calculate offset in the free list bitmap */
239     Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
240     Bit = 1 << (FreeEntry->Size & 7);
241 
242     /* Assure it was set and the corresponding free list is empty */
243     ASSERT(Heap->u.FreeListsInUseBytes[Index] & Bit);
244     ASSERT(IsListEmpty(&Heap->FreeLists[FreeEntry->Size]));
245 
246     /* Clear it */
247     Heap->u.FreeListsInUseBytes[Index] ^= Bit;
248 }
249 
250 VOID NTAPI
251 RtlpInsertFreeBlockHelper(PHEAP Heap,
252                           PHEAP_FREE_ENTRY FreeEntry,
253                           SIZE_T BlockSize,
254                           BOOLEAN NoFill)
255 {
256     PLIST_ENTRY FreeListHead, Current;
257     PHEAP_FREE_ENTRY CurrentEntry;
258 
259     ASSERT(FreeEntry->Size == BlockSize);
260 
261     /* Fill if it's not denied */
262     if (!NoFill)
263     {
264         FreeEntry->Flags &= ~(HEAP_ENTRY_FILL_PATTERN |
265                               HEAP_ENTRY_EXTRA_PRESENT |
266                               HEAP_ENTRY_BUSY);
267 
268         if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
269         {
270             RtlFillMemoryUlong((PCHAR)(FreeEntry + 1),
271                                (BlockSize << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry),
272                                ARENA_FREE_FILLER);
273 
274             FreeEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
275         }
276     }
277     else
278     {
279         /* Clear out all flags except the last entry one */
280         FreeEntry->Flags &= HEAP_ENTRY_LAST_ENTRY;
281     }
282 
283     /* Insert it either into dedicated or non-dedicated list */
284     if (BlockSize < HEAP_FREELISTS)
285     {
286         /* Dedicated list */
287         FreeListHead = &Heap->FreeLists[BlockSize];
288 
289         if (IsListEmpty(FreeListHead))
290         {
291             RtlpSetFreeListsBit(Heap, FreeEntry);
292         }
293     }
294     else
295     {
296         /* Non-dedicated one */
297         FreeListHead = &Heap->FreeLists[0];
298         Current = FreeListHead->Flink;
299 
300         /* Find a position where to insert it to (the list must be sorted) */
301         while (FreeListHead != Current)
302         {
303             CurrentEntry = CONTAINING_RECORD(Current, HEAP_FREE_ENTRY, FreeList);
304 
305             if (BlockSize <= CurrentEntry->Size)
306                 break;
307 
308             Current = Current->Flink;
309         }
310 
311         FreeListHead = Current;
312     }
313 
314     /* Actually insert it into the list */
315     InsertTailList(FreeListHead, &FreeEntry->FreeList);
316 }
317 
318 VOID NTAPI
319 RtlpInsertFreeBlock(PHEAP Heap,
320                     PHEAP_FREE_ENTRY FreeEntry,
321                     SIZE_T BlockSize)
322 {
323     USHORT Size, PreviousSize;
324     UCHAR SegmentOffset, Flags;
325     PHEAP_SEGMENT Segment;
326 
327     DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize);
328 
329     /* Increase the free size counter */
330     Heap->TotalFreeSize += BlockSize;
331 
332     /* Remember certain values */
333     Flags = FreeEntry->Flags;
334     PreviousSize = FreeEntry->PreviousSize;
335     SegmentOffset = FreeEntry->SegmentOffset;
336     Segment = Heap->Segments[SegmentOffset];
337 
338     /* Process it */
339     while (BlockSize)
340     {
341         /* Check for the max size */
342         if (BlockSize > HEAP_MAX_BLOCK_SIZE)
343         {
344             Size = HEAP_MAX_BLOCK_SIZE;
345 
346             /* Special compensation if it goes above limit just by 1 */
347             if (BlockSize == (HEAP_MAX_BLOCK_SIZE + 1))
348                 Size -= 16;
349 
350             FreeEntry->Flags = 0;
351         }
352         else
353         {
354             Size = (USHORT)BlockSize;
355             FreeEntry->Flags = Flags;
356         }
357 
358         /* Change its size and insert it into a free list */
359         FreeEntry->Size = Size;
360         FreeEntry->PreviousSize = PreviousSize;
361         FreeEntry->SegmentOffset = SegmentOffset;
362 
363         /* Call a helper to actually insert the block */
364         RtlpInsertFreeBlockHelper(Heap, FreeEntry, Size, FALSE);
365 
366         /* Update sizes */
367         PreviousSize = Size;
368         BlockSize -= Size;
369 
370         /* Go to the next entry */
371         FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
372 
373         /* Check if that's all */
374         if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) return;
375     }
376 
377     /* Update previous size if needed */
378     if (!(Flags & HEAP_ENTRY_LAST_ENTRY))
379         FreeEntry->PreviousSize = PreviousSize;
380 }
381 
382 VOID NTAPI
383 RtlpRemoveFreeBlock(PHEAP Heap,
384                     PHEAP_FREE_ENTRY FreeEntry,
385                     BOOLEAN Dedicated,
386                     BOOLEAN NoFill)
387 {
388     SIZE_T Result, RealSize;
389 
390     /* Remove the free block and update the freelists bitmap */
391     if (RemoveEntryList(&FreeEntry->FreeList) &&
392         (Dedicated || (!Dedicated && FreeEntry->Size < HEAP_FREELISTS)))
393     {
394         RtlpClearFreeListsBit(Heap, FreeEntry);
395     }
396 
397     /* Fill with pattern if necessary */
398     if (!NoFill &&
399         (FreeEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
400     {
401         RealSize = (FreeEntry->Size << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry);
402 
403         /* Deduct extra stuff from block's real size */
404         if (FreeEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT &&
405             RealSize > sizeof(HEAP_FREE_ENTRY_EXTRA))
406         {
407             RealSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
408         }
409 
410         /* Check if the free filler is intact */
411         Result = RtlCompareMemoryUlong((PCHAR)(FreeEntry + 1),
412                                         RealSize,
413                                         ARENA_FREE_FILLER);
414 
415         if (Result != RealSize)
416         {
417             DPRINT1("Free heap block %p modified at %p after it was freed\n",
418                 FreeEntry,
419                 (PCHAR)(FreeEntry + 1) + Result);
420         }
421     }
422 }
423 
424 SIZE_T NTAPI
425 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry)
426 {
427     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
428 
429     /* Get pointer to the containing record */
430     VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
431     ASSERT(VirtualEntry->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY));
432 
433     /* Restore the real size */
434     return VirtualEntry->CommitSize - HeapEntry->Size;
435 }
436 
437 PHEAP_UCR_DESCRIPTOR NTAPI
438 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment)
439 {
440     PLIST_ENTRY Entry;
441     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
442     PHEAP_UCR_SEGMENT UcrSegment;
443     PHEAP Heap = Segment->Heap;
444     SIZE_T ReserveSize = 16 * PAGE_SIZE;
445     SIZE_T CommitSize = 1 * PAGE_SIZE;
446     NTSTATUS Status;
447 
448     DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment);
449 
450     /* Check if we have unused UCRs */
451     if (IsListEmpty(&Heap->UCRList))
452     {
453         /* Get a pointer to the first UCR segment */
454         UcrSegment = CONTAINING_RECORD(Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry);
455 
456         /* Check the list of UCR segments */
457         if (IsListEmpty(&Heap->UCRSegments) ||
458             UcrSegment->ReservedSize == UcrSegment->CommittedSize)
459         {
460             /* We need to create a new one. Reserve 16 pages for it */
461             UcrSegment = NULL;
462             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
463                                              (PVOID *)&UcrSegment,
464                                              0,
465                                              &ReserveSize,
466                                              MEM_RESERVE,
467                                              PAGE_READWRITE);
468 
469             if (!NT_SUCCESS(Status)) return NULL;
470 
471             /* Commit one page */
472             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
473                                              (PVOID *)&UcrSegment,
474                                              0,
475                                              &CommitSize,
476                                              MEM_COMMIT,
477                                              PAGE_READWRITE);
478 
479             if (!NT_SUCCESS(Status))
480             {
481                 /* Release reserved memory */
482                 ZwFreeVirtualMemory(NtCurrentProcess(),
483                                     (PVOID *)&UcrSegment,
484                                     &ReserveSize,
485                                     MEM_RELEASE);
486                 return NULL;
487             }
488 
489             /* Set it's data */
490             UcrSegment->ReservedSize = ReserveSize;
491             UcrSegment->CommittedSize = CommitSize;
492 
493             /* Add it to the head of the list */
494             InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry);
495 
496             /* Get a pointer to the first available UCR descriptor */
497             UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1);
498         }
499         else
500         {
501             /* It's possible to use existing UCR segment. Commit one more page */
502             UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize);
503             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
504                                              (PVOID *)&UcrDescriptor,
505                                              0,
506                                              &CommitSize,
507                                              MEM_COMMIT,
508                                              PAGE_READWRITE);
509 
510             if (!NT_SUCCESS(Status)) return NULL;
511 
512             /* Update sizes */
513             UcrSegment->CommittedSize += CommitSize;
514         }
515 
516         /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
517         while ((PCHAR)(UcrDescriptor + 1) <= (PCHAR)UcrSegment + UcrSegment->CommittedSize)
518         {
519             InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry);
520             UcrDescriptor++;
521         }
522     }
523 
524     /* There are unused UCRs, just get the first one */
525     Entry = RemoveHeadList(&Heap->UCRList);
526     UcrDescriptor = CONTAINING_RECORD(Entry, HEAP_UCR_DESCRIPTOR, ListEntry);
527     return UcrDescriptor;
528 }
529 
530 VOID NTAPI
531 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment,
532                             PHEAP_UCR_DESCRIPTOR UcrDescriptor)
533 {
534     /* Zero it out */
535     UcrDescriptor->Address = NULL;
536     UcrDescriptor->Size = 0;
537 
538     /* Put it into the heap's list of unused UCRs */
539     InsertHeadList(&Segment->Heap->UCRList, &UcrDescriptor->ListEntry);
540 }
541 
542 VOID NTAPI
543 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment,
544                            ULONG_PTR Address,
545                            SIZE_T Size)
546 {
547     PLIST_ENTRY Current;
548     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
549 
550     DPRINT("RtlpInsertUnCommittedPages(%p %08Ix %Ix)\n", Segment, Address, Size);
551 
552     /* Go through the list of UCR descriptors, they are sorted from lowest address
553        to the highest */
554     Current = Segment->UCRSegmentList.Flink;
555     while (Current != &Segment->UCRSegmentList)
556     {
557         UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
558 
559         if ((ULONG_PTR)UcrDescriptor->Address > Address)
560         {
561             /* Check for a really lucky case */
562             if ((Address + Size) == (ULONG_PTR)UcrDescriptor->Address)
563             {
564                 /* Exact match */
565                 UcrDescriptor->Address = (PVOID)Address;
566                 UcrDescriptor->Size += Size;
567                 return;
568             }
569 
570             /* We found the block before which the new one should go */
571             break;
572         }
573         else if (((ULONG_PTR)UcrDescriptor->Address + UcrDescriptor->Size) == Address)
574         {
575             /* Modify this entry */
576             Address = (ULONG_PTR)UcrDescriptor->Address;
577             Size += UcrDescriptor->Size;
578 
579             /* Advance to the next descriptor */
580             Current = Current->Flink;
581 
582             /* Remove the current descriptor from the list and destroy it */
583             RemoveEntryList(&UcrDescriptor->SegmentEntry);
584             RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
585 
586             Segment->NumberOfUnCommittedRanges--;
587         }
588         else
589         {
590             /* Advance to the next descriptor */
591             Current = Current->Flink;
592         }
593     }
594 
595     /* Create a new UCR descriptor */
596     UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
597     if (!UcrDescriptor) return;
598 
599     UcrDescriptor->Address = (PVOID)Address;
600     UcrDescriptor->Size = Size;
601 
602     /* "Current" is the descriptor before which our one should go */
603     InsertTailList(Current, &UcrDescriptor->SegmentEntry);
604 
605     DPRINT("Added segment UCR with base %08Ix, size 0x%x\n", Address, Size);
606 
607     /* Increase counters */
608     Segment->NumberOfUnCommittedRanges++;
609 }
610 
611 PHEAP_FREE_ENTRY NTAPI
612 RtlpFindAndCommitPages(PHEAP Heap,
613                        PHEAP_SEGMENT Segment,
614                        PSIZE_T Size,
615                        PVOID AddressRequested)
616 {
617     PLIST_ENTRY Current;
618     ULONG_PTR Address = 0;
619     PHEAP_UCR_DESCRIPTOR UcrDescriptor, PreviousUcr = NULL;
620     PHEAP_ENTRY FirstEntry, LastEntry;
621     NTSTATUS Status;
622 
623     DPRINT("RtlpFindAndCommitPages(%p %p %Ix %08Ix)\n", Heap, Segment, *Size, Address);
624 
625     /* Go through UCRs in a segment */
626     Current = Segment->UCRSegmentList.Flink;
627     while (Current != &Segment->UCRSegmentList)
628     {
629         UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
630 
631         /* Check if we can use that one right away */
632         if (UcrDescriptor->Size >= *Size &&
633             (UcrDescriptor->Address == AddressRequested || !AddressRequested))
634         {
635             /* Get the address */
636             Address = (ULONG_PTR)UcrDescriptor->Address;
637 
638             /* Commit it */
639             if (Heap->CommitRoutine)
640             {
641                 Status = Heap->CommitRoutine(Heap, (PVOID *)&Address, Size);
642             }
643             else
644             {
645                 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
646                                                  (PVOID *)&Address,
647                                                  0,
648                                                  Size,
649                                                  MEM_COMMIT,
650                                                  PAGE_READWRITE);
651             }
652 
653             DPRINT("Committed %Iu bytes at base %08Ix, UCR size is %lu\n", *Size, Address, UcrDescriptor->Size);
654 
655             /* Fail in unsuccessful case */
656             if (!NT_SUCCESS(Status))
657             {
658                 DPRINT1("Committing page failed with status 0x%08X\n", Status);
659                 return NULL;
660             }
661 
662             /* Update tracking numbers */
663             Segment->NumberOfUnCommittedPages -= (ULONG)(*Size / PAGE_SIZE);
664 
665             /* Calculate first and last entries */
666             FirstEntry = (PHEAP_ENTRY)Address;
667 
668             /* Go through the entries to find the last one */
669             if (PreviousUcr)
670                 LastEntry = (PHEAP_ENTRY)((ULONG_PTR)PreviousUcr->Address + PreviousUcr->Size);
671             else
672                 LastEntry = &Segment->Entry;
673 
674             while (!(LastEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
675             {
676                 ASSERT(LastEntry->Size != 0);
677                 LastEntry += LastEntry->Size;
678             }
679             ASSERT((LastEntry + LastEntry->Size) == FirstEntry);
680 
681             /* Unmark it as a last entry */
682             LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
683 
684             /* Update UCR descriptor */
685             UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size);
686             UcrDescriptor->Size -= *Size;
687 
688             DPRINT("Updating UcrDescriptor %p, new Address %p, size %lu\n",
689                 UcrDescriptor, UcrDescriptor->Address, UcrDescriptor->Size);
690 
691             /* Set various first entry fields */
692             FirstEntry->SegmentOffset = LastEntry->SegmentOffset;
693             FirstEntry->Size = (USHORT)(*Size >> HEAP_ENTRY_SHIFT);
694             FirstEntry->PreviousSize = LastEntry->Size;
695 
696             /* Check if anything left in this UCR */
697             if (UcrDescriptor->Size == 0)
698             {
699                 /* It's fully exhausted */
700 
701                 /* Check if this is the end of the segment */
702                 if(UcrDescriptor->Address == Segment->LastValidEntry)
703                 {
704                     FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
705                 }
706                 else
707                 {
708                     FirstEntry->Flags = 0;
709                     /* Update field of next entry */
710                     ASSERT((FirstEntry + FirstEntry->Size)->PreviousSize == 0);
711                     (FirstEntry + FirstEntry->Size)->PreviousSize = FirstEntry->Size;
712                 }
713 
714                 /* This UCR needs to be removed because it became useless */
715                 RemoveEntryList(&UcrDescriptor->SegmentEntry);
716 
717                 RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
718                 Segment->NumberOfUnCommittedRanges--;
719             }
720             else
721             {
722                 FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
723             }
724 
725             /* We're done */
726             return (PHEAP_FREE_ENTRY)FirstEntry;
727         }
728 
729         /* Advance to the next descriptor */
730         PreviousUcr = UcrDescriptor;
731         Current = Current->Flink;
732     }
733 
734     return NULL;
735 }
736 
737 VOID NTAPI
738 RtlpDeCommitFreeBlock(PHEAP Heap,
739                       PHEAP_FREE_ENTRY FreeEntry,
740                       SIZE_T Size)
741 {
742     PHEAP_SEGMENT Segment;
743     PHEAP_ENTRY PrecedingInUseEntry = NULL, NextInUseEntry = NULL;
744     PHEAP_FREE_ENTRY NextFreeEntry;
745     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
746     SIZE_T PrecedingSize, NextSize, DecommitSize;
747     ULONG_PTR DecommitBase;
748     NTSTATUS Status;
749 
750     DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size);
751 
752     /* We can't decommit if there is a commit routine! */
753     if (Heap->CommitRoutine)
754     {
755         /* Just add it back the usual way */
756         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
757         return;
758     }
759 
760     /* Get the segment */
761     Segment = Heap->Segments[FreeEntry->SegmentOffset];
762 
763     /* Get the preceding entry */
764     DecommitBase = ROUND_UP(FreeEntry, PAGE_SIZE);
765     PrecedingSize = (PHEAP_ENTRY)DecommitBase - (PHEAP_ENTRY)FreeEntry;
766 
767     if (PrecedingSize == 1)
768     {
769         /* Just 1 heap entry, increase the base/size */
770         DecommitBase += PAGE_SIZE;
771         PrecedingSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
772     }
773     else if (FreeEntry->PreviousSize &&
774              (DecommitBase == (ULONG_PTR)FreeEntry))
775     {
776         PrecedingInUseEntry = (PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize;
777     }
778 
779     /* Get the next entry */
780     NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
781     DecommitSize = ROUND_DOWN(NextFreeEntry, PAGE_SIZE);
782     NextSize = (PHEAP_ENTRY)NextFreeEntry - (PHEAP_ENTRY)DecommitSize;
783 
784     if (NextSize == 1)
785     {
786         /* Just 1 heap entry, increase the size */
787         DecommitSize -= PAGE_SIZE;
788         NextSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
789     }
790     else if (NextSize == 0 &&
791              !(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
792     {
793         NextInUseEntry = (PHEAP_ENTRY)NextFreeEntry;
794     }
795 
796     NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry - NextSize);
797 
798     /* Calculate real decommit size */
799     if (DecommitSize > DecommitBase)
800     {
801         DecommitSize -= DecommitBase;
802     }
803     else
804     {
805         /* Nothing to decommit */
806         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
807         return;
808     }
809 
810     /* A decommit is necessary. Create a UCR descriptor */
811     UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
812     if (!UcrDescriptor)
813     {
814         DPRINT1("HEAP: Failed to create UCR descriptor\n");
815         RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize);
816         return;
817     }
818 
819     /* Decommit the memory */
820     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
821                                  (PVOID *)&DecommitBase,
822                                  &DecommitSize,
823                                  MEM_DECOMMIT);
824 
825     /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
826     RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
827 
828     if (!NT_SUCCESS(Status))
829     {
830         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
831         return;
832     }
833 
834     /* Insert uncommitted pages */
835     RtlpInsertUnCommittedPages(Segment, DecommitBase, DecommitSize);
836     Segment->NumberOfUnCommittedPages += (ULONG)(DecommitSize / PAGE_SIZE);
837 
838     if (PrecedingSize)
839     {
840         /* Adjust size of this free entry and insert it */
841         FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
842         FreeEntry->Size = (USHORT)PrecedingSize;
843         Heap->TotalFreeSize += PrecedingSize;
844 
845         /* Insert it into the free list */
846         RtlpInsertFreeBlockHelper(Heap, FreeEntry, PrecedingSize, FALSE);
847     }
848     else if (PrecedingInUseEntry)
849     {
850         /* Adjust preceding in use entry */
851         PrecedingInUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
852     }
853 
854     /* Now the next one */
855     if (NextSize)
856     {
857         /* Adjust size of this free entry and insert it */
858         NextFreeEntry->Flags = 0;
859         NextFreeEntry->PreviousSize = 0;
860         NextFreeEntry->SegmentOffset = Segment->Entry.SegmentOffset;
861         NextFreeEntry->Size = (USHORT)NextSize;
862 
863         ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry + NextSize))->PreviousSize = (USHORT)NextSize;
864 
865         Heap->TotalFreeSize += NextSize;
866         RtlpInsertFreeBlockHelper(Heap, NextFreeEntry, NextSize, FALSE);
867     }
868     else if (NextInUseEntry)
869     {
870         NextInUseEntry->PreviousSize = 0;
871     }
872 }
873 
874 NTSTATUS
875 NTAPI
876 RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
877                           OUT PHEAP_SEGMENT Segment,
878                           IN UCHAR SegmentIndex,
879                           IN ULONG SegmentFlags,
880                           IN SIZE_T SegmentReserve,
881                           IN SIZE_T SegmentCommit)
882 {
883     PHEAP_ENTRY HeapEntry;
884 
885     /* Preconditions */
886     ASSERT(Heap != NULL);
887     ASSERT(Segment != NULL);
888     ASSERT(SegmentCommit >= PAGE_SIZE);
889     ASSERT(ROUND_DOWN(SegmentCommit, PAGE_SIZE) == SegmentCommit);
890     ASSERT(SegmentReserve >= SegmentCommit);
891     ASSERT(ROUND_DOWN(SegmentReserve, PAGE_SIZE) == SegmentReserve);
892 
893     DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap, Segment, SegmentIndex, SegmentFlags, SegmentReserve, SegmentCommit);
894 
895     /* Initialise the Heap Entry header if this is not the first Heap Segment */
896     if ((PHEAP_SEGMENT) (Heap) != Segment)
897     {
898         Segment->Entry.Size = ROUND_UP(sizeof(HEAP_SEGMENT), sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
899         Segment->Entry.Flags = HEAP_ENTRY_BUSY;
900         Segment->Entry.SmallTagIndex = LOBYTE(Segment->Entry.Size) ^ HIBYTE(Segment->Entry.Size) ^ Segment->Entry.Flags;
901         Segment->Entry.PreviousSize = 0;
902         Segment->Entry.SegmentOffset = SegmentIndex;
903         Segment->Entry.UnusedBytes = 0;
904     }
905 
906     /* Sanity check */
907     ASSERT((Segment->Entry.Size << HEAP_ENTRY_SHIFT) <= PAGE_SIZE);
908 
909     /* Initialise the Heap Segment header */
910     Segment->SegmentSignature = HEAP_SEGMENT_SIGNATURE;
911     Segment->SegmentFlags = SegmentFlags;
912     Segment->Heap = Heap;
913     Heap->Segments[SegmentIndex] = Segment;
914 
915     /* Initialise the Heap Segment location information */
916     Segment->BaseAddress = Segment;
917     Segment->NumberOfPages = (ULONG)(SegmentReserve >> PAGE_SHIFT);
918 
919     /* Initialise the Heap Entries contained within the Heap Segment */
920     Segment->FirstEntry = &Segment->Entry + Segment->Entry.Size;
921     Segment->LastValidEntry = (PHEAP_ENTRY)((ULONG_PTR)Segment + SegmentReserve);
922 
923     if (((SIZE_T)Segment->Entry.Size << HEAP_ENTRY_SHIFT) < SegmentCommit)
924     {
925         HeapEntry = Segment->FirstEntry;
926 
927         /* Prepare a Free Heap Entry header */
928         HeapEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
929         HeapEntry->PreviousSize = Segment->Entry.Size;
930         HeapEntry->SegmentOffset = SegmentIndex;
931 
932         /* Register the Free Heap Entry */
933         RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY) HeapEntry, (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size);
934     }
935 
936     /* Initialise the Heap Segment UnCommitted Range information */
937     Segment->NumberOfUnCommittedPages = (ULONG)((SegmentReserve - SegmentCommit) >> PAGE_SHIFT);
938     Segment->NumberOfUnCommittedRanges = 0;
939     InitializeListHead(&Segment->UCRSegmentList);
940 
941     /* Register the UnCommitted Range of the Heap Segment */
942     if (Segment->NumberOfUnCommittedPages != 0)
943         RtlpInsertUnCommittedPages(Segment, (ULONG_PTR) (Segment) + SegmentCommit, SegmentReserve - SegmentCommit);
944 
945     return STATUS_SUCCESS;
946 }
947 
948 VOID NTAPI
949 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment)
950 {
951     NTSTATUS Status;
952     PVOID BaseAddress;
953     SIZE_T Size = 0;
954 
955     /* Make sure it's not user allocated */
956     if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return;
957 
958     BaseAddress = Segment->BaseAddress;
959     DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress);
960 
961     /* Release virtual memory */
962     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
963                                  &BaseAddress,
964                                  &Size,
965                                  MEM_RELEASE);
966 
967     if (!NT_SUCCESS(Status))
968     {
969         DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status);
970     }
971 }
972 
973 PHEAP_FREE_ENTRY NTAPI
974 RtlpCoalesceHeap(PHEAP Heap)
975 {
976     UNIMPLEMENTED;
977     return NULL;
978 }
979 
980 PHEAP_FREE_ENTRY NTAPI
981 RtlpCoalesceFreeBlocks (PHEAP Heap,
982                         PHEAP_FREE_ENTRY FreeEntry,
983                         PSIZE_T FreeSize,
984                         BOOLEAN Remove)
985 {
986     PHEAP_FREE_ENTRY CurrentEntry, NextEntry;
987 
988     /* Get the previous entry */
989     CurrentEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize);
990 
991     /* Check it */
992     if (CurrentEntry != FreeEntry &&
993         !(CurrentEntry->Flags & HEAP_ENTRY_BUSY) &&
994         (*FreeSize + CurrentEntry->Size) <= HEAP_MAX_BLOCK_SIZE)
995     {
996         ASSERT(FreeEntry->PreviousSize == CurrentEntry->Size);
997 
998         /* Remove it if asked for */
999         if (Remove)
1000         {
1001             RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
1002             Heap->TotalFreeSize -= FreeEntry->Size;
1003 
1004             /* Remove it only once! */
1005             Remove = FALSE;
1006         }
1007 
1008         /* Remove previous entry too */
1009         RtlpRemoveFreeBlock(Heap, CurrentEntry, FALSE, FALSE);
1010 
1011         /* Copy flags */
1012         CurrentEntry->Flags = FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
1013 
1014         /* Advance FreeEntry and update sizes */
1015         FreeEntry = CurrentEntry;
1016         *FreeSize = *FreeSize + CurrentEntry->Size;
1017         Heap->TotalFreeSize -= CurrentEntry->Size;
1018         FreeEntry->Size = (USHORT)(*FreeSize);
1019 
1020         /* Also update previous size if needed */
1021         if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
1022         {
1023             ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
1024         }
1025     }
1026 
1027     /* Check the next block if it exists */
1028     if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
1029     {
1030         NextEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + *FreeSize);
1031 
1032         if (!(NextEntry->Flags & HEAP_ENTRY_BUSY) &&
1033             NextEntry->Size + *FreeSize <= HEAP_MAX_BLOCK_SIZE)
1034         {
1035             ASSERT(*FreeSize == NextEntry->PreviousSize);
1036 
1037             /* Remove it if asked for */
1038             if (Remove)
1039             {
1040                 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
1041                 Heap->TotalFreeSize -= FreeEntry->Size;
1042             }
1043 
1044             /* Copy flags */
1045             FreeEntry->Flags = NextEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
1046 
1047             /* Remove next entry now */
1048             RtlpRemoveFreeBlock(Heap, NextEntry, FALSE, FALSE);
1049 
1050             /* Update sizes */
1051             *FreeSize = *FreeSize + NextEntry->Size;
1052             Heap->TotalFreeSize -= NextEntry->Size;
1053             FreeEntry->Size = (USHORT)(*FreeSize);
1054 
1055             /* Also update previous size if needed */
1056             if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
1057             {
1058                 ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
1059             }
1060         }
1061     }
1062     return FreeEntry;
1063 }
1064 
1065 PHEAP_FREE_ENTRY NTAPI
1066 RtlpExtendHeap(PHEAP Heap,
1067                SIZE_T Size)
1068 {
1069     ULONG Pages;
1070     UCHAR Index, EmptyIndex;
1071     SIZE_T FreeSize, CommitSize, ReserveSize;
1072     PHEAP_SEGMENT Segment;
1073     PHEAP_FREE_ENTRY FreeEntry;
1074     NTSTATUS Status;
1075 
1076     DPRINT("RtlpExtendHeap(%p %x)\n", Heap, Size);
1077 
1078     /* Calculate amount in pages */
1079     Pages = (ULONG)((Size + PAGE_SIZE - 1) / PAGE_SIZE);
1080     FreeSize = Pages * PAGE_SIZE;
1081     DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages, FreeSize);
1082 
1083     /* Find an empty segment */
1084     EmptyIndex = HEAP_SEGMENTS;
1085     for (Index = 0; Index < HEAP_SEGMENTS; Index++)
1086     {
1087         Segment = Heap->Segments[Index];
1088 
1089         if (Segment) DPRINT("Segment[%u] %p with NOUCP %x\n", Index, Segment, Segment->NumberOfUnCommittedPages);
1090 
1091         /* Check if its size suits us */
1092         if (Segment &&
1093             Pages <= Segment->NumberOfUnCommittedPages)
1094         {
1095             DPRINT("This segment is suitable\n");
1096 
1097             /* Commit needed amount */
1098             FreeEntry = RtlpFindAndCommitPages(Heap, Segment, &FreeSize, NULL);
1099 
1100             /* Coalesce it with adjacent entries */
1101             if (FreeEntry)
1102             {
1103                 FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
1104                 FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
1105                 RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
1106                 return FreeEntry;
1107             }
1108         }
1109         else if (!Segment &&
1110                  EmptyIndex == HEAP_SEGMENTS)
1111         {
1112             /* Remember the first unused segment index */
1113             EmptyIndex = Index;
1114         }
1115     }
1116 
1117     /* No luck, need to grow the heap */
1118     if ((Heap->Flags & HEAP_GROWABLE) &&
1119         (EmptyIndex != HEAP_SEGMENTS))
1120     {
1121         Segment = NULL;
1122 
1123         /* Reserve the memory */
1124         if ((Size + PAGE_SIZE) <= Heap->SegmentReserve)
1125             ReserveSize = Heap->SegmentReserve;
1126         else
1127             ReserveSize = Size + PAGE_SIZE;
1128 
1129         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
1130                                          (PVOID)&Segment,
1131                                          0,
1132                                          &ReserveSize,
1133                                          MEM_RESERVE,
1134                                          PAGE_READWRITE);
1135 
1136         /* If it failed, retry again with a half division algorithm */
1137         while (!NT_SUCCESS(Status) &&
1138             ReserveSize != Size + PAGE_SIZE)
1139         {
1140             ReserveSize /= 2;
1141 
1142             if (ReserveSize < (Size + PAGE_SIZE))
1143                 ReserveSize = Size + PAGE_SIZE;
1144 
1145             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
1146                                              (PVOID)&Segment,
1147                                              0,
1148                                              &ReserveSize,
1149                                              MEM_RESERVE,
1150                                              PAGE_READWRITE);
1151         }
1152 
1153         /* Proceed only if it's success */
1154         if (NT_SUCCESS(Status))
1155         {
1156             Heap->SegmentReserve += ReserveSize;
1157 
1158             /* Now commit the memory */
1159             if ((Size + PAGE_SIZE) <= Heap->SegmentCommit)
1160                 CommitSize = Heap->SegmentCommit;
1161             else
1162                 CommitSize = Size + PAGE_SIZE;
1163 
1164             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
1165                                              (PVOID)&Segment,
1166                                              0,
1167                                              &CommitSize,
1168                                              MEM_COMMIT,
1169                                              PAGE_READWRITE);
1170 
1171             DPRINT("Committed %lu bytes at base %p\n", CommitSize, Segment);
1172 
1173             /* Initialize heap segment if commit was successful */
1174             if (NT_SUCCESS(Status))
1175                 Status = RtlpInitializeHeapSegment(Heap, Segment, EmptyIndex, 0, ReserveSize, CommitSize);
1176 
1177             /* If everything worked - cool */
1178             if (NT_SUCCESS(Status)) return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
1179 
1180             DPRINT1("Committing failed with status 0x%08X\n", Status);
1181 
1182             /* Nope, we failed. Free memory */
1183             ZwFreeVirtualMemory(NtCurrentProcess(),
1184                                 (PVOID)&Segment,
1185                                 &ReserveSize,
1186                                 MEM_RELEASE);
1187         }
1188         else
1189         {
1190             DPRINT1("Reserving failed with status 0x%08X\n", Status);
1191         }
1192     }
1193 
1194     if (RtlpGetMode() == UserMode)
1195     {
1196         /* If coalescing on free is disabled in usermode, then do it here */
1197         if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)
1198         {
1199             FreeEntry = RtlpCoalesceHeap(Heap);
1200 
1201             /* If it's a suitable one - return it */
1202             if (FreeEntry &&
1203                 FreeEntry->Size >= Size)
1204             {
1205                 return FreeEntry;
1206             }
1207         }
1208     }
1209 
1210     return NULL;
1211 }
1212 
1213 /***********************************************************************
1214  *           RtlCreateHeap
1215  * RETURNS
1216  * Handle of heap: Success
1217  * NULL: Failure
1218  *
1219  * @implemented
1220  */
1221 HANDLE NTAPI
1222 RtlCreateHeap(ULONG Flags,
1223               PVOID Addr,
1224               SIZE_T TotalSize,
1225               SIZE_T CommitSize,
1226               PVOID Lock,
1227               PRTL_HEAP_PARAMETERS Parameters)
1228 {
1229     PVOID CommittedAddress = NULL, UncommittedAddress = NULL;
1230     PHEAP Heap = NULL;
1231     RTL_HEAP_PARAMETERS SafeParams = {0};
1232     ULONG_PTR MaximumUserModeAddress;
1233     SYSTEM_BASIC_INFORMATION SystemInformation;
1234     MEMORY_BASIC_INFORMATION MemoryInfo;
1235     ULONG NtGlobalFlags = RtlGetNtGlobalFlags();
1236     ULONG HeapSegmentFlags = 0;
1237     NTSTATUS Status;
1238     ULONG MaxBlockSize;
1239 
1240     /* Check for a special heap */
1241     if (RtlpPageHeapEnabled && !Addr && !Lock)
1242     {
1243         Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
1244         if (Heap) return Heap;
1245 
1246         /* Reset a special Parameters == -1 hack */
1247         if ((ULONG_PTR)Parameters == (ULONG_PTR)-1)
1248             Parameters = NULL;
1249         else
1250             DPRINT1("Enabling page heap failed\n");
1251     }
1252 
1253     /* Check validation flags */
1254     if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS) && (Flags & ~HEAP_CREATE_VALID_MASK))
1255     {
1256         DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags);
1257         Flags &= HEAP_CREATE_VALID_MASK;
1258     }
1259 
1260     /* Capture parameters */
1261     if (Parameters)
1262     {
1263         _SEH2_TRY
1264         {
1265             /* If size of structure correct, then copy it */
1266             if (Parameters->Length == sizeof(RTL_HEAP_PARAMETERS))
1267                 RtlCopyMemory(&SafeParams, Parameters, sizeof(RTL_HEAP_PARAMETERS));
1268         }
1269         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1270         {
1271             _SEH2_YIELD(return NULL);
1272         }
1273         _SEH2_END;
1274     }
1275 
1276     Parameters = &SafeParams;
1277 
1278     /* Check global flags */
1279     if (NtGlobalFlags & FLG_HEAP_DISABLE_COALESCING)
1280         Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
1281 
1282     if (NtGlobalFlags & FLG_HEAP_ENABLE_FREE_CHECK)
1283         Flags |= HEAP_FREE_CHECKING_ENABLED;
1284 
1285     if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK)
1286         Flags |= HEAP_TAIL_CHECKING_ENABLED;
1287 
1288     if (RtlpGetMode() == UserMode)
1289     {
1290         /* Also check these flags if in usermode */
1291         if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL)
1292             Flags |= HEAP_VALIDATE_ALL_ENABLED;
1293 
1294         if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS)
1295             Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
1296 
1297         if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB)
1298             Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
1299     }
1300 
1301     /* Set tunable parameters */
1302     RtlpSetHeapParameters(Parameters);
1303 
1304     /* Get the max um address */
1305     Status = ZwQuerySystemInformation(SystemBasicInformation,
1306                                       &SystemInformation,
1307                                       sizeof(SystemInformation),
1308                                       NULL);
1309 
1310     if (!NT_SUCCESS(Status))
1311     {
1312         DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status);
1313         return NULL;
1314     }
1315 
1316     MaximumUserModeAddress = SystemInformation.MaximumUserModeAddress;
1317 
1318     /* Calculate max alloc size */
1319     if (!Parameters->MaximumAllocationSize)
1320         Parameters->MaximumAllocationSize = MaximumUserModeAddress - (ULONG_PTR)0x10000 - PAGE_SIZE;
1321 
1322     MaxBlockSize = 0x80000 - PAGE_SIZE;
1323 
1324     if (!Parameters->VirtualMemoryThreshold ||
1325         Parameters->VirtualMemoryThreshold > MaxBlockSize)
1326     {
1327         Parameters->VirtualMemoryThreshold = MaxBlockSize;
1328     }
1329 
1330     /* Check reserve/commit sizes and set default values */
1331     if (!CommitSize)
1332     {
1333         CommitSize = PAGE_SIZE;
1334         if (TotalSize)
1335             TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
1336         else
1337             TotalSize = 64 * PAGE_SIZE;
1338     }
1339     else
1340     {
1341         /* Round up the commit size to be at least the page size */
1342         CommitSize = ROUND_UP(CommitSize, PAGE_SIZE);
1343 
1344         if (TotalSize)
1345             TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
1346         else
1347             TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE);
1348     }
1349 
1350     /* Call special heap */
1351     if (RtlpHeapIsSpecial(Flags))
1352         return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
1353 
1354     /* Without serialization, a lock makes no sense */
1355     if ((Flags & HEAP_NO_SERIALIZE) && (Lock != NULL))
1356         return NULL;
1357 
1358     /* See if we are already provided with an address for the heap */
1359     if (Addr)
1360     {
1361         if (Parameters->CommitRoutine)
1362         {
1363             /* There is a commit routine, so no problem here, check params */
1364             if ((Flags & HEAP_GROWABLE) ||
1365                 !Parameters->InitialCommit ||
1366                 !Parameters->InitialReserve ||
1367                 (Parameters->InitialCommit > Parameters->InitialReserve))
1368             {
1369                 /* Fail */
1370                 return NULL;
1371             }
1372 
1373             /* Calculate committed and uncommitted addresses */
1374             CommittedAddress = Addr;
1375             UncommittedAddress = (PCHAR)Addr + Parameters->InitialCommit;
1376             TotalSize = Parameters->InitialReserve;
1377 
1378             /* Zero the initial page ourselves */
1379             RtlZeroMemory(CommittedAddress, PAGE_SIZE);
1380         }
1381         else
1382         {
1383             /* Commit routine is absent, so query how much memory caller reserved */
1384             Status = ZwQueryVirtualMemory(NtCurrentProcess(),
1385                                           Addr,
1386                                           MemoryBasicInformation,
1387                                           &MemoryInfo,
1388                                           sizeof(MemoryInfo),
1389                                           NULL);
1390 
1391             if (!NT_SUCCESS(Status))
1392             {
1393                 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status);
1394                 return NULL;
1395             }
1396 
1397             /* Validate it */
1398             if (MemoryInfo.BaseAddress != Addr ||
1399                 MemoryInfo.State == MEM_FREE)
1400             {
1401                 return NULL;
1402             }
1403 
1404             /* Validation checks passed, set committed/uncommitted addresses */
1405             CommittedAddress = Addr;
1406 
1407             /* Check if it's committed or not */
1408             if (MemoryInfo.State == MEM_COMMIT)
1409             {
1410                 /* Zero it out because it's already committed */
1411                 RtlZeroMemory(CommittedAddress, PAGE_SIZE);
1412 
1413                 /* Calculate uncommitted address value */
1414                 CommitSize = MemoryInfo.RegionSize;
1415                 TotalSize = CommitSize;
1416                 UncommittedAddress = (PCHAR)Addr + CommitSize;
1417 
1418                 /* Check if uncommitted address is reserved */
1419                 Status = ZwQueryVirtualMemory(NtCurrentProcess(),
1420                                               UncommittedAddress,
1421                                               MemoryBasicInformation,
1422                                               &MemoryInfo,
1423                                               sizeof(MemoryInfo),
1424                                               NULL);
1425 
1426                 if (NT_SUCCESS(Status) &&
1427                     MemoryInfo.State == MEM_RESERVE)
1428                 {
1429                     /* It is, so add it up to the reserve size */
1430                     TotalSize += MemoryInfo.RegionSize;
1431                 }
1432             }
1433             else
1434             {
1435                 /* It's not committed, inform following code that a commit is necessary */
1436                 CommitSize = PAGE_SIZE;
1437                 UncommittedAddress = Addr;
1438             }
1439         }
1440 
1441         /* Mark this as a user-committed mem */
1442         HeapSegmentFlags = HEAP_USER_ALLOCATED;
1443         Heap = (PHEAP)Addr;
1444     }
1445     else
1446     {
1447         /* Check commit routine */
1448         if (Parameters->CommitRoutine) return NULL;
1449 
1450         /* Reserve memory */
1451         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
1452                                          (PVOID *)&Heap,
1453                                          0,
1454                                          &TotalSize,
1455                                          MEM_RESERVE,
1456                                          PAGE_READWRITE);
1457 
1458         if (!NT_SUCCESS(Status))
1459         {
1460             DPRINT1("Failed to reserve memory with status 0x%08x\n", Status);
1461             return NULL;
1462         }
1463 
1464         /* Set base addresses */
1465         CommittedAddress = Heap;
1466         UncommittedAddress = Heap;
1467     }
1468 
1469     /* Check if we need to commit something */
1470     if (CommittedAddress == UncommittedAddress)
1471     {
1472         /* Commit the required size */
1473         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
1474                                          &CommittedAddress,
1475                                          0,
1476                                          &CommitSize,
1477                                          MEM_COMMIT,
1478                                          PAGE_READWRITE);
1479 
1480         DPRINT("Committed %Iu bytes at base %p\n", CommitSize, CommittedAddress);
1481 
1482         if (!NT_SUCCESS(Status))
1483         {
1484             DPRINT1("Failure, Status 0x%08X\n", Status);
1485 
1486             /* Release memory if it was reserved */
1487             if (!Addr) ZwFreeVirtualMemory(NtCurrentProcess(),
1488                                            (PVOID *)&Heap,
1489                                            &TotalSize,
1490                                            MEM_RELEASE);
1491 
1492             return NULL;
1493         }
1494 
1495         /* Calculate new uncommitted address */
1496         UncommittedAddress = (PCHAR)UncommittedAddress + CommitSize;
1497     }
1498 
1499     /* Initialize the heap */
1500     Status = RtlpInitializeHeap(Heap, Flags, Lock, Parameters);
1501     if (!NT_SUCCESS(Status))
1502     {
1503         DPRINT1("Failed to initialize heap (%x)\n", Status);
1504         return NULL;
1505     }
1506 
1507     /* Initialize heap's first segment */
1508     Status = RtlpInitializeHeapSegment(Heap, (PHEAP_SEGMENT) (Heap), 0, HeapSegmentFlags, TotalSize, CommitSize);
1509     if (!NT_SUCCESS(Status))
1510     {
1511         DPRINT1("Failed to initialize heap segment (%x)\n", Status);
1512         return NULL;
1513     }
1514 
1515     DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap, CommitSize, TotalSize);
1516 
1517     /* Add heap to process list in case of usermode heap */
1518     if (RtlpGetMode() == UserMode)
1519     {
1520         RtlpAddHeapToProcessList(Heap);
1521 
1522         // FIXME: What about lookasides?
1523     }
1524 
1525     return Heap;
1526 }
1527 
1528 /***********************************************************************
1529  *           RtlDestroyHeap
1530  * RETURNS
1531  * TRUE: Success
1532  * FALSE: Failure
1533  *
1534  * @implemented
1535  *
1536  * RETURNS
1537  *  Success: A NULL HANDLE, if heap is NULL or it was destroyed
1538  *  Failure: The Heap handle, if heap is the process heap.
1539  */
1540 HANDLE NTAPI
1541 RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */
1542 {
1543     PHEAP Heap = (PHEAP)HeapPtr;
1544     PLIST_ENTRY Current;
1545     PHEAP_UCR_SEGMENT UcrSegment;
1546     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
1547     PVOID BaseAddress;
1548     SIZE_T Size;
1549     LONG i;
1550     PHEAP_SEGMENT Segment;
1551 
1552     if (!HeapPtr) return NULL;
1553 
1554     /* Call page heap routine if required */
1555     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpPageHeapDestroy(HeapPtr);
1556 
1557     /* Call special heap */
1558     if (RtlpHeapIsSpecial(Heap->Flags))
1559     {
1560         if (!RtlDebugDestroyHeap(Heap)) return HeapPtr;
1561     }
1562 
1563     /* Check for a process heap */
1564     if (RtlpGetMode() == UserMode &&
1565         HeapPtr == NtCurrentPeb()->ProcessHeap) return HeapPtr;
1566 
1567     /* Free up all big allocations */
1568     Current = Heap->VirtualAllocdBlocks.Flink;
1569     while (Current != &Heap->VirtualAllocdBlocks)
1570     {
1571         VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
1572         BaseAddress = (PVOID)VirtualEntry;
1573         Current = Current->Flink;
1574         Size = 0;
1575         ZwFreeVirtualMemory(NtCurrentProcess(),
1576                             &BaseAddress,
1577                             &Size,
1578                             MEM_RELEASE);
1579     }
1580 
1581     /* Delete tags and remove heap from the process heaps list in user mode */
1582     if (RtlpGetMode() == UserMode)
1583     {
1584         // FIXME DestroyTags
1585         RtlpRemoveHeapFromProcessList(Heap);
1586     }
1587 
1588     /* Delete the heap lock */
1589     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
1590     {
1591         /* Delete it if it wasn't user allocated */
1592         if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED))
1593             RtlDeleteHeapLock(Heap->LockVariable);
1594 
1595         /* Clear out the lock variable */
1596         Heap->LockVariable = NULL;
1597     }
1598 
1599     /* Free UCR segments if any were created */
1600     Current = Heap->UCRSegments.Flink;
1601     while (Current != &Heap->UCRSegments)
1602     {
1603         UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry);
1604 
1605         /* Advance to the next descriptor */
1606         Current = Current->Flink;
1607 
1608         BaseAddress = (PVOID)UcrSegment;
1609         Size = 0;
1610 
1611         /* Release that memory */
1612         ZwFreeVirtualMemory(NtCurrentProcess(),
1613                             &BaseAddress,
1614                             &Size,
1615                             MEM_RELEASE);
1616     }
1617 
1618     /* Go through segments and destroy them */
1619     for (i = HEAP_SEGMENTS - 1; i >= 0; i--)
1620     {
1621         Segment = Heap->Segments[i];
1622         if (Segment) RtlpDestroyHeapSegment(Segment);
1623     }
1624 
1625     return NULL;
1626 }
1627 
1628 PHEAP_ENTRY NTAPI
1629 RtlpSplitEntry(PHEAP Heap,
1630                ULONG Flags,
1631                PHEAP_FREE_ENTRY FreeBlock,
1632                SIZE_T AllocationSize,
1633                SIZE_T Index,
1634                SIZE_T Size)
1635 {
1636     PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
1637     UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
1638     PHEAP_ENTRY InUseEntry;
1639     SIZE_T FreeSize;
1640 
1641     /* Add extra flags in case of settable user value feature is requested,
1642        or there is a tag (small or normal) or there is a request to
1643        capture stack backtraces */
1644     if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
1645         Heap->PseudoTagEntries)
1646     {
1647         /* Add flag which means that the entry will have extra stuff attached */
1648         EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
1649 
1650         /* NB! AllocationSize is already adjusted by RtlAllocateHeap */
1651     }
1652 
1653     /* Add settable user flags, if any */
1654     EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
1655 
1656     /* Save flags, update total free size */
1657     FreeFlags = FreeBlock->Flags;
1658     Heap->TotalFreeSize -= FreeBlock->Size;
1659 
1660     /* Make this block an in-use one */
1661     InUseEntry = (PHEAP_ENTRY)FreeBlock;
1662     InUseEntry->Flags = EntryFlags;
1663     InUseEntry->SmallTagIndex = 0;
1664 
1665     /* Calculate the extra amount */
1666     FreeSize = InUseEntry->Size - Index;
1667 
1668     /* Update it's size fields (we don't need their data anymore) */
1669     InUseEntry->Size = (USHORT)Index;
1670     InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
1671 
1672     /* If there is something to split - do the split */
1673     if (FreeSize != 0)
1674     {
1675         /* Don't split if resulting entry can't contain any payload data
1676         (i.e. being just HEAP_ENTRY_SIZE) */
1677         if (FreeSize == 1)
1678         {
1679             /* Increase sizes of the in-use entry */
1680             InUseEntry->Size++;
1681             InUseEntry->UnusedBytes += sizeof(HEAP_ENTRY);
1682         }
1683         else
1684         {
1685             /* Calculate a pointer to the new entry */
1686             SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
1687 
1688             /* Initialize it */
1689             SplitBlock->Flags = FreeFlags;
1690             SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
1691             SplitBlock->Size = (USHORT)FreeSize;
1692             SplitBlock->PreviousSize = (USHORT)Index;
1693 
1694             /* Check if it's the last entry */
1695             if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
1696             {
1697                 /* Insert it to the free list if it's the last entry */
1698                 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
1699                 Heap->TotalFreeSize += FreeSize;
1700             }
1701             else
1702             {
1703                 /* Not so easy - need to update next's previous size too */
1704                 SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
1705 
1706                 if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
1707                 {
1708                     SplitBlock2->PreviousSize = (USHORT)FreeSize;
1709                     RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
1710                     Heap->TotalFreeSize += FreeSize;
1711                 }
1712                 else
1713                 {
1714                     /* Even more complex - the next entry is free, so we can merge them into one! */
1715                     SplitBlock->Flags = SplitBlock2->Flags;
1716 
1717                     /* Remove that next entry */
1718                     RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
1719 
1720                     /* Update sizes */
1721                     FreeSize += SplitBlock2->Size;
1722                     Heap->TotalFreeSize -= SplitBlock2->Size;
1723 
1724                     if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
1725                     {
1726                         /* Insert it back */
1727                         SplitBlock->Size = (USHORT)FreeSize;
1728 
1729                         /* Don't forget to update previous size of the next entry! */
1730                         if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
1731                         {
1732                             ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
1733                         }
1734 
1735                         /* Actually insert it */
1736                         RtlpInsertFreeBlockHelper(Heap, SplitBlock, (USHORT)FreeSize, FALSE);
1737 
1738                         /* Update total size */
1739                         Heap->TotalFreeSize += FreeSize;
1740                     }
1741                     else
1742                     {
1743                         /* Resulting block is quite big */
1744                         RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
1745                     }
1746                 }
1747             }
1748 
1749             /* Reset flags of the free entry */
1750             FreeFlags = 0;
1751         }
1752     }
1753 
1754     /* Set last entry flag */
1755     if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
1756         InUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
1757 
1758     return InUseEntry;
1759 }
1760 
1761 PVOID NTAPI
1762 RtlpAllocateNonDedicated(PHEAP Heap,
1763                          ULONG Flags,
1764                          SIZE_T Size,
1765                          SIZE_T AllocationSize,
1766                          SIZE_T Index,
1767                          BOOLEAN HeapLocked)
1768 {
1769     PLIST_ENTRY FreeListHead, Next;
1770     PHEAP_FREE_ENTRY FreeBlock;
1771     PHEAP_ENTRY InUseEntry;
1772     PHEAP_ENTRY_EXTRA Extra;
1773     EXCEPTION_RECORD ExceptionRecord;
1774 
1775     /* Go through the zero list to find a place where to insert the new entry */
1776     FreeListHead = &Heap->FreeLists[0];
1777 
1778     /* Start from the largest block to reduce time */
1779     Next = FreeListHead->Blink;
1780     if (FreeListHead != Next)
1781     {
1782         FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
1783 
1784         if (FreeBlock->Size >= Index)
1785         {
1786             /* Our request is smaller than the largest entry in the zero list */
1787 
1788             /* Go through the list to find insertion place */
1789             Next = FreeListHead->Flink;
1790             while (FreeListHead != Next)
1791             {
1792                 FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
1793 
1794                 if (FreeBlock->Size >= Index)
1795                 {
1796                     /* Found minimally fitting entry. Proceed to either using it as it is
1797                     or splitting it to two entries */
1798                     RemoveEntryList(&FreeBlock->FreeList);
1799 
1800                     /* Split it */
1801                     InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
1802 
1803                     /* Release the lock */
1804                     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
1805 
1806                     /* Zero memory if that was requested */
1807                     if (Flags & HEAP_ZERO_MEMORY)
1808                         RtlZeroMemory(InUseEntry + 1, Size);
1809                     else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
1810                     {
1811                         /* Fill this block with a special pattern */
1812                         RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
1813                     }
1814 
1815                     /* Fill tail of the block with a special pattern too if requested */
1816                     if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
1817                     {
1818                         RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
1819                         InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
1820                     }
1821 
1822                     /* Prepare extra if it's present */
1823                     if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
1824                     {
1825                         Extra = RtlpGetExtraStuffPointer(InUseEntry);
1826                         RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
1827 
1828                         // TODO: Tagging
1829                     }
1830 
1831                     /* Return pointer to the */
1832                     return InUseEntry + 1;
1833                 }
1834 
1835                 /* Advance to the next entry */
1836                 Next = Next->Flink;
1837             }
1838         }
1839     }
1840 
1841     /* Extend the heap, 0 list didn't have anything suitable */
1842     FreeBlock = RtlpExtendHeap(Heap, AllocationSize);
1843 
1844     /* Use the new biggest entry we've got */
1845     if (FreeBlock)
1846     {
1847         RemoveEntryList(&FreeBlock->FreeList);
1848 
1849         /* Split it */
1850         InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
1851 
1852         /* Release the lock */
1853         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
1854 
1855         /* Zero memory if that was requested */
1856         if (Flags & HEAP_ZERO_MEMORY)
1857             RtlZeroMemory(InUseEntry + 1, Size);
1858         else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
1859         {
1860             /* Fill this block with a special pattern */
1861             RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
1862         }
1863 
1864         /* Fill tail of the block with a special pattern too if requested */
1865         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
1866         {
1867             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
1868             InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
1869         }
1870 
1871         /* Prepare extra if it's present */
1872         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
1873         {
1874             Extra = RtlpGetExtraStuffPointer(InUseEntry);
1875             RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
1876 
1877             // TODO: Tagging
1878         }
1879 
1880         /* Return pointer to the */
1881         return InUseEntry + 1;
1882     }
1883 
1884     /* Really unfortunate, out of memory condition */
1885     RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
1886 
1887     /* Generate an exception */
1888     if (Flags & HEAP_GENERATE_EXCEPTIONS)
1889     {
1890         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
1891         ExceptionRecord.ExceptionRecord = NULL;
1892         ExceptionRecord.NumberParameters = 1;
1893         ExceptionRecord.ExceptionFlags = 0;
1894         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
1895 
1896         RtlRaiseException(&ExceptionRecord);
1897     }
1898 
1899     /* Release the lock */
1900     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
1901     DPRINT1("HEAP: Allocation failed!\n");
1902     DPRINT1("Flags %x\n", Heap->Flags);
1903     return NULL;
1904 }
1905 
1906 /***********************************************************************
1907  *           HeapAlloc   (KERNEL32.334)
1908  * RETURNS
1909  * Pointer to allocated memory block
1910  * NULL: Failure
1911  * 0x7d030f60--invalid flags in RtlHeapAllocate
1912  * @implemented
1913  */
1914 PVOID NTAPI
1915 RtlAllocateHeap(IN PVOID HeapPtr,
1916                 IN ULONG Flags,
1917                 IN SIZE_T Size)
1918 {
1919     PHEAP Heap = (PHEAP)HeapPtr;
1920     PULONG FreeListsInUse;
1921     ULONG FreeListsInUseUlong;
1922     SIZE_T AllocationSize;
1923     SIZE_T Index, InUseIndex, i;
1924     PLIST_ENTRY FreeListHead;
1925     PHEAP_ENTRY InUseEntry;
1926     PHEAP_FREE_ENTRY FreeBlock;
1927     UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
1928     EXCEPTION_RECORD ExceptionRecord;
1929     BOOLEAN HeapLocked = FALSE;
1930     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock = NULL;
1931     PHEAP_ENTRY_EXTRA Extra;
1932     NTSTATUS Status;
1933 
1934     /* Force flags */
1935     Flags |= Heap->ForceFlags;
1936 
1937     /* Call special heap */
1938     if (RtlpHeapIsSpecial(Flags))
1939         return RtlDebugAllocateHeap(Heap, Flags, Size);
1940 
1941     /* Check for the maximum size */
1942     if (Size >= 0x80000000)
1943     {
1944         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
1945         DPRINT1("HEAP: Allocation failed!\n");
1946         return NULL;
1947     }
1948 
1949     if (Flags & (HEAP_CREATE_ENABLE_TRACING))
1950     {
1951         DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags);
1952     }
1953 
1954     //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
1955 
1956     /* Calculate allocation size and index */
1957     if (Size)
1958         AllocationSize = Size;
1959     else
1960         AllocationSize = 1;
1961     AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
1962 
1963     /* Add extra flags in case of settable user value feature is requested,
1964        or there is a tag (small or normal) or there is a request to
1965        capture stack backtraces */
1966     if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
1967         Heap->PseudoTagEntries)
1968     {
1969         /* Add flag which means that the entry will have extra stuff attached */
1970         EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
1971 
1972         /* Account for extra stuff size */
1973         AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
1974     }
1975 
1976     /* Add settable user flags, if any */
1977     EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
1978 
1979     Index = AllocationSize >> HEAP_ENTRY_SHIFT;
1980 
1981     /* Acquire the lock if necessary */
1982     if (!(Flags & HEAP_NO_SERIALIZE))
1983     {
1984         RtlEnterHeapLock(Heap->LockVariable, TRUE);
1985         HeapLocked = TRUE;
1986     }
1987 
1988     /* Depending on the size, the allocation is going to be done from dedicated,
1989        non-dedicated lists or a virtual block of memory */
1990     if (Index < HEAP_FREELISTS)
1991     {
1992         FreeListHead = &Heap->FreeLists[Index];
1993 
1994         if (!IsListEmpty(FreeListHead))
1995         {
1996             /* There is a free entry in this list */
1997             FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
1998                                           HEAP_FREE_ENTRY,
1999                                           FreeList);
2000 
2001             /* Save flags and remove the free entry */
2002             FreeFlags = FreeBlock->Flags;
2003             RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
2004 
2005             /* Update the total free size of the heap */
2006             Heap->TotalFreeSize -= Index;
2007 
2008             /* Initialize this block */
2009             InUseEntry = (PHEAP_ENTRY)FreeBlock;
2010             InUseEntry->Flags = EntryFlags | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
2011             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
2012             InUseEntry->SmallTagIndex = 0;
2013         }
2014         else
2015         {
2016             /* Find smallest free block which this request could fit in */
2017             InUseIndex = Index >> 5;
2018             FreeListsInUse = &Heap->u.FreeListsInUseUlong[InUseIndex];
2019 
2020             /* This bit magic disables all sizes which are less than the requested allocation size */
2021             FreeListsInUseUlong = *FreeListsInUse++ & ~((1 << ((ULONG)Index & 0x1f)) - 1);
2022 
2023             /* If size is definitily more than our lists - go directly to the non-dedicated one */
2024             if (InUseIndex > 3)
2025                 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
2026 
2027             /* Go through the list */
2028             for (i = InUseIndex; i < 4; i++)
2029             {
2030                 if (FreeListsInUseUlong)
2031                 {
2032                     FreeListHead = &Heap->FreeLists[i * 32];
2033                     break;
2034                 }
2035 
2036                 if (i < 3) FreeListsInUseUlong = *FreeListsInUse++;
2037             }
2038 
2039             /* Nothing found, search in the non-dedicated list */
2040             if (i == 4)
2041                 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
2042 
2043             /* That list is found, now calculate exact block  */
2044             FreeListHead += RtlpFindLeastSetBit(FreeListsInUseUlong);
2045 
2046             /* Take this entry and remove it from the list of free blocks */
2047             FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
2048                                           HEAP_FREE_ENTRY,
2049                                           FreeList);
2050             RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
2051 
2052             /* Split it */
2053             InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
2054         }
2055 
2056         /* Release the lock */
2057         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
2058 
2059         /* Zero memory if that was requested */
2060         if (Flags & HEAP_ZERO_MEMORY)
2061             RtlZeroMemory(InUseEntry + 1, Size);
2062         else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
2063         {
2064             /* Fill this block with a special pattern */
2065             RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
2066         }
2067 
2068         /* Fill tail of the block with a special pattern too if requested */
2069         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
2070         {
2071             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
2072             InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
2073         }
2074 
2075         /* Prepare extra if it's present */
2076         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
2077         {
2078             Extra = RtlpGetExtraStuffPointer(InUseEntry);
2079             RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
2080 
2081             // TODO: Tagging
2082         }
2083 
2084         /* User data starts right after the entry's header */
2085         return InUseEntry + 1;
2086     }
2087     else if (Index <= Heap->VirtualMemoryThreshold)
2088     {
2089         /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2090         return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
2091     }
2092     else if (Heap->Flags & HEAP_GROWABLE)
2093     {
2094         /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2095         AllocationSize += sizeof(HEAP_VIRTUAL_ALLOC_ENTRY) - sizeof(HEAP_ENTRY);
2096 
2097         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
2098                                          (PVOID *)&VirtualBlock,
2099                                          0,
2100                                          &AllocationSize,
2101                                          MEM_COMMIT,
2102                                          PAGE_READWRITE);
2103 
2104         if (!NT_SUCCESS(Status))
2105         {
2106             // Set STATUS!
2107             /* Release the lock */
2108             if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
2109             DPRINT1("HEAP: Allocation failed!\n");
2110             return NULL;
2111         }
2112 
2113         /* Initialize the newly allocated block */
2114         VirtualBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
2115         ASSERT(VirtualBlock->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY));
2116         VirtualBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT;
2117         VirtualBlock->CommitSize = AllocationSize;
2118         VirtualBlock->ReserveSize = AllocationSize;
2119 
2120         /* Insert it into the list of virtual allocations */
2121         InsertTailList(&Heap->VirtualAllocdBlocks, &VirtualBlock->Entry);
2122 
2123         /* Release the lock */
2124         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
2125 
2126         /* Return pointer to user data */
2127         return VirtualBlock + 1;
2128     }
2129 
2130     /* Generate an exception */
2131     if (Flags & HEAP_GENERATE_EXCEPTIONS)
2132     {
2133         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
2134         ExceptionRecord.ExceptionRecord = NULL;
2135         ExceptionRecord.NumberParameters = 1;
2136         ExceptionRecord.ExceptionFlags = 0;
2137         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
2138 
2139         RtlRaiseException(&ExceptionRecord);
2140     }
2141 
2142     RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL);
2143 
2144     /* Release the lock */
2145     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
2146     DPRINT1("HEAP: Allocation failed!\n");
2147     return NULL;
2148 }
2149 
2150 
2151 /***********************************************************************
2152  *           HeapFree   (KERNEL32.338)
2153  * RETURNS
2154  * TRUE: Success
2155  * FALSE: Failure
2156  *
2157  * @implemented
2158  */
2159 BOOLEAN NTAPI RtlFreeHeap(
2160    HANDLE HeapPtr, /* [in] Handle of heap */
2161    ULONG Flags,   /* [in] Heap freeing flags */
2162    PVOID Ptr     /* [in] Address of memory to free */
2163 )
2164 {
2165     PHEAP Heap;
2166     PHEAP_ENTRY HeapEntry;
2167     USHORT TagIndex = 0;
2168     SIZE_T BlockSize;
2169     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
2170     BOOLEAN Locked = FALSE;
2171     NTSTATUS Status;
2172 
2173     /* Freeing NULL pointer is a legal operation */
2174     if (!Ptr) return TRUE;
2175 
2176     /* Get pointer to the heap and force flags */
2177     Heap = (PHEAP)HeapPtr;
2178     Flags |= Heap->ForceFlags;
2179 
2180     /* Call special heap */
2181     if (RtlpHeapIsSpecial(Flags))
2182         return RtlDebugFreeHeap(Heap, Flags, Ptr);
2183 
2184     /* Get pointer to the heap entry */
2185     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
2186 
2187     /* Protect with SEH in case the pointer is not valid */
2188     _SEH2_TRY
2189     {
2190         /* Check this entry, fail if it's invalid */
2191         if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY) ||
2192             (((ULONG_PTR)Ptr & 0x7) != 0) ||
2193             (HeapEntry->SegmentOffset >= HEAP_SEGMENTS))
2194         {
2195             /* This is an invalid block */
2196             DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr);
2197             RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
2198             _SEH2_YIELD(return FALSE);
2199         }
2200     }
2201     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2202     {
2203         /* The pointer was invalid */
2204         DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr);
2205         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
2206         _SEH2_YIELD(return FALSE);
2207     }
2208     _SEH2_END;
2209 
2210     /* Lock if necessary */
2211     if (!(Flags & HEAP_NO_SERIALIZE))
2212     {
2213         RtlEnterHeapLock(Heap->LockVariable, TRUE);
2214         Locked = TRUE;
2215     }
2216 
2217     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
2218     {
2219         /* Big allocation */
2220         VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
2221 
2222         /* Remove it from the list */
2223         RemoveEntryList(&VirtualEntry->Entry);
2224 
2225         // TODO: Tagging
2226 
2227         BlockSize = 0;
2228         Status = ZwFreeVirtualMemory(NtCurrentProcess(),
2229                                      (PVOID *)&VirtualEntry,
2230                                      &BlockSize,
2231                                      MEM_RELEASE);
2232 
2233         if (!NT_SUCCESS(Status))
2234         {
2235             DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
2236                 Status, Heap, Ptr, VirtualEntry);
2237             RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
2238         }
2239     }
2240     else
2241     {
2242         /* Normal allocation */
2243         BlockSize = HeapEntry->Size;
2244 
2245         // TODO: Tagging
2246 
2247         /* Coalesce in kernel mode, and in usermode if it's not disabled */
2248         if (RtlpGetMode() == KernelMode ||
2249             (RtlpGetMode() == UserMode && !(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)))
2250         {
2251             HeapEntry = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks(Heap,
2252                                                            (PHEAP_FREE_ENTRY)HeapEntry,
2253                                                            &BlockSize,
2254                                                            FALSE);
2255         }
2256 
2257         /* If there is no need to decommit the block - put it into a free list */
2258         if (BlockSize < Heap->DeCommitFreeBlockThreshold ||
2259             (Heap->TotalFreeSize + BlockSize < Heap->DeCommitTotalFreeThreshold))
2260         {
2261             /* Check if it needs to go to a 0 list */
2262             if (BlockSize > HEAP_MAX_BLOCK_SIZE)
2263             {
2264                 /* General-purpose 0 list */
2265                 RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
2266             }
2267             else
2268             {
2269                 /* Usual free list */
2270                 RtlpInsertFreeBlockHelper(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize, FALSE);
2271 
2272                 /* Assert sizes are consistent */
2273                 if (!(HeapEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
2274                 {
2275                     ASSERT((HeapEntry + BlockSize)->PreviousSize == BlockSize);
2276                 }
2277 
2278                 /* Increase the free size */
2279                 Heap->TotalFreeSize += BlockSize;
2280             }
2281 
2282 
2283             if (RtlpGetMode() == UserMode &&
2284                 TagIndex != 0)
2285             {
2286                 // FIXME: Tagging
2287                 UNIMPLEMENTED;
2288             }
2289         }
2290         else
2291         {
2292             /* Decommit this block */
2293             RtlpDeCommitFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
2294         }
2295     }
2296 
2297     /* Release the heap lock */
2298     if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
2299 
2300     return TRUE;
2301 }
2302 
2303 BOOLEAN NTAPI
2304 RtlpGrowBlockInPlace (IN PHEAP Heap,
2305                       IN ULONG Flags,
2306                       IN PHEAP_ENTRY InUseEntry,
2307                       IN SIZE_T Size,
2308                       IN SIZE_T Index)
2309 {
2310     UCHAR EntryFlags, RememberFlags;
2311     PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry;
2312     SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0;
2313     PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
2314 
2315     /* We can't grow beyond specified threshold */
2316     if (Index > Heap->VirtualMemoryThreshold)
2317         return FALSE;
2318 
2319     /* Get entry flags */
2320     EntryFlags = InUseEntry->Flags;
2321 
2322     /* Get the next free entry */
2323     FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
2324 
2325     if (EntryFlags & HEAP_ENTRY_LAST_ENTRY)
2326     {
2327         /* There is no next block, just uncommitted space. Calculate how much is needed */
2328         FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT;
2329         FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
2330 
2331         /* Find and commit those pages */
2332         FreeEntry = RtlpFindAndCommitPages(Heap,
2333                                            Heap->Segments[InUseEntry->SegmentOffset],
2334                                            &FreeSize,
2335                                            FreeEntry);
2336 
2337         /* Fail if it failed... */
2338         if (!FreeEntry) return FALSE;
2339 
2340         /* It was successful, perform coalescing */
2341         FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
2342         FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
2343 
2344         /* Check if it's enough */
2345         if (FreeSize + InUseEntry->Size < Index)
2346         {
2347             /* Still not enough */
2348             RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
2349             Heap->TotalFreeSize += FreeSize;
2350             return FALSE;
2351         }
2352 
2353         /* Remember flags of this free entry */
2354         RememberFlags = FreeEntry->Flags;
2355 
2356         /* Sum up sizes */
2357         FreeSize += InUseEntry->Size;
2358     }
2359     else
2360     {
2361         /* The next block indeed exists. Check if it's free or in use */
2362         if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
2363 
2364         /* Next entry is free, check if it can fit the block we need */
2365         FreeSize = InUseEntry->Size + FreeEntry->Size;
2366         if (FreeSize < Index) return FALSE;
2367 
2368         /* Remember flags of this free entry */
2369         RememberFlags = FreeEntry->Flags;
2370 
2371         /* Remove this block from the free list */
2372         RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
2373         Heap->TotalFreeSize -= FreeEntry->Size;
2374     }
2375 
2376     PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
2377     FreeSize -= Index;
2378 
2379     /* Don't produce too small blocks */
2380     if (FreeSize <= 2)
2381     {
2382         Index += FreeSize;
2383         FreeSize = 0;
2384     }
2385 
2386     /* Process extra stuff */
2387     if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT)
2388     {
2389         /* Calculate pointers */
2390         OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
2391         NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
2392 
2393         /* Copy contents */
2394         *NewExtra = *OldExtra;
2395 
2396         // FIXME Tagging
2397     }
2398 
2399     /* Update sizes */
2400     InUseEntry->Size = (USHORT)Index;
2401     InUseEntry->UnusedBytes = (UCHAR)((Index << HEAP_ENTRY_SHIFT) - Size);
2402 
2403     /* Check if there is a free space remaining after merging those blocks */
2404     if (!FreeSize)
2405     {
2406         /* Update flags and sizes */
2407         InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
2408 
2409         /* Either update previous size of the next entry or mark it as a last
2410            entry in the segment*/
2411         if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
2412             (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size;
2413     }
2414     else
2415     {
2416         /* Complex case, we need to split the block to give unused free space
2417            back to the heap */
2418         UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
2419         UnusedEntry->PreviousSize = (USHORT)Index;
2420         UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
2421 
2422         /* Update the following block or set the last entry in the segment */
2423         if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
2424         {
2425             /* Set flags and size */
2426             UnusedEntry->Flags = RememberFlags;
2427             UnusedEntry->Size = (USHORT)FreeSize;
2428 
2429             /* Insert it to the heap and update total size  */
2430             RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
2431             Heap->TotalFreeSize += FreeSize;
2432         }
2433         else
2434         {
2435             /* There is a block after this one  */
2436             FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
2437 
2438             if (FollowingEntry->Flags & HEAP_ENTRY_BUSY)
2439             {
2440                 /* Update flags and set size of the unused space entry */
2441                 UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY);
2442                 UnusedEntry->Size = (USHORT)FreeSize;
2443 
2444                 /* Update previous size of the following entry */
2445                 FollowingEntry->PreviousSize = (USHORT)FreeSize;
2446 
2447                 /* Insert it to the heap and update total free size */
2448                 RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
2449                 Heap->TotalFreeSize += FreeSize;
2450             }
2451             else
2452             {
2453                 /* That following entry is also free, what a fortune! */
2454                 RememberFlags = FollowingEntry->Flags;
2455 
2456                 /* Remove it */
2457                 RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE, FALSE);
2458                 Heap->TotalFreeSize -= FollowingEntry->Size;
2459 
2460                 /* And make up a new combined block */
2461                 FreeSize += FollowingEntry->Size;
2462                 UnusedEntry->Flags = RememberFlags;
2463 
2464                 /* Check where to put it */
2465                 if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
2466                 {
2467                     /* Fine for a dedicated list */
2468                     UnusedEntry->Size = (USHORT)FreeSize;
2469 
2470                     if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
2471                         ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = (USHORT)FreeSize;
2472 
2473                     /* Insert it back and update total size */
2474                     RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
2475                     Heap->TotalFreeSize += FreeSize;
2476                 }
2477                 else
2478                 {
2479                     /* The block is very large, leave all the hassle to the insertion routine */
2480                     RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize);
2481                 }
2482             }
2483         }
2484     }
2485 
2486     /* Properly "zero out" (and fill!) the space */
2487     if (Flags & HEAP_ZERO_MEMORY)
2488     {
2489         RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize);
2490     }
2491     else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
2492     {
2493         /* Calculate tail part which we need to fill */
2494         TailPart = PrevSize & (sizeof(ULONG) - 1);
2495 
2496         /* "Invert" it as usual */
2497         if (TailPart) TailPart = 4 - TailPart;
2498 
2499         if (Size > (PrevSize + TailPart))
2500             AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
2501 
2502         if (AddedSize)
2503         {
2504             RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart,
2505                                AddedSize,
2506                                ARENA_INUSE_FILLER);
2507         }
2508     }
2509 
2510     /* Fill the new tail */
2511     if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
2512     {
2513         RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
2514                       HEAP_ENTRY_SIZE,
2515                       HEAP_TAIL_FILL);
2516     }
2517 
2518     /* Copy user settable flags */
2519     InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
2520     InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
2521 
2522     /* Return success */
2523     return TRUE;
2524 }
2525 
2526 PHEAP_ENTRY_EXTRA NTAPI
2527 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry)
2528 {
2529     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
2530 
2531     /* Check if it's a big block */
2532     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
2533     {
2534         VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
2535 
2536         /* Return a pointer to the extra stuff*/
2537         return &VirtualEntry->ExtraStuff;
2538     }
2539     else
2540     {
2541         /* This is a usual entry, which means extra stuff follows this block */
2542         return (PHEAP_ENTRY_EXTRA)(HeapEntry + HeapEntry->Size - 1);
2543     }
2544 }
2545 
2546 
2547 /***********************************************************************
2548  *           RtlReAllocateHeap
2549  * PARAMS
2550  *   Heap   [in] Handle of heap block
2551  *   Flags    [in] Heap reallocation flags
2552  *   Ptr,    [in] Address of memory to reallocate
2553  *   Size     [in] Number of bytes to reallocate
2554  *
2555  * RETURNS
2556  * Pointer to reallocated memory block
2557  * NULL: Failure
2558  * 0x7d030f60--invalid flags in RtlHeapAllocate
2559  * @implemented
2560  */
2561 PVOID NTAPI
2562 RtlReAllocateHeap(HANDLE HeapPtr,
2563                   ULONG Flags,
2564                   PVOID Ptr,
2565                   SIZE_T Size)
2566 {
2567     PHEAP Heap = (PHEAP)HeapPtr;
2568     PHEAP_ENTRY InUseEntry, NewInUseEntry;
2569     PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
2570     SIZE_T AllocationSize, FreeSize, DecommitSize;
2571     BOOLEAN HeapLocked = FALSE;
2572     PVOID NewBaseAddress;
2573     PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
2574     SIZE_T OldSize, Index, OldIndex;
2575     UCHAR FreeFlags;
2576     NTSTATUS Status;
2577     PVOID DecommitBase;
2578     SIZE_T RemainderBytes, ExtraSize;
2579     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
2580     EXCEPTION_RECORD ExceptionRecord;
2581 
2582     /* Return success in case of a null pointer */
2583     if (!Ptr)
2584     {
2585         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS);
2586         return NULL;
2587     }
2588 
2589     /* Force heap flags */
2590     Flags |= Heap->ForceFlags;
2591 
2592     /* Call special heap */
2593     if (RtlpHeapIsSpecial(Flags))
2594         return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size);
2595 
2596     /* Make sure size is valid */
2597     if (Size >= 0x80000000)
2598     {
2599         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
2600         return NULL;
2601     }
2602 
2603     /* Calculate allocation size and index */
2604     if (Size)
2605         AllocationSize = Size;
2606     else
2607         AllocationSize = 1;
2608     AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
2609 
2610     /* Add up extra stuff, if it is present anywhere */
2611     if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) ||
2612         (Flags & HEAP_EXTRA_FLAGS_MASK) ||
2613         Heap->PseudoTagEntries)
2614     {
2615         AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
2616     }
2617 
2618     /* Acquire the lock if necessary */
2619     if (!(Flags & HEAP_NO_SERIALIZE))
2620     {
2621         RtlEnterHeapLock(Heap->LockVariable, TRUE);
2622         HeapLocked = TRUE;
2623         Flags &= ~HEAP_NO_SERIALIZE;
2624     }
2625 
2626     /* Get the pointer to the in-use entry */
2627     InUseEntry = (PHEAP_ENTRY)Ptr - 1;
2628 
2629     /* If that entry is not really in-use, we have a problem */
2630     if (!(InUseEntry->Flags & HEAP_ENTRY_BUSY))
2631     {
2632         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
2633 
2634         /* Release the lock and return */
2635         if (HeapLocked)
2636             RtlLeaveHeapLock(Heap->LockVariable);
2637         return Ptr;
2638     }
2639 
2640     if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
2641     {
2642         /* This is a virtually allocated block. Get its size */
2643         OldSize = RtlpGetSizeOfBigBlock(InUseEntry);
2644 
2645         /* Convert it to an index */
2646         OldIndex = (OldSize + InUseEntry->Size) >> HEAP_ENTRY_SHIFT;
2647 
2648         /* Calculate new allocation size and round it to the page size */
2649         AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
2650         AllocationSize = ROUND_UP(AllocationSize, PAGE_SIZE);
2651     }
2652     else
2653     {
2654         /* Usual entry */
2655         OldIndex = InUseEntry->Size;
2656 
2657         OldSize = (OldIndex << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
2658     }
2659 
2660     /* Calculate new index */
2661     Index = AllocationSize >> HEAP_ENTRY_SHIFT;
2662 
2663     /* Check for 4 different scenarios (old size, new size, old index, new index) */
2664     if (Index <= OldIndex)
2665     {
2666         /* Difference must be greater than 1, adjust if it's not so */
2667         if (Index + 1 == OldIndex)
2668         {
2669             Index++;
2670             AllocationSize += sizeof(HEAP_ENTRY);
2671         }
2672 
2673         /* Calculate new size */
2674         if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
2675         {
2676             /* Simple in case of a virtual alloc - just an unused size */
2677             InUseEntry->Size = (USHORT)(AllocationSize - Size);
2678             ASSERT(InUseEntry->Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY));
2679         }
2680         else if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
2681         {
2682             /* There is extra stuff, take it into account */
2683             OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
2684             NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
2685             *NewExtra = *OldExtra;
2686 
2687             // FIXME Tagging, TagIndex
2688 
2689             /* Update unused bytes count */
2690             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
2691         }
2692         else
2693         {
2694             // FIXME Tagging, SmallTagIndex
2695             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
2696         }
2697 
2698         /* If new size is bigger than the old size */
2699         if (Size > OldSize)
2700         {
2701             /* Zero out that additional space if required */
2702             if (Flags & HEAP_ZERO_MEMORY)
2703             {
2704                 RtlZeroMemory((PCHAR)Ptr + OldSize, Size - OldSize);
2705             }
2706             else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
2707             {
2708                 /* Fill it on free if required */
2709                 RemainderBytes = OldSize & (sizeof(ULONG) - 1);
2710 
2711                 if (RemainderBytes)
2712                     RemainderBytes = 4 - RemainderBytes;
2713 
2714                 if (Size > (OldSize + RemainderBytes))
2715                 {
2716                     /* Calculate actual amount of extra bytes to fill */
2717                     ExtraSize = (Size - (OldSize + RemainderBytes)) & ~(sizeof(ULONG) - 1);
2718 
2719                     /* Fill them if there are any */
2720                     if (ExtraSize != 0)
2721                     {
2722                         RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + OldSize + RemainderBytes,
2723                                            ExtraSize,
2724                                            ARENA_INUSE_FILLER);
2725                     }
2726                 }
2727             }
2728         }
2729 
2730         /* Fill tail of the heap entry if required */
2731         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
2732         {
2733             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
2734                           HEAP_ENTRY_SIZE,
2735                           HEAP_TAIL_FILL);
2736         }
2737 
2738         /* Check if the difference is significant or not */
2739         if (Index != OldIndex)
2740         {
2741             /* Save flags */
2742             FreeFlags = InUseEntry->Flags & ~HEAP_ENTRY_BUSY;
2743 
2744             if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC)
2745             {
2746                 /* This is a virtual block allocation */
2747                 VirtualAllocBlock = CONTAINING_RECORD(InUseEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
2748 
2749                 // FIXME Tagging!
2750 
2751                 DecommitBase = (PCHAR)VirtualAllocBlock + AllocationSize;
2752                 DecommitSize = (OldIndex << HEAP_ENTRY_SHIFT) - AllocationSize;
2753 
2754                 /* Release the memory */
2755                 Status = ZwFreeVirtualMemory(NtCurrentProcess(),
2756                                              (PVOID *)&DecommitBase,
2757                                              &DecommitSize,
2758                                              MEM_RELEASE);
2759 
2760                 if (!NT_SUCCESS(Status))
2761                 {
2762                     DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase, DecommitSize, Status);
2763                 }
2764                 else
2765                 {
2766                     /* Otherwise reduce the commit size */
2767                     VirtualAllocBlock->CommitSize -= DecommitSize;
2768                 }
2769             }
2770             else
2771             {
2772                 /* Reduce size of the block and possibly split it */
2773                 SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
2774 
2775                 /* Initialize this entry */
2776                 SplitBlock->Flags = FreeFlags;
2777                 SplitBlock->PreviousSize = (USHORT)Index;
2778                 SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
2779 
2780                 /* Remember free size */
2781                 FreeSize = InUseEntry->Size - Index;
2782 
2783                 /* Set new size */
2784                 InUseEntry->Size = (USHORT)Index;
2785                 InUseEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
2786 
2787                 /* Is that the last entry */
2788                 if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
2789                 {
2790                     /* Set its size and insert it to the list */
2791                     SplitBlock->Size = (USHORT)FreeSize;
2792                     RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
2793 
2794                     /* Update total free size */
2795                     Heap->TotalFreeSize += FreeSize;
2796                 }
2797                 else
2798                 {
2799                     /* Get the block after that one */
2800                     SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
2801 
2802                     if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
2803                     {
2804                         /* It's in use, add it here*/
2805                         SplitBlock->Size = (USHORT)FreeSize;
2806 
2807                         /* Update previous size of the next entry */
2808                         ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
2809 
2810                         /* Insert it to the list */
2811                         RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
2812 
2813                         /* Update total size */
2814                         Heap->TotalFreeSize += FreeSize;
2815                     }
2816                     else
2817                     {
2818                         /* Next entry is free, so merge with it */
2819                         SplitBlock->Flags = SplitBlock2->Flags;
2820 
2821                         /* Remove it, update total size */
2822                         RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
2823                         Heap->TotalFreeSize -= SplitBlock2->Size;
2824 
2825                         /* Calculate total free size */
2826                         FreeSize += SplitBlock2->Size;
2827 
2828                         if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
2829                         {
2830                             SplitBlock->Size = (USHORT)FreeSize;
2831 
2832                             if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
2833                             {
2834                                 /* Update previous size of the next entry */
2835                                 ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
2836                             }
2837 
2838                             /* Insert the new one back and update total size */
2839                             RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
2840                             Heap->TotalFreeSize += FreeSize;
2841                         }
2842                         else
2843                         {
2844                             /* Just add it */
2845                             RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
2846                         }
2847                     }
2848                 }
2849             }
2850         }
2851     }
2852     else
2853     {
2854         /* We're growing the block */
2855         if ((InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
2856             !RtlpGrowBlockInPlace(Heap, Flags, InUseEntry, Size, Index))
2857         {
2858             /* Growing in place failed, so growing out of place */
2859             if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
2860             {
2861                 DPRINT1("Realloc in place failed, but it was the only option\n");
2862                 Ptr = NULL;
2863             }
2864             else
2865             {
2866                 /* Clear tag bits */
2867                 Flags &= ~HEAP_TAG_MASK;
2868 
2869                 /* Process extra stuff */
2870                 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
2871                 {
2872                     /* Preserve user settable flags */
2873                     Flags &= ~HEAP_SETTABLE_USER_FLAGS;
2874 
2875                     Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
2876 
2877                     /* Get pointer to the old extra data */
2878                     OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
2879 
2880                     /* Save tag index if it was set */
2881                     if (OldExtra->TagIndex &&
2882                         !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG))
2883                     {
2884                         Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT;
2885                     }
2886                 }
2887                 else if (InUseEntry->SmallTagIndex)
2888                 {
2889                     /* Take small tag index into account */
2890                     Flags |= InUseEntry->SmallTagIndex << HEAP_TAG_SHIFT;
2891                 }
2892 
2893                 /* Allocate new block from the heap */
2894                 NewBaseAddress = RtlAllocateHeap(HeapPtr,
2895                                                  Flags & ~HEAP_ZERO_MEMORY,
2896                                                  Size);
2897 
2898                 /* Proceed if it didn't fail */
2899                 if (NewBaseAddress)
2900                 {
2901                     /* Get new entry pointer */
2902                     NewInUseEntry = (PHEAP_ENTRY)NewBaseAddress - 1;
2903 
2904                     /* Process extra stuff if it exists */
2905                     if (NewInUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
2906                     {
2907                         NewExtra = RtlpGetExtraStuffPointer(NewInUseEntry);
2908 
2909                         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
2910                         {
2911                             OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
2912                             NewExtra->Settable = OldExtra->Settable;
2913                         }
2914                         else
2915                         {
2916                             RtlZeroMemory(NewExtra, sizeof(*NewExtra));
2917                         }
2918                     }
2919 
2920                     /* Copy actual user bits */
2921                     if (Size < OldSize)
2922                         RtlMoveMemory(NewBaseAddress, Ptr, Size);
2923                     else
2924                         RtlMoveMemory(NewBaseAddress, Ptr, OldSize);
2925 
2926                     /* Zero remaining part if required */
2927                     if (Size > OldSize &&
2928                         (Flags & HEAP_ZERO_MEMORY))
2929                     {
2930                         RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize);
2931                     }
2932 
2933                     /* Free the old block */
2934                     RtlFreeHeap(HeapPtr, Flags, Ptr);
2935                 }
2936 
2937                 Ptr = NewBaseAddress;
2938             }
2939         }
2940     }
2941 
2942     /* Did resizing fail? */
2943     if (!Ptr && (Flags & HEAP_GENERATE_EXCEPTIONS))
2944     {
2945         /* Generate an exception if required */
2946         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
2947         ExceptionRecord.ExceptionRecord = NULL;
2948         ExceptionRecord.NumberParameters = 1;
2949         ExceptionRecord.ExceptionFlags = 0;
2950         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
2951 
2952         RtlRaiseException(&ExceptionRecord);
2953     }
2954 
2955     /* Release the heap lock if it was acquired */
2956     if (HeapLocked)
2957         RtlLeaveHeapLock(Heap->LockVariable);
2958 
2959     return Ptr;
2960 }
2961 
2962 
2963 /***********************************************************************
2964  *           RtlCompactHeap
2965  *
2966  * @unimplemented
2967  */
2968 ULONG NTAPI
2969 RtlCompactHeap(HANDLE Heap,
2970 		ULONG Flags)
2971 {
2972    UNIMPLEMENTED;
2973    return 0;
2974 }
2975 
2976 
2977 /***********************************************************************
2978  *           RtlLockHeap
2979  * Attempts to acquire the critical section object for a specified heap.
2980  *
2981  * PARAMS
2982  *   Heap  [in] Handle of heap to lock for exclusive access
2983  *
2984  * RETURNS
2985  * TRUE: Success
2986  * FALSE: Failure
2987  *
2988  * @implemented
2989  */
2990 BOOLEAN NTAPI
2991 RtlLockHeap(IN HANDLE HeapPtr)
2992 {
2993     PHEAP Heap = (PHEAP)HeapPtr;
2994 
2995     /* Check for page heap */
2996     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
2997     {
2998         return RtlpPageHeapLock(Heap);
2999     }
3000 
3001     /* Check if it's really a heap */
3002     if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
3003 
3004     /* Lock if it's lockable */
3005     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
3006     {
3007         RtlEnterHeapLock(Heap->LockVariable, TRUE);
3008     }
3009 
3010     return TRUE;
3011 }
3012 
3013 
3014 /***********************************************************************
3015  *           RtlUnlockHeap
3016  * Releases ownership of the critical section object.
3017  *
3018  * PARAMS
3019  *   Heap  [in] Handle to the heap to unlock
3020  *
3021  * RETURNS
3022  * TRUE: Success
3023  * FALSE: Failure
3024  *
3025  * @implemented
3026  */
3027 BOOLEAN NTAPI
3028 RtlUnlockHeap(HANDLE HeapPtr)
3029 {
3030     PHEAP Heap = (PHEAP)HeapPtr;
3031 
3032     /* Check for page heap */
3033     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
3034     {
3035         return RtlpPageHeapUnlock(Heap);
3036     }
3037 
3038     /* Check if it's really a heap */
3039     if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
3040 
3041     /* Unlock if it's lockable */
3042     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
3043     {
3044         RtlLeaveHeapLock(Heap->LockVariable);
3045     }
3046 
3047     return TRUE;
3048 }
3049 
3050 
3051 /***********************************************************************
3052  *           RtlSizeHeap
3053  * PARAMS
3054  *   Heap  [in] Handle of heap
3055  *   Flags   [in] Heap size control flags
3056  *   Ptr     [in] Address of memory to return size for
3057  *
3058  * RETURNS
3059  * Size in bytes of allocated memory
3060  * 0xffffffff: Failure
3061  *
3062  * @implemented
3063  */
3064 SIZE_T NTAPI
3065 RtlSizeHeap(
3066    HANDLE HeapPtr,
3067    ULONG Flags,
3068    PVOID Ptr
3069 )
3070 {
3071     PHEAP Heap = (PHEAP)HeapPtr;
3072     PHEAP_ENTRY HeapEntry;
3073     SIZE_T EntrySize;
3074 
3075     // FIXME This is a hack around missing SEH support!
3076     if (!Heap)
3077     {
3078         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE);
3079         return (SIZE_T)-1;
3080     }
3081 
3082     /* Force flags */
3083     Flags |= Heap->ForceFlags;
3084 
3085     /* Call special heap */
3086     if (RtlpHeapIsSpecial(Flags))
3087         return RtlDebugSizeHeap(Heap, Flags, Ptr);
3088 
3089     /* Get the heap entry pointer */
3090     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
3091 
3092     /* Return -1 if that entry is free */
3093     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
3094     {
3095         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
3096         return (SIZE_T)-1;
3097     }
3098 
3099     /* Get size of this block depending if it's a usual or a big one */
3100     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
3101     {
3102         EntrySize = RtlpGetSizeOfBigBlock(HeapEntry);
3103     }
3104     else
3105     {
3106         /* Calculate it */
3107         EntrySize = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
3108     }
3109 
3110     /* Return calculated size */
3111     return EntrySize;
3112 }
3113 
3114 BOOLEAN NTAPI
3115 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry)
3116 {
3117     SIZE_T Size, Result;
3118     PCHAR TailPart;
3119 
3120     /* Calculate size */
3121     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
3122         Size = RtlpGetSizeOfBigBlock(HeapEntry);
3123     else
3124         Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
3125 
3126     /* Calculate pointer to the tail part of the block */
3127     TailPart = (PCHAR)(HeapEntry + 1) + Size;
3128 
3129     /* Compare tail pattern */
3130     Result = RtlCompareMemory(TailPart,
3131                               FillPattern,
3132                               HEAP_ENTRY_SIZE);
3133 
3134     if (Result != HEAP_ENTRY_SIZE)
3135     {
3136         DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result);
3137         return FALSE;
3138     }
3139 
3140     /* All is fine */
3141     return TRUE;
3142 }
3143 
3144 BOOLEAN NTAPI
3145 RtlpValidateHeapHeaders(
3146     PHEAP Heap,
3147     BOOLEAN Recalculate)
3148 {
3149     // We skip header validation for now
3150     return TRUE;
3151 }
3152 
3153 BOOLEAN NTAPI
3154 RtlpValidateHeapEntry(
3155     PHEAP Heap,
3156     PHEAP_ENTRY HeapEntry)
3157 {
3158     BOOLEAN BigAllocation, EntryFound = FALSE;
3159     PHEAP_SEGMENT Segment;
3160     ULONG SegmentOffset;
3161 
3162     /* Perform various consistency checks of this entry */
3163     if (!HeapEntry) goto invalid_entry;
3164     if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry;
3165     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry;
3166 
3167     BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC;
3168     Segment = Heap->Segments[HeapEntry->SegmentOffset];
3169 
3170     if (BigAllocation &&
3171         (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock)))
3172          goto invalid_entry;
3173 
3174     if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS ||
3175         !Segment ||
3176         HeapEntry < Segment->FirstEntry ||
3177         HeapEntry >= Segment->LastValidEntry))
3178         goto invalid_entry;
3179 
3180     if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) &&
3181         !RtlpCheckInUsePattern(HeapEntry))
3182         goto invalid_entry;
3183 
3184     /* Checks are done, if this is a virtual entry, that's all */
3185     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE;
3186 
3187     /* Go through segments and check if this entry fits into any of them */
3188     for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
3189     {
3190         Segment = Heap->Segments[SegmentOffset];
3191         if (!Segment) continue;
3192 
3193         if ((HeapEntry >= Segment->FirstEntry) &&
3194             (HeapEntry < Segment->LastValidEntry))
3195         {
3196             /* Got it */
3197             EntryFound = TRUE;
3198             break;
3199         }
3200     }
3201 
3202     /* Return our result of finding entry in the segments */
3203     return EntryFound;
3204 
3205 invalid_entry:
3206     DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap);
3207     return FALSE;
3208 }
3209 
3210 BOOLEAN NTAPI
3211 RtlpValidateHeapSegment(
3212     PHEAP Heap,
3213     PHEAP_SEGMENT Segment,
3214     UCHAR SegmentOffset,
3215     PULONG FreeEntriesCount,
3216     PSIZE_T TotalFreeSize,
3217     PSIZE_T TagEntries,
3218     PSIZE_T PseudoTagEntries)
3219 {
3220     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
3221     PLIST_ENTRY UcrEntry;
3222     SIZE_T ByteSize, Size, Result;
3223     PHEAP_ENTRY CurrentEntry;
3224     ULONG UnCommittedPages;
3225     ULONG UnCommittedRanges;
3226     ULONG PreviousSize;
3227 
3228     UnCommittedPages = 0;
3229     UnCommittedRanges = 0;
3230 
3231     if (IsListEmpty(&Segment->UCRSegmentList))
3232     {
3233         UcrEntry = NULL;
3234         UcrDescriptor = NULL;
3235     }
3236     else
3237     {
3238         UcrEntry = Segment->UCRSegmentList.Flink;
3239         UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
3240     }
3241 
3242     if (Segment->BaseAddress == Heap)
3243         CurrentEntry = &Heap->Entry;
3244     else
3245         CurrentEntry = &Segment->Entry;
3246 
3247     while (CurrentEntry < Segment->LastValidEntry)
3248     {
3249         if (UcrDescriptor &&
3250             ((PVOID)CurrentEntry >= UcrDescriptor->Address))
3251         {
3252             DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
3253                     CurrentEntry, UcrDescriptor->Address,
3254                     (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
3255 
3256             return FALSE;
3257         }
3258 
3259         PreviousSize = 0;
3260 
3261         while (CurrentEntry < Segment->LastValidEntry)
3262         {
3263             if (PreviousSize != CurrentEntry->PreviousSize)
3264             {
3265                 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
3266                     CurrentEntry, CurrentEntry->PreviousSize, PreviousSize);
3267 
3268                 return FALSE;
3269             }
3270 
3271             PreviousSize = CurrentEntry->Size;
3272             Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT;
3273 
3274             if (CurrentEntry->Flags & HEAP_ENTRY_BUSY)
3275             {
3276                 if (TagEntries)
3277                 {
3278                     UNIMPLEMENTED;
3279                 }
3280 
3281                 /* Check fill pattern */
3282                 if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)
3283                 {
3284                     if (!RtlpCheckInUsePattern(CurrentEntry))
3285                         return FALSE;
3286                 }
3287             }
3288             else
3289             {
3290                 /* The entry is free, increase free entries count and total free size */
3291                 *FreeEntriesCount = *FreeEntriesCount + 1;
3292                 *TotalFreeSize += CurrentEntry->Size;
3293 
3294                 if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) &&
3295                     (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
3296                 {
3297                     ByteSize = Size - sizeof(HEAP_FREE_ENTRY);
3298 
3299                     if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) &&
3300                         (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA)))
3301                     {
3302                         ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
3303                     }
3304 
3305                     Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1),
3306                                                     ByteSize,
3307                                                     ARENA_FREE_FILLER);
3308 
3309                     if (Result != ByteSize)
3310                     {
3311                         DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
3312                             CurrentEntry,
3313                             (PCHAR)(CurrentEntry + 1) + Result);
3314 
3315                         return FALSE;
3316                     }
3317                 }
3318             }
3319 
3320             if (CurrentEntry->SegmentOffset != SegmentOffset)
3321             {
3322                 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n",
3323                         CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset);
3324                 return FALSE;
3325             }
3326 
3327             /* Check if it's the last entry */
3328             if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY)
3329             {
3330                 CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
3331 
3332                 if (!UcrDescriptor)
3333                 {
3334                     /* Check if it's not really the last one */
3335                     if (CurrentEntry != Segment->LastValidEntry)
3336                     {
3337                         DPRINT1("HEAP: Heap entry %p is not last block in segment (%p)\n",
3338                                 CurrentEntry, Segment->LastValidEntry);
3339                         return FALSE;
3340                     }
3341                 }
3342                 else if (CurrentEntry != UcrDescriptor->Address)
3343                 {
3344                     DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
3345                         CurrentEntry, UcrDescriptor->Address);
3346 
3347                     return FALSE;
3348                 }
3349                 else
3350                 {
3351                     UnCommittedPages += (ULONG)(UcrDescriptor->Size / PAGE_SIZE);
3352                     UnCommittedRanges++;
3353 
3354                     CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
3355 
3356                     /* Go to the next UCR descriptor */
3357                     UcrEntry = UcrEntry->Flink;
3358                     if (UcrEntry == &Segment->UCRSegmentList)
3359                     {
3360                         UcrEntry = NULL;
3361                         UcrDescriptor = NULL;
3362                     }
3363                     else
3364                     {
3365                         UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
3366                     }
3367                 }
3368 
3369                 break;
3370             }
3371 
3372             /* Advance to the next entry */
3373             CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
3374         }
3375     }
3376 
3377     /* Check total numbers of UCP and UCR */
3378     if (Segment->NumberOfUnCommittedPages != UnCommittedPages)
3379     {
3380         DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
3381             Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages);
3382 
3383         return FALSE;
3384     }
3385 
3386     if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges)
3387     {
3388         DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
3389             Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges);
3390 
3391         return FALSE;
3392     }
3393 
3394     return TRUE;
3395 }
3396 
3397 BOOLEAN NTAPI
3398 RtlpValidateHeap(PHEAP Heap,
3399                  BOOLEAN ForceValidation)
3400 {
3401     PHEAP_SEGMENT Segment;
3402     BOOLEAN EmptyList;
3403     UCHAR SegmentOffset;
3404     SIZE_T Size, TotalFreeSize;
3405     ULONG PreviousSize;
3406     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
3407     PLIST_ENTRY ListHead, NextEntry;
3408     PHEAP_FREE_ENTRY FreeEntry;
3409     ULONG FreeBlocksCount, FreeListEntriesCount;
3410 
3411     /* Check headers */
3412     if (!RtlpValidateHeapHeaders(Heap, FALSE))
3413         return FALSE;
3414 
3415     /* Skip validation if it's not needed */
3416     if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED))
3417         return TRUE;
3418 
3419     /* Check free lists bitmaps */
3420     FreeListEntriesCount = 0;
3421     ListHead = &Heap->FreeLists[0];
3422 
3423     for (Size = 0; Size < HEAP_FREELISTS; Size++)
3424     {
3425         if (Size)
3426         {
3427             /* This is a dedicated list. Check if it's empty */
3428             EmptyList = IsListEmpty(ListHead);
3429 
3430             if (Heap->u.FreeListsInUseBytes[Size >> 3] & (1 << (Size & 7)))
3431             {
3432                 if (EmptyList)
3433                 {
3434                     DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size);
3435                     return FALSE;
3436                 }
3437             }
3438             else
3439             {
3440                 if (!EmptyList)
3441                 {
3442                     DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size);
3443                     return FALSE;
3444                 }
3445             }
3446         }
3447 
3448         /* Now check this list entries */
3449         NextEntry = ListHead->Flink;
3450         PreviousSize = 0;
3451 
3452         while (ListHead != NextEntry)
3453         {
3454             FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList);
3455             NextEntry = NextEntry->Flink;
3456 
3457             /* If there is an in-use entry in a free list - that's quite a big problem */
3458             if (FreeEntry->Flags & HEAP_ENTRY_BUSY)
3459             {
3460                 DPRINT1("HEAP: %Ix-dedicated list free element %p is marked in-use\n", Size, FreeEntry);
3461                 return FALSE;
3462             }
3463 
3464             /* Check sizes according to that specific list's size */
3465             if ((Size == 0) && (FreeEntry->Size < HEAP_FREELISTS))
3466             {
3467                 DPRINT1("HEAP: Non dedicated list free element %p has size %x which would fit a dedicated list\n", FreeEntry, FreeEntry->Size);
3468                 return FALSE;
3469             }
3470             else if (Size && (FreeEntry->Size != Size))
3471             {
3472                 DPRINT1("HEAP: %Ix-dedicated list free element %p has incorrect size %x\n", Size, FreeEntry, FreeEntry->Size);
3473                 return FALSE;
3474             }
3475             else if ((Size == 0) && (FreeEntry->Size < PreviousSize))
3476             {
3477                 DPRINT1("HEAP: Non dedicated list free element %p is not put in order\n", FreeEntry);
3478                 return FALSE;
3479             }
3480 
3481             /* Remember previous size*/
3482             PreviousSize = FreeEntry->Size;
3483 
3484             /* Add up to the total amount of free entries */
3485             FreeListEntriesCount++;
3486         }
3487 
3488         /* Go to the head of the next free list */
3489         ListHead++;
3490     }
3491 
3492     /* Check big allocations */
3493     ListHead = &Heap->VirtualAllocdBlocks;
3494     NextEntry = ListHead->Flink;
3495 
3496     while (ListHead != NextEntry)
3497     {
3498         VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
3499 
3500         /* We can only check the fill pattern */
3501         if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN)
3502         {
3503             if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock))
3504                 return FALSE;
3505         }
3506 
3507         NextEntry = NextEntry->Flink;
3508     }
3509 
3510     /* Check all segments */
3511     FreeBlocksCount = 0;
3512     TotalFreeSize = 0;
3513 
3514     for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
3515     {
3516         Segment = Heap->Segments[SegmentOffset];
3517 
3518         /* Go to the next one if there is no segment */
3519         if (!Segment) continue;
3520 
3521         if (!RtlpValidateHeapSegment(Heap,
3522                                      Segment,
3523                                      SegmentOffset,
3524                                      &FreeBlocksCount,
3525                                      &TotalFreeSize,
3526                                      NULL,
3527                                      NULL))
3528         {
3529             return FALSE;
3530         }
3531     }
3532 
3533     if (FreeListEntriesCount != FreeBlocksCount)
3534     {
3535         DPRINT1("HEAP: Free blocks count in arena (%lu) does not match free blocks number in the free lists (%lu)\n", FreeBlocksCount, FreeListEntriesCount);
3536         return FALSE;
3537     }
3538 
3539     if (Heap->TotalFreeSize != TotalFreeSize)
3540     {
3541         DPRINT1("HEAP: Total size of free blocks in arena (%Iu) does not equal to the one in heap header (%Iu)\n", TotalFreeSize, Heap->TotalFreeSize);
3542         return FALSE;
3543     }
3544 
3545     return TRUE;
3546 }
3547 
3548 /***********************************************************************
3549  *           RtlValidateHeap
3550  * Validates a specified heap.
3551  *
3552  * PARAMS
3553  *   Heap  [in] Handle to the heap
3554  *   Flags   [in] Bit flags that control access during operation
3555  *   Block  [in] Optional pointer to memory block to validate
3556  *
3557  * NOTES
3558  * Flags is ignored.
3559  *
3560  * RETURNS
3561  * TRUE: Success
3562  * FALSE: Failure
3563  *
3564  * @implemented
3565  */
3566 BOOLEAN NTAPI RtlValidateHeap(
3567    HANDLE HeapPtr,
3568    ULONG Flags,
3569    PVOID Block
3570 )
3571 {
3572     PHEAP Heap = (PHEAP)HeapPtr;
3573     BOOLEAN HeapLocked = FALSE;
3574     BOOLEAN HeapValid;
3575 
3576     /* Check for page heap */
3577     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
3578         return RtlpDebugPageHeapValidate(HeapPtr, Flags, Block);
3579 
3580     /* Check signature */
3581     if (Heap->Signature != HEAP_SIGNATURE)
3582     {
3583         DPRINT1("HEAP: Signature %lx is invalid for heap %p\n", Heap->Signature, Heap);
3584         return FALSE;
3585     }
3586 
3587     /* Force flags */
3588     Flags = Heap->ForceFlags;
3589 
3590     /* Acquire the lock if necessary */
3591     if (!(Flags & HEAP_NO_SERIALIZE))
3592     {
3593         RtlEnterHeapLock(Heap->LockVariable, TRUE);
3594         HeapLocked = TRUE;
3595     }
3596 
3597     /* Either validate whole heap or just one entry */
3598     if (!Block)
3599         HeapValid = RtlpValidateHeap(Heap, TRUE);
3600     else
3601         HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1);
3602 
3603     /* Unlock if it's lockable */
3604     if (HeapLocked)
3605     {
3606         RtlLeaveHeapLock(Heap->LockVariable);
3607     }
3608 
3609     return HeapValid;
3610 }
3611 
3612 /*
3613  * @implemented
3614  */
3615 NTSTATUS NTAPI
3616 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine,
3617                     PVOID lParam)
3618 {
3619     UNIMPLEMENTED;
3620     return STATUS_NOT_IMPLEMENTED;
3621 }
3622 
3623 
3624 /*
3625  * @implemented
3626  */
3627 ULONG NTAPI
3628 RtlGetProcessHeaps(ULONG count,
3629                    HANDLE *heaps)
3630 {
3631     UNIMPLEMENTED;
3632     return 0;
3633 }
3634 
3635 
3636 /*
3637  * @implemented
3638  */
3639 BOOLEAN NTAPI
3640 RtlValidateProcessHeaps(VOID)
3641 {
3642     UNIMPLEMENTED;
3643     return TRUE;
3644 }
3645 
3646 
3647 /*
3648  * @unimplemented
3649  */
3650 BOOLEAN NTAPI
3651 RtlZeroHeap(
3652     IN PVOID HeapHandle,
3653     IN ULONG Flags
3654     )
3655 {
3656     UNIMPLEMENTED;
3657     return FALSE;
3658 }
3659 
3660 /*
3661  * @implemented
3662  */
3663 BOOLEAN
3664 NTAPI
3665 RtlSetUserValueHeap(IN PVOID HeapHandle,
3666                     IN ULONG Flags,
3667                     IN PVOID BaseAddress,
3668                     IN PVOID UserValue)
3669 {
3670     PHEAP Heap = (PHEAP)HeapHandle;
3671     PHEAP_ENTRY HeapEntry;
3672     PHEAP_ENTRY_EXTRA Extra;
3673     BOOLEAN HeapLocked = FALSE, ValueSet = FALSE;
3674 
3675     /* Force flags */
3676     Flags |= Heap->Flags;
3677 
3678     /* Call special heap */
3679     if (RtlpHeapIsSpecial(Flags))
3680         return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue);
3681 
3682     /* Lock if it's lockable */
3683     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
3684     {
3685         RtlEnterHeapLock(Heap->LockVariable, TRUE);
3686         HeapLocked = TRUE;
3687     }
3688 
3689     /* Get a pointer to the entry */
3690     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
3691 
3692     /* If it's a free entry - return error */
3693     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
3694     {
3695         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
3696 
3697         /* Release the heap lock if it was acquired */
3698         if (HeapLocked)
3699             RtlLeaveHeapLock(Heap->LockVariable);
3700 
3701         return FALSE;
3702     }
3703 
3704     /* Check if this entry has an extra stuff associated with it */
3705     if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
3706     {
3707         /* Use extra to store the value */
3708         Extra = RtlpGetExtraStuffPointer(HeapEntry);
3709         Extra->Settable = (ULONG_PTR)UserValue;
3710 
3711         /* Indicate that value was set */
3712         ValueSet = TRUE;
3713     }
3714 
3715     /* Release the heap lock if it was acquired */
3716     if (HeapLocked)
3717         RtlLeaveHeapLock(Heap->LockVariable);
3718 
3719     return ValueSet;
3720 }
3721 
3722 /*
3723  * @implemented
3724  */
3725 BOOLEAN
3726 NTAPI
3727 RtlSetUserFlagsHeap(IN PVOID HeapHandle,
3728                     IN ULONG Flags,
3729                     IN PVOID BaseAddress,
3730                     IN ULONG UserFlagsReset,
3731                     IN ULONG UserFlagsSet)
3732 {
3733     PHEAP Heap = (PHEAP)HeapHandle;
3734     PHEAP_ENTRY HeapEntry;
3735     BOOLEAN HeapLocked = FALSE;
3736 
3737     /* Force flags */
3738     Flags |= Heap->Flags;
3739 
3740     /* Call special heap */
3741     if (RtlpHeapIsSpecial(Flags))
3742         return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
3743 
3744     /* Lock if it's lockable */
3745     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
3746     {
3747         RtlEnterHeapLock(Heap->LockVariable, TRUE);
3748         HeapLocked = TRUE;
3749     }
3750 
3751     /* Get a pointer to the entry */
3752     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
3753 
3754     /* If it's a free entry - return error */
3755     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
3756     {
3757         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
3758 
3759         /* Release the heap lock if it was acquired */
3760         if (HeapLocked)
3761             RtlLeaveHeapLock(Heap->LockVariable);
3762 
3763         return FALSE;
3764     }
3765 
3766     /* Set / reset flags */
3767     HeapEntry->Flags &= ~(UserFlagsReset >> 4);
3768     HeapEntry->Flags |= (UserFlagsSet >> 4);
3769 
3770     /* Release the heap lock if it was acquired */
3771     if (HeapLocked)
3772         RtlLeaveHeapLock(Heap->LockVariable);
3773 
3774     return TRUE;
3775 }
3776 
3777 /*
3778  * @implemented
3779  */
3780 BOOLEAN
3781 NTAPI
3782 RtlGetUserInfoHeap(IN PVOID HeapHandle,
3783                    IN ULONG Flags,
3784                    IN PVOID BaseAddress,
3785                    OUT PVOID *UserValue,
3786                    OUT PULONG UserFlags)
3787 {
3788     PHEAP Heap = (PHEAP)HeapHandle;
3789     PHEAP_ENTRY HeapEntry;
3790     PHEAP_ENTRY_EXTRA Extra;
3791     BOOLEAN HeapLocked = FALSE;
3792 
3793     /* Force flags */
3794     Flags |= Heap->Flags;
3795 
3796     /* Call special heap */
3797     if (RtlpHeapIsSpecial(Flags))
3798         return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags);
3799 
3800     /* Lock if it's lockable */
3801     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
3802     {
3803         RtlEnterHeapLock(Heap->LockVariable, TRUE);
3804         HeapLocked = TRUE;
3805     }
3806 
3807     /* Get a pointer to the entry */
3808     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
3809 
3810     /* If it's a free entry - return error */
3811     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
3812     {
3813         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
3814 
3815         /* Release the heap lock if it was acquired */
3816         if (HeapLocked)
3817             RtlLeaveHeapLock(Heap->LockVariable);
3818 
3819         return FALSE;
3820     }
3821 
3822     /* Check if this entry has an extra stuff associated with it */
3823     if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
3824     {
3825         /* Get pointer to extra data */
3826         Extra = RtlpGetExtraStuffPointer(HeapEntry);
3827 
3828         /* Pass user value */
3829         if (UserValue)
3830             *UserValue = (PVOID)Extra->Settable;
3831     }
3832 
3833     /* Decode and return user flags */
3834     if (UserFlags)
3835         *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
3836 
3837     /* Release the heap lock if it was acquired */
3838     if (HeapLocked)
3839         RtlLeaveHeapLock(Heap->LockVariable);
3840 
3841     return TRUE;
3842 }
3843 
3844 /*
3845  * @unimplemented
3846  */
3847 NTSTATUS
3848 NTAPI
3849 RtlUsageHeap(IN HANDLE Heap,
3850              IN ULONG Flags,
3851              OUT PRTL_HEAP_USAGE Usage)
3852 {
3853     /* TODO */
3854     UNIMPLEMENTED;
3855     return STATUS_NOT_IMPLEMENTED;
3856 }
3857 
3858 PWSTR
3859 NTAPI
3860 RtlQueryTagHeap(IN PVOID HeapHandle,
3861                 IN ULONG Flags,
3862                 IN USHORT TagIndex,
3863                 IN BOOLEAN ResetCounters,
3864                 OUT PRTL_HEAP_TAG_INFO HeapTagInfo)
3865 {
3866     /* TODO */
3867     UNIMPLEMENTED;
3868     return NULL;
3869 }
3870 
3871 ULONG
3872 NTAPI
3873 RtlExtendHeap(IN HANDLE Heap,
3874               IN ULONG Flags,
3875               IN PVOID P,
3876               IN SIZE_T Size)
3877 {
3878     /* TODO */
3879     UNIMPLEMENTED;
3880     return 0;
3881 }
3882 
3883 ULONG
3884 NTAPI
3885 RtlCreateTagHeap(IN HANDLE HeapHandle,
3886                  IN ULONG Flags,
3887                  IN PWSTR TagName,
3888                  IN PWSTR TagSubName)
3889 {
3890     /* TODO */
3891     UNIMPLEMENTED;
3892     return 0;
3893 }
3894 
3895 NTSTATUS
3896 NTAPI
3897 RtlWalkHeap(IN HANDLE HeapHandle,
3898             IN PVOID HeapEntry)
3899 {
3900     UNIMPLEMENTED;
3901     return STATUS_NOT_IMPLEMENTED;
3902 }
3903 
3904 PVOID
3905 NTAPI
3906 RtlProtectHeap(IN PVOID HeapHandle,
3907                IN BOOLEAN ReadOnly)
3908 {
3909     UNIMPLEMENTED;
3910     return NULL;
3911 }
3912 
3913 NTSTATUS
3914 NTAPI
3915 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
3916                       IN HEAP_INFORMATION_CLASS HeapInformationClass,
3917                       IN PVOID HeapInformation,
3918                       IN SIZE_T HeapInformationLength)
3919 {
3920     /* Setting heap information is not really supported except for enabling LFH */
3921     if (HeapInformationClass == HeapCompatibilityInformation)
3922     {
3923         /* Check buffer length */
3924         if (HeapInformationLength < sizeof(ULONG))
3925         {
3926             /* The provided buffer is too small */
3927             return STATUS_BUFFER_TOO_SMALL;
3928         }
3929 
3930         /* Check for a special magic value for enabling LFH */
3931         if (*(PULONG)HeapInformation != 2)
3932         {
3933             return STATUS_UNSUCCESSFUL;
3934         }
3935 
3936         DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
3937         return STATUS_SUCCESS;
3938     }
3939 
3940     return STATUS_SUCCESS;
3941 }
3942 
3943 NTSTATUS
3944 NTAPI
3945 RtlQueryHeapInformation(HANDLE HeapHandle,
3946                         HEAP_INFORMATION_CLASS HeapInformationClass,
3947                         PVOID HeapInformation,
3948                         SIZE_T HeapInformationLength,
3949                         PSIZE_T ReturnLength OPTIONAL)
3950 {
3951     PHEAP Heap = (PHEAP)HeapHandle;
3952 
3953     /* Only HeapCompatibilityInformation is supported */
3954     if (HeapInformationClass == HeapCompatibilityInformation)
3955     {
3956         /* Set result length */
3957         if (ReturnLength)
3958             *ReturnLength = sizeof(ULONG);
3959 
3960         /* Check buffer length */
3961         if (HeapInformationLength < sizeof(ULONG))
3962         {
3963             /* It's too small, return needed length */
3964             return STATUS_BUFFER_TOO_SMALL;
3965         }
3966 
3967         /* Return front end heap type */
3968         *(PULONG)HeapInformation = Heap->FrontEndHeapType;
3969 
3970         return STATUS_SUCCESS;
3971     }
3972 
3973     return STATUS_UNSUCCESSFUL;
3974 }
3975 
3976 /* @implemented */
3977 ULONG
3978 NTAPI
3979 RtlMultipleAllocateHeap(IN PVOID HeapHandle,
3980                         IN ULONG Flags,
3981                         IN SIZE_T Size,
3982                         IN ULONG Count,
3983                         OUT PVOID *Array)
3984 {
3985     ULONG Index;
3986     EXCEPTION_RECORD ExceptionRecord;
3987 
3988     for (Index = 0; Index < Count; ++Index)
3989     {
3990         Array[Index] = RtlAllocateHeap(HeapHandle, Flags, Size);
3991         if (Array[Index] == NULL)
3992         {
3993             /* ERROR_NOT_ENOUGH_MEMORY */
3994             RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
3995 
3996             if (Flags & HEAP_GENERATE_EXCEPTIONS)
3997             {
3998                 ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
3999                 ExceptionRecord.ExceptionRecord = NULL;
4000                 ExceptionRecord.NumberParameters = 0;
4001                 ExceptionRecord.ExceptionFlags = 0;
4002 
4003                 RtlRaiseException(&ExceptionRecord);
4004             }
4005             break;
4006         }
4007     }
4008 
4009     return Index;
4010 }
4011 
4012 /* @implemented */
4013 ULONG
4014 NTAPI
4015 RtlMultipleFreeHeap(IN PVOID HeapHandle,
4016                     IN ULONG Flags,
4017                     IN ULONG Count,
4018                     OUT PVOID *Array)
4019 {
4020     ULONG Index;
4021 
4022     for (Index = 0; Index < Count; ++Index)
4023     {
4024         if (Array[Index] == NULL)
4025             continue;
4026 
4027         _SEH2_TRY
4028         {
4029             if (!RtlFreeHeap(HeapHandle, Flags, Array[Index]))
4030             {
4031                 /* ERROR_INVALID_PARAMETER */
4032                 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
4033                 break;
4034             }
4035         }
4036         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4037         {
4038             /* ERROR_INVALID_PARAMETER */
4039             RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
4040             break;
4041         }
4042         _SEH2_END;
4043     }
4044 
4045     return Index;
4046 }
4047 
4048 /*
4049  * Info:
4050  * - https://securityxploded.com/enumheaps.php
4051  * - https://evilcodecave.wordpress.com/2009/04/14/rtlqueryprocessheapinformation-as-anti-dbg-trick/
4052  */
4053 struct _DEBUG_BUFFER;
4054 
4055 NTSTATUS
4056 NTAPI
4057 RtlQueryProcessHeapInformation(
4058     IN struct _DEBUG_BUFFER *DebugBuffer)
4059 {
4060     UNIMPLEMENTED;
4061     return STATUS_NOT_IMPLEMENTED;
4062 }
4063 
4064 /* EOF */
4065