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