xref: /reactos/sdk/lib/rtl/heap.c (revision 7983297b)
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
RtlpFindLeastSetBit(ULONG Bits)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
RtlpIsLastCommittedEntry(PHEAP_ENTRY Entry)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
RtlpInitializeHeap(OUT PHEAP Heap,IN ULONG Flags,IN PHEAP_LOCK Lock OPTIONAL,IN PRTL_HEAP_PARAMETERS Parameters)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
RtlpInsertFreeBlockHelper(PHEAP Heap,PHEAP_FREE_ENTRY FreeEntry,SIZE_T BlockSize,BOOLEAN NoFill)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
RtlpInsertFreeBlock(PHEAP Heap,PHEAP_FREE_ENTRY FreeEntry,SIZE_T BlockSize)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
RtlpRemoveFreeBlock(PHEAP Heap,PHEAP_FREE_ENTRY FreeEntry,BOOLEAN NoFill)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
RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry)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
RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment)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
RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment,PHEAP_UCR_DESCRIPTOR UcrDescriptor)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
RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment,ULONG_PTR Address,SIZE_T Size)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
RtlpFindAndCommitPages(PHEAP Heap,PHEAP_SEGMENT Segment,PSIZE_T Size,PVOID AddressRequested)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
RtlpDeCommitFreeBlock(PHEAP Heap,PHEAP_FREE_ENTRY FreeEntry,SIZE_T Size)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
RtlpInitializeHeapSegment(IN OUT PHEAP Heap,OUT PHEAP_SEGMENT Segment,IN UCHAR SegmentIndex,IN ULONG SegmentFlags,IN SIZE_T SegmentReserve,IN SIZE_T SegmentCommit)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
RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment)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
RtlpCoalesceHeap(PHEAP Heap)1162 RtlpCoalesceHeap(PHEAP Heap)
1163 {
1164     UNIMPLEMENTED;
1165     return NULL;
1166 }
1167 
1168 PHEAP_FREE_ENTRY NTAPI
RtlpCoalesceFreeBlocks(PHEAP Heap,PHEAP_FREE_ENTRY FreeEntry,PSIZE_T FreeSize,BOOLEAN Remove)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
RtlpExtendHeap(PHEAP Heap,SIZE_T Size)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
RtlCreateHeap(ULONG Flags,PVOID Addr,SIZE_T TotalSize,SIZE_T CommitSize,PVOID Lock,PRTL_HEAP_PARAMETERS Parameters)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
RtlDestroyHeap(HANDLE HeapPtr)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
RtlpSplitEntry(PHEAP Heap,ULONG Flags,PHEAP_FREE_ENTRY FreeBlock,SIZE_T AllocationSize,SIZE_T Index,SIZE_T Size)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
RtlpAllocateNonDedicated(PHEAP Heap,ULONG Flags,SIZE_T Size,SIZE_T AllocationSize,SIZE_T Index,BOOLEAN HeapLocked)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
RtlAllocateHeap(IN PVOID HeapPtr,IN ULONG Flags,IN SIZE_T Size)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  */
RtlFreeHeap(HANDLE HeapPtr,ULONG Flags,PVOID Ptr)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
RtlpGrowBlockInPlace(IN PHEAP Heap,IN ULONG Flags,IN PHEAP_ENTRY InUseEntry,IN SIZE_T Size,IN SIZE_T Index)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
RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry)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
RtlReAllocateHeap(HANDLE HeapPtr,ULONG Flags,PVOID Ptr,SIZE_T Size)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
RtlCompactHeap(HANDLE Heap,ULONG Flags)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
RtlLockHeap(IN HANDLE HeapPtr)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
RtlUnlockHeap(HANDLE HeapPtr)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
RtlSizeHeap(HANDLE HeapPtr,ULONG Flags,PVOID Ptr)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
RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry)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
RtlpValidateHeapHeaders(PHEAP Heap,BOOLEAN Recalculate)3279 RtlpValidateHeapHeaders(
3280     PHEAP Heap,
3281     BOOLEAN Recalculate)
3282 {
3283     // We skip header validation for now
3284     return TRUE;
3285 }
3286 
3287 BOOLEAN NTAPI
RtlpValidateHeapEntry(PHEAP Heap,PHEAP_ENTRY HeapEntry)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
RtlpValidateHeapSegment(PHEAP Heap,PHEAP_SEGMENT Segment,UCHAR SegmentOffset,PULONG FreeEntriesCount,PSIZE_T TotalFreeSize,PSIZE_T TagEntries,PSIZE_T PseudoTagEntries)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
RtlpValidateHeap(PHEAP Heap,BOOLEAN ForceValidation)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  */
RtlValidateHeap(HANDLE HeapPtr,ULONG Flags,PVOID Block)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
RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine,PVOID lParam)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
RtlValidateProcessHeaps(VOID)3792 RtlValidateProcessHeaps(VOID)
3793 {
3794     UNIMPLEMENTED;
3795     return TRUE;
3796 }
3797 
3798 
3799 /*
3800  * @unimplemented
3801  */
3802 BOOLEAN NTAPI
RtlZeroHeap(IN PVOID HeapHandle,IN ULONG Flags)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
RtlSetUserValueHeap(IN PVOID HeapHandle,IN ULONG Flags,IN PVOID BaseAddress,IN PVOID UserValue)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
RtlSetUserFlagsHeap(IN PVOID HeapHandle,IN ULONG Flags,IN PVOID BaseAddress,IN ULONG UserFlagsReset,IN ULONG UserFlagsSet)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
RtlGetUserInfoHeap(IN PVOID HeapHandle,IN ULONG Flags,IN PVOID BaseAddress,OUT PVOID * UserValue,OUT PULONG UserFlags)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
RtlUsageHeap(IN HANDLE Heap,IN ULONG Flags,OUT PRTL_HEAP_USAGE Usage)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
RtlQueryTagHeap(IN PVOID HeapHandle,IN ULONG Flags,IN USHORT TagIndex,IN BOOLEAN ResetCounters,OUT PRTL_HEAP_TAG_INFO HeapTagInfo)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
RtlExtendHeap(IN HANDLE Heap,IN ULONG Flags,IN PVOID P,IN SIZE_T Size)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
RtlCreateTagHeap(_In_ HANDLE HeapHandle,_In_ ULONG Flags,_In_opt_ PWSTR TagName,_In_ PWSTR TagSubName)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
RtlWalkHeap(IN HANDLE HeapHandle,IN PVOID HeapEntry)4049 RtlWalkHeap(IN HANDLE HeapHandle,
4050             IN PVOID HeapEntry)
4051 {
4052     UNIMPLEMENTED;
4053     return STATUS_NOT_IMPLEMENTED;
4054 }
4055 
4056 PVOID
4057 NTAPI
RtlProtectHeap(IN PVOID HeapHandle,IN BOOLEAN ReadOnly)4058 RtlProtectHeap(IN PVOID HeapHandle,
4059                IN BOOLEAN ReadOnly)
4060 {
4061     UNIMPLEMENTED;
4062     return NULL;
4063 }
4064 
4065 NTSTATUS
4066 NTAPI
RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,IN HEAP_INFORMATION_CLASS HeapInformationClass,IN PVOID HeapInformation,IN SIZE_T HeapInformationLength)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
RtlQueryHeapInformation(HANDLE HeapHandle,HEAP_INFORMATION_CLASS HeapInformationClass,PVOID HeapInformation,SIZE_T HeapInformationLength,PSIZE_T ReturnLength OPTIONAL)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
RtlMultipleAllocateHeap(IN PVOID HeapHandle,IN ULONG Flags,IN SIZE_T Size,IN ULONG Count,OUT PVOID * Array)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
RtlMultipleFreeHeap(IN PVOID HeapHandle,IN ULONG Flags,IN ULONG Count,OUT PVOID * Array)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
RtlQueryProcessHeapInformation(IN struct _DEBUG_BUFFER * DebugBuffer)4209 RtlQueryProcessHeapInformation(
4210     IN struct _DEBUG_BUFFER *DebugBuffer)
4211 {
4212     UNIMPLEMENTED;
4213     return STATUS_NOT_IMPLEMENTED;
4214 }
4215 
4216 /* EOF */
4217 
4218