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