1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS system libraries
4 * PURPOSE: RTL Heap backend allocator
5 * PROGRAMMERS: Copyright 2010 Aleksey Bragin
9 http://msdn.microsoft.com/en-us/library/ms810466.aspx
10 http://msdn.microsoft.com/en-us/library/ms810603.aspx
11 http://www.securitylab.ru/analytics/216376.php
12 http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
13 http://www.phreedom.org/research/exploits/asn1-bitstring/
14 http://illmatics.com/Understanding_the_LFH.pdf
15 http://www.alex-ionescu.com/?p=18
18 /* INCLUDES *****************************************************************/
26 HEAP_LOCK RtlpProcessHeapsListLock
;
30 /* How many least significant bits are clear */
31 UCHAR RtlpBitsClearLow
[] =
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
52 RtlpFindLeastSetBit(ULONG Bits
)
57 return RtlpBitsClearLow
[Bits
& 0xFF]; /* Lowest byte */
59 return RtlpBitsClearLow
[(Bits
>> 8) & 0xFF] + 8; /* 2nd byte */
63 if ((Bits
>> 16) & 0xFF)
64 return RtlpBitsClearLow
[(Bits
>> 16) & 0xFF] + 16; /* 3rd byte */
66 return RtlpBitsClearLow
[(Bits
>> 24) & 0xFF] + 24; /* Highest byte */
70 /* Maximum size of a tail-filling pattern used for compare operation */
71 UCHAR FillPattern
[HEAP_ENTRY_SIZE
] =
85 RtlCompareMemoryUlong(PVOID Source
, ULONG Length
, ULONG Value
);
87 /* FUNCTIONS *****************************************************************/
90 RtlpInitializeHeap(PHEAP Heap
,
96 PVOID NextHeapBase
= Heap
+ 1;
97 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
103 *HeaderSize
+= NumUCRs
* sizeof(*UcrDescriptor
);
105 /* Prepare a list of UCRs */
106 InitializeListHead(&Heap
->UCRList
);
107 InitializeListHead(&Heap
->UCRSegments
);
108 UcrDescriptor
= NextHeapBase
;
110 for (i
=0; i
<NumUCRs
; i
++, UcrDescriptor
++)
112 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
115 NextHeapBase
= UcrDescriptor
;
118 /* Round up header size again */
119 *HeaderSize
= ROUND_UP(*HeaderSize
, HEAP_ENTRY_SIZE
);
121 ASSERT(*HeaderSize
<= PAGE_SIZE
);
123 /* Initialize heap's header */
124 Heap
->Entry
.Size
= (*HeaderSize
) >> HEAP_ENTRY_SHIFT
;
125 Heap
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
127 Heap
->Signature
= HEAP_SIGNATURE
;
129 Heap
->ForceFlags
= (Flags
& (HEAP_NO_SERIALIZE
|
130 HEAP_GENERATE_EXCEPTIONS
|
132 HEAP_REALLOC_IN_PLACE_ONLY
|
133 HEAP_VALIDATE_PARAMETERS_ENABLED
|
134 HEAP_VALIDATE_ALL_ENABLED
|
135 HEAP_TAIL_CHECKING_ENABLED
|
136 HEAP_CREATE_ALIGN_16
|
137 HEAP_FREE_CHECKING_ENABLED
));
138 Heap
->HeaderValidateCopy
= NULL
;
139 Heap
->HeaderValidateLength
= ((PCHAR
)NextHeapBase
- (PCHAR
)Heap
);
141 /* Initialize free lists */
142 for (i
=0; i
<HEAP_FREELISTS
; i
++)
144 InitializeListHead(&Heap
->FreeLists
[i
]);
147 /* Initialize "big" allocations list */
148 InitializeListHead(&Heap
->VirtualAllocdBlocks
);
150 /* Initialize lock */
154 Status
= RtlInitializeHeapLock((PHEAP_LOCK
)Lock
);
155 if (!NT_SUCCESS(Status
))
157 DPRINT1("Initializing the lock failed!\n");
158 return /*NULL*/; // FIXME!
162 /* Set the lock variable */
163 Heap
->LockVariable
= Lock
;
167 RtlpSetFreeListsBit(PHEAP Heap
,
168 PHEAP_FREE_ENTRY FreeEntry
)
172 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
174 /* Calculate offset in the free list bitmap */
175 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
176 Bit
= 1 << (FreeEntry
->Size
& 7);
178 /* Assure it's not already set */
179 ASSERT((Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
) == 0);
182 Heap
->u
.FreeListsInUseBytes
[Index
] |= Bit
;
186 RtlpClearFreeListsBit(PHEAP Heap
,
187 PHEAP_FREE_ENTRY FreeEntry
)
191 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
193 /* Calculate offset in the free list bitmap */
194 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
195 Bit
= 1 << (FreeEntry
->Size
& 7);
197 /* Assure it was set and the corresponding free list is empty */
198 ASSERT(Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
);
199 ASSERT(IsListEmpty(&Heap
->FreeLists
[FreeEntry
->Size
]));
202 Heap
->u
.FreeListsInUseBytes
[Index
] ^= Bit
;
206 RtlpInsertFreeBlockHelper(PHEAP Heap
,
207 PHEAP_FREE_ENTRY FreeEntry
,
211 PLIST_ENTRY FreeListHead
, Current
;
212 PHEAP_FREE_ENTRY CurrentEntry
;
214 ASSERT(FreeEntry
->Size
== BlockSize
);
216 /* Fill if it's not denied */
219 FreeEntry
->Flags
&= ~(HEAP_ENTRY_FILL_PATTERN
|
220 HEAP_ENTRY_EXTRA_PRESENT
|
223 if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
225 RtlFillMemoryUlong((PCHAR
)(FreeEntry
+ 1),
226 (BlockSize
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
),
229 FreeEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
234 /* Clear out all flags except the last entry one */
235 FreeEntry
->Flags
&= HEAP_ENTRY_LAST_ENTRY
;
238 /* Insert it either into dedicated or non-dedicated list */
239 if (BlockSize
< HEAP_FREELISTS
)
242 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
244 if (IsListEmpty(FreeListHead
))
246 RtlpSetFreeListsBit(Heap
, FreeEntry
);
251 /* Non-dedicated one */
252 FreeListHead
= &Heap
->FreeLists
[0];
253 Current
= FreeListHead
->Flink
;
255 /* Find a position where to insert it to (the list must be sorted) */
256 while (FreeListHead
!= Current
)
258 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
260 if (BlockSize
<= CurrentEntry
->Size
)
263 Current
= Current
->Flink
;
266 FreeListHead
= Current
;
269 /* Actually insert it into the list */
270 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
274 RtlpInsertFreeBlock(PHEAP Heap
,
275 PHEAP_FREE_ENTRY FreeEntry
,
278 USHORT Size
, PreviousSize
;
279 UCHAR SegmentOffset
, Flags
;
280 PHEAP_SEGMENT Segment
;
282 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
284 /* Increase the free size counter */
285 Heap
->TotalFreeSize
+= BlockSize
;
287 /* Remember certain values */
288 Flags
= FreeEntry
->Flags
;
289 PreviousSize
= FreeEntry
->PreviousSize
;
290 SegmentOffset
= FreeEntry
->SegmentOffset
;
291 Segment
= Heap
->Segments
[SegmentOffset
];
296 /* Check for the max size */
297 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
299 Size
= HEAP_MAX_BLOCK_SIZE
;
301 /* Special compensation if it goes above limit just by 1 */
302 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
305 FreeEntry
->Flags
= 0;
310 FreeEntry
->Flags
= Flags
;
313 /* Change its size and insert it into a free list */
314 FreeEntry
->Size
= Size
;
315 FreeEntry
->PreviousSize
= PreviousSize
;
316 FreeEntry
->SegmentOffset
= SegmentOffset
;
318 /* Call a helper to actually insert the block */
319 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
325 /* Go to the next entry */
326 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
328 /* Check if that's all */
329 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
332 /* Update previous size if needed */
333 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
334 FreeEntry
->PreviousSize
= PreviousSize
;
338 RtlpRemoveFreeBlock(PHEAP Heap
,
339 PHEAP_FREE_ENTRY FreeEntry
,
343 SIZE_T Result
, RealSize
;
345 /* Remove the free block and update the freelists bitmap */
346 if (RemoveEntryList(&FreeEntry
->FreeList
) &&
347 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
349 RtlpClearFreeListsBit(Heap
, FreeEntry
);
352 /* Fill with pattern if necessary */
354 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
356 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
358 /* Deduct extra stuff from block's real size */
359 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
360 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
362 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
365 /* Check if the free filler is intact */
366 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
370 if (Result
!= RealSize
)
372 DPRINT1("Free heap block %p modified at %p after it was freed\n",
374 (PCHAR
)(FreeEntry
+ 1) + Result
);
380 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
382 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
384 /* Get pointer to the containing record */
385 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
387 /* Restore the real size */
388 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
391 PHEAP_UCR_DESCRIPTOR NTAPI
392 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
395 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
396 PHEAP_UCR_SEGMENT UcrSegment
;
397 PHEAP Heap
= Segment
->Heap
;
398 SIZE_T ReserveSize
= 16 * PAGE_SIZE
;
399 SIZE_T CommitSize
= 1 * PAGE_SIZE
;
402 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
404 /* Check if we have unused UCRs */
405 if (IsListEmpty(&Heap
->UCRList
))
407 /* Get a pointer to the first UCR segment */
408 UcrSegment
= CONTAINING_RECORD(Heap
->UCRSegments
.Flink
, HEAP_UCR_SEGMENT
, ListEntry
);
410 /* Check the list of UCR segments */
411 if (IsListEmpty(&Heap
->UCRSegments
) ||
412 UcrSegment
->ReservedSize
== UcrSegment
->CommittedSize
)
414 /* We need to create a new one. Reserve 16 pages for it */
416 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
417 (PVOID
*)&UcrSegment
,
423 if (!NT_SUCCESS(Status
)) return NULL
;
425 /* Commit one page */
426 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
427 (PVOID
*)&UcrSegment
,
433 if (!NT_SUCCESS(Status
))
435 /* Release reserved memory */
436 ZwFreeVirtualMemory(NtCurrentProcess(),
437 (PVOID
*)&UcrDescriptor
,
444 UcrSegment
->ReservedSize
= ReserveSize
;
445 UcrSegment
->CommittedSize
= CommitSize
;
447 /* Add it to the head of the list */
448 InsertHeadList(&Heap
->UCRSegments
, &UcrSegment
->ListEntry
);
450 /* Get a pointer to the first available UCR descriptor */
451 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)(UcrSegment
+ 1);
455 /* It's possible to use existing UCR segment. Commit one more page */
456 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
);
457 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
458 (PVOID
*)&UcrDescriptor
,
464 if (!NT_SUCCESS(Status
)) return NULL
;
467 UcrSegment
->CommittedSize
+= CommitSize
;
470 /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
471 while ((PCHAR
)UcrDescriptor
< ((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
))
473 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
478 /* There are unused UCRs, just get the first one */
479 Entry
= RemoveHeadList(&Heap
->UCRList
);
480 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
481 return UcrDescriptor
;
485 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
486 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
489 UcrDescriptor
->Address
= NULL
;
490 UcrDescriptor
->Size
= 0;
492 /* Put it into the heap's list of unused UCRs */
493 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
497 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
502 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
504 DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment
, Address
, Size
);
506 /* Go through the list of UCR descriptors, they are sorted from lowest address
508 Current
= Segment
->UCRSegmentList
.Flink
;
509 while(Current
!= &Segment
->UCRSegmentList
)
511 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
513 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
515 /* Check for a really lucky case */
516 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
519 UcrDescriptor
->Address
= (PVOID
)Address
;
520 UcrDescriptor
->Size
+= Size
;
524 /* We found the block after which the new one should go */
527 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
529 /* Modify this entry */
530 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
531 Size
+= UcrDescriptor
->Size
;
533 /* Advance to the next descriptor */
534 Current
= Current
->Flink
;
536 /* Remove the current descriptor from the list and destroy it */
537 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
538 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
540 Segment
->NumberOfUnCommittedRanges
--;
544 /* Advance to the next descriptor */
545 Current
= Current
->Flink
;
549 /* Create a new UCR descriptor */
550 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
551 if (!UcrDescriptor
) return;
553 UcrDescriptor
->Address
= (PVOID
)Address
;
554 UcrDescriptor
->Size
= Size
;
556 /* "Current" is the descriptor after which our one should go */
557 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
559 DPRINT("Added segment UCR with base %p, size 0x%x\n", Address
, Size
);
561 /* Increase counters */
562 Segment
->NumberOfUnCommittedRanges
++;
565 PHEAP_FREE_ENTRY NTAPI
566 RtlpFindAndCommitPages(PHEAP Heap
,
567 PHEAP_SEGMENT Segment
,
569 PVOID AddressRequested
)
572 ULONG_PTR Address
= 0;
573 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
574 PHEAP_ENTRY FirstEntry
, LastEntry
;
577 DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap
, Segment
, *Size
, Address
);
579 /* Go through UCRs in a segment */
580 Current
= Segment
->UCRSegmentList
.Flink
;
581 while(Current
!= &Segment
->UCRSegmentList
)
583 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
585 /* Check if we can use that one right away */
586 if (UcrDescriptor
->Size
>= *Size
&&
587 (UcrDescriptor
->Address
== AddressRequested
|| !AddressRequested
))
589 /* Get the address */
590 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
593 if (Heap
->CommitRoutine
)
595 Status
= Heap
->CommitRoutine(Heap
, (PVOID
*)&Address
, Size
);
599 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
607 DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size
, Address
, UcrDescriptor
->Size
);
609 /* Fail in unsuccessful case */
610 if (!NT_SUCCESS(Status
))
612 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
616 /* Update tracking numbers */
617 Segment
->NumberOfUnCommittedPages
-= *Size
/ PAGE_SIZE
;
619 /* Calculate first and last entries */
620 FirstEntry
= (PHEAP_ENTRY
)Address
;
622 /* Go through the entries to find the last one */
624 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
626 LastEntry
= &Segment
->Entry
;
628 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
630 LastEntry
+= LastEntry
->Size
;
632 ASSERT((LastEntry
+ LastEntry
->Size
) == FirstEntry
);
634 /* Unmark it as a last entry */
635 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
637 /* Update UCR descriptor */
638 UcrDescriptor
->Address
= (PVOID
)((ULONG_PTR
)UcrDescriptor
->Address
+ *Size
);
639 UcrDescriptor
->Size
-= *Size
;
641 DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
642 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
644 /* Set various first entry fields*/
645 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
646 FirstEntry
->Size
= *Size
>> HEAP_ENTRY_SHIFT
;
647 FirstEntry
->PreviousSize
= LastEntry
->Size
;
649 /* Check if anything left in this UCR */
650 if (UcrDescriptor
->Size
== 0)
652 /* It's fully exhausted */
654 /* Check if this is the end of the segment */
655 if(UcrDescriptor
->Address
== Segment
->LastValidEntry
)
657 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
661 FirstEntry
->Flags
= 0;
662 /* Update field of next entry */
663 ASSERT((FirstEntry
+ FirstEntry
->Size
)->PreviousSize
== 0);
664 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
667 /* This UCR needs to be removed because it became useless */
668 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
670 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
671 Segment
->NumberOfUnCommittedRanges
--;
675 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
679 return (PHEAP_FREE_ENTRY
)FirstEntry
;
682 /* Advance to the next descriptor */
683 PreviousUcr
= UcrDescriptor
;
684 Current
= Current
->Flink
;
691 RtlpDeCommitFreeBlock(PHEAP Heap
,
692 PHEAP_FREE_ENTRY FreeEntry
,
695 PHEAP_SEGMENT Segment
;
696 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
697 PHEAP_FREE_ENTRY NextFreeEntry
;
698 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
699 ULONG PrecedingSize
, NextSize
, DecommitSize
;
700 ULONG_PTR DecommitBase
;
703 DPRINT("Decommitting %p %p %x\n", Heap
, FreeEntry
, Size
);
705 /* We can't decommit if there is a commit routine! */
706 if (Heap
->CommitRoutine
)
708 /* Just add it back the usual way */
709 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
713 /* Get the segment */
714 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
716 /* Get the preceding entry */
717 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
718 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
720 if (PrecedingSize
== 1)
722 /* Just 1 heap entry, increase the base/size */
723 DecommitBase
+= PAGE_SIZE
;
724 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
726 else if (FreeEntry
->PreviousSize
&&
727 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
729 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
732 /* Get the next entry */
733 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
734 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
735 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
739 /* Just 1 heap entry, increase the size */
740 DecommitSize
-= PAGE_SIZE
;
741 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
743 else if (NextSize
== 0 &&
744 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
746 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
749 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
751 /* Calculate real decommit size */
752 if (DecommitSize
> DecommitBase
)
754 DecommitSize
-= DecommitBase
;
758 /* Nothing to decommit */
759 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
763 /* A decommit is necessary. Create a UCR descriptor */
764 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
767 DPRINT1("HEAP: Failed to create UCR descriptor\n");
768 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
772 /* Decommit the memory */
773 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
774 (PVOID
*)&DecommitBase
,
778 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
779 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
781 if (!NT_SUCCESS(Status
))
783 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
787 /* Insert uncommitted pages */
788 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
789 Segment
->NumberOfUnCommittedPages
+= (DecommitSize
/ PAGE_SIZE
);
793 /* Adjust size of this free entry and insert it */
794 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
795 FreeEntry
->Size
= PrecedingSize
;
796 Heap
->TotalFreeSize
+= PrecedingSize
;
798 /* Insert it into the free list */
799 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
, FALSE
);
801 else if (PrecedingInUseEntry
)
803 /* Adjust preceding in use entry */
804 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
807 /* Now the next one */
810 /* Adjust size of this free entry and insert it */
811 NextFreeEntry
->Flags
= 0;
812 NextFreeEntry
->PreviousSize
= 0;
813 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
814 NextFreeEntry
->Size
= NextSize
;
816 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= NextSize
;
818 Heap
->TotalFreeSize
+= NextSize
;
819 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
, FALSE
);
821 else if (NextInUseEntry
)
823 NextInUseEntry
->PreviousSize
= 0;
828 RtlpInitializeHeapSegment(PHEAP Heap
,
829 PHEAP_SEGMENT Segment
,
833 PVOID UncommittedBase
,
836 ULONG Pages
, CommitSize
;
837 PHEAP_ENTRY HeapEntry
;
838 USHORT PreviousSize
= 0, NewSize
;
841 Pages
= ((PCHAR
)LimitAddress
- (PCHAR
)BaseAddress
) / PAGE_SIZE
;
843 HeapEntry
= (PHEAP_ENTRY
)ROUND_UP(Segment
+ 1, HEAP_ENTRY_SIZE
);
845 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %p %p %p)\n", Heap
, Segment
, SegmentIndex
, Flags
, BaseAddress
, UncommittedBase
, LimitAddress
);
846 DPRINT("Pages %x, HeapEntry %p, sizeof(HEAP_SEGMENT) %x\n", Pages
, HeapEntry
, sizeof(HEAP_SEGMENT
));
848 /* Check if it's the first segment and remember its size */
849 if (Heap
== BaseAddress
)
850 PreviousSize
= Heap
->Entry
.Size
;
852 NewSize
= ((PCHAR
)HeapEntry
- (PCHAR
)Segment
) >> HEAP_ENTRY_SHIFT
;
854 if ((PVOID
)(HeapEntry
+ 1) >= UncommittedBase
)
856 /* Check if it goes beyond the limit */
857 if ((PVOID
)(HeapEntry
+ 1) >= LimitAddress
)
860 /* Need to commit memory */
861 CommitSize
= (PCHAR
)(HeapEntry
+ 1) - (PCHAR
)UncommittedBase
;
862 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
863 (PVOID
)&UncommittedBase
,
868 if (!NT_SUCCESS(Status
))
870 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
874 DPRINT("Committed %d bytes at base %p\n", CommitSize
, UncommittedBase
);
876 /* Calcule the new uncommitted base */
877 UncommittedBase
= (PVOID
)((PCHAR
)UncommittedBase
+ CommitSize
);
880 /* Initialize the segment entry */
881 Segment
->Entry
.PreviousSize
= PreviousSize
;
882 Segment
->Entry
.Size
= NewSize
;
883 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
884 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
886 /* Initialize the segment itself */
887 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
888 Segment
->Heap
= Heap
;
889 Segment
->BaseAddress
= BaseAddress
;
890 Segment
->FirstEntry
= HeapEntry
;
891 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((PCHAR
)BaseAddress
+ Pages
* PAGE_SIZE
);
892 Segment
->NumberOfPages
= Pages
;
893 Segment
->NumberOfUnCommittedPages
= ((PCHAR
)LimitAddress
- (PCHAR
)UncommittedBase
) / PAGE_SIZE
;
894 InitializeListHead(&Segment
->UCRSegmentList
);
896 /* Insert uncommitted pages into UCR (uncommitted ranges) list */
897 if (Segment
->NumberOfUnCommittedPages
)
899 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
)UncommittedBase
, Segment
->NumberOfUnCommittedPages
* PAGE_SIZE
);
902 /* Set the segment index pointer */
903 Heap
->Segments
[SegmentIndex
] = Segment
;
905 /* Prepare a free heap entry */
906 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
907 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
908 HeapEntry
->SegmentOffset
= SegmentIndex
;
911 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, (PHEAP_ENTRY
)UncommittedBase
- HeapEntry
);
917 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
923 /* Make sure it's not user allocated */
924 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
926 BaseAddress
= Segment
->BaseAddress
;
927 DPRINT("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
929 /* Release virtual memory */
930 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
935 if (!NT_SUCCESS(Status
))
937 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
943 RtlpAddHeapToProcessList(PHEAP Heap
)
948 Peb
= RtlGetCurrentPeb();
950 /* Acquire the lock */
951 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
954 /* Check if max number of heaps reached */
955 if (Peb
->NumberOfHeaps
== Peb
->MaximumNumberOfHeaps
)
957 // TODO: Handle this case
961 /* Add the heap to the process heaps */
962 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = Heap
;
963 Peb
->NumberOfHeaps
++;
964 Heap
->ProcessHeapsListIndex
= Peb
->NumberOfHeaps
;
967 /* Release the lock */
968 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
975 RtlpRemoveHeapFromProcessList(PHEAP Heap
)
978 PHEAP
*Current
, *Next
;
982 Peb
= RtlGetCurrentPeb();
984 /* Acquire the lock */
985 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
987 /* Check if we don't need anything to do */
988 if ((Heap
->ProcessHeapsListIndex
== 0) ||
989 (Heap
->ProcessHeapsListIndex
> Peb
->NumberOfHeaps
) ||
990 (Peb
->NumberOfHeaps
== 0))
992 /* Release the lock */
993 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
998 /* The process actually has more than one heap.
999 Use classic, lernt from university times algorithm for removing an entry
1000 from a static array */
1002 Current
= (PHEAP
*)&Peb
->ProcessHeaps
[Heap
->ProcessHeapsListIndex
- 1];
1005 /* How many items we need to shift to the left */
1006 Count
= Peb
->NumberOfHeaps
- (Heap
->ProcessHeapsListIndex
- 1);
1008 /* Move them all in a loop */
1011 /* Copy it and advance next pointer */
1014 /* Update its index */
1015 (*Current
)->ProcessHeapsListIndex
-= 1;
1017 /* Advance pointers */
1022 /* Decrease total number of heaps */
1023 Peb
->NumberOfHeaps
--;
1025 /* Zero last unused item */
1026 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = NULL
;
1027 Heap
->ProcessHeapsListIndex
= 0;
1029 /* Release the lock */
1030 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1033 PHEAP_FREE_ENTRY NTAPI
1034 RtlpCoalesceHeap(PHEAP Heap
)
1040 PHEAP_FREE_ENTRY NTAPI
1041 RtlpCoalesceFreeBlocks (PHEAP Heap
,
1042 PHEAP_FREE_ENTRY FreeEntry
,
1046 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
1048 /* Get the previous entry */
1049 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
1052 if (CurrentEntry
!= FreeEntry
&&
1053 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1054 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
1056 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
1058 /* Remove it if asked for */
1061 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1062 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1064 /* Remove it only once! */
1068 /* Remove previous entry too */
1069 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1072 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1074 /* Advance FreeEntry and update sizes */
1075 FreeEntry
= CurrentEntry
;
1076 *FreeSize
= *FreeSize
+ CurrentEntry
->Size
;
1077 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1078 FreeEntry
->Size
= *FreeSize
;
1080 /* Also update previous size if needed */
1081 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1083 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1087 /* Check the next block if it exists */
1088 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1090 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1092 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1093 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1095 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1097 /* Remove it if asked for */
1100 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1101 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1105 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1107 /* Remove next entry now */
1108 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1111 *FreeSize
= *FreeSize
+ NextEntry
->Size
;
1112 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1113 FreeEntry
->Size
= *FreeSize
;
1115 /* Also update previous size if needed */
1116 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1118 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1125 PHEAP_FREE_ENTRY NTAPI
1126 RtlpExtendHeap(PHEAP Heap
,
1130 UCHAR Index
, EmptyIndex
;
1131 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1132 PHEAP_SEGMENT Segment
;
1133 PHEAP_FREE_ENTRY FreeEntry
;
1136 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1138 /* Calculate amount in pages */
1139 Pages
= (Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
1140 FreeSize
= Pages
* PAGE_SIZE
;
1141 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1143 /* Find an empty segment */
1144 EmptyIndex
= HEAP_SEGMENTS
;
1145 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1147 Segment
= Heap
->Segments
[Index
];
1149 if (Segment
) DPRINT("Segment[%d] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1151 /* Check if its size suits us */
1153 Pages
<= Segment
->NumberOfUnCommittedPages
)
1155 DPRINT("This segment is suitable\n");
1157 /* Commit needed amount */
1158 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1160 /* Coalesce it with adjacent entries */
1163 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1164 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1165 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1169 else if (!Segment
&&
1170 EmptyIndex
== HEAP_SEGMENTS
)
1172 /* Remember the first unused segment index */
1177 /* No luck, need to grow the heap */
1178 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1179 (EmptyIndex
!= HEAP_SEGMENTS
))
1183 /* Reserve the memory */
1184 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1185 ReserveSize
= Heap
->SegmentReserve
;
1187 ReserveSize
= Size
+ PAGE_SIZE
;
1189 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1196 /* If it failed, retry again with a half division algorithm */
1197 while (!NT_SUCCESS(Status
) &&
1198 ReserveSize
!= Size
+ PAGE_SIZE
)
1202 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1203 ReserveSize
= Size
+ PAGE_SIZE
;
1205 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1213 /* Proceed only if it's success */
1214 if (NT_SUCCESS(Status
))
1216 Heap
->SegmentReserve
+= ReserveSize
;
1218 /* Now commit the memory */
1219 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1220 CommitSize
= Heap
->SegmentCommit
;
1222 CommitSize
= Size
+ PAGE_SIZE
;
1224 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1231 DPRINT("Committed %d bytes at base %p\n", CommitSize
, Segment
);
1233 /* Initialize heap segment if commit was successful */
1234 if (NT_SUCCESS(Status
))
1236 if (!RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, Segment
,
1237 (PCHAR
)Segment
+ CommitSize
, (PCHAR
)Segment
+ ReserveSize
))
1239 Status
= STATUS_NO_MEMORY
;
1243 /* If everything worked - cool */
1244 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1246 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1248 /* Nope, we failed. Free memory */
1249 ZwFreeVirtualMemory(NtCurrentProcess(),
1256 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1260 if (RtlpGetMode() == UserMode
)
1262 /* If coalescing on free is disabled in usermode, then do it here */
1263 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1265 FreeEntry
= RtlpCoalesceHeap(Heap
);
1267 /* If it's a suitable one - return it */
1269 FreeEntry
->Size
>= Size
)
1279 /***********************************************************************
1282 * Handle of heap: Success
1288 RtlCreateHeap(ULONG Flags
,
1293 PRTL_HEAP_PARAMETERS Parameters
)
1295 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1297 RTL_HEAP_PARAMETERS SafeParams
= {0};
1299 ULONG_PTR MaximumUserModeAddress
;
1300 SYSTEM_BASIC_INFORMATION SystemInformation
;
1301 MEMORY_BASIC_INFORMATION MemoryInfo
;
1302 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1303 ULONG HeapSegmentFlags
= 0;
1305 ULONG MaxBlockSize
, HeaderSize
;
1306 BOOLEAN AllocateLock
= FALSE
;
1308 /* Check for a special heap */
1309 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1311 Heap
= RtlpPageHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1312 if (Heap
) return Heap
;
1314 /* Reset a special Parameters == -1 hack */
1315 if ((ULONG_PTR
)Parameters
== (ULONG_PTR
)-1)
1318 DPRINT1("Enabling page heap failed\n");
1321 /* Check validation flags */
1322 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1324 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1325 Flags
&= HEAP_CREATE_VALID_MASK
;
1328 /* TODO: Capture parameters, once we decide to use SEH */
1329 if (!Parameters
) Parameters
= &SafeParams
;
1331 /* Check global flags */
1332 if (NtGlobalFlags
& FLG_HEAP_DISABLE_COALESCING
)
1333 Flags
|= HEAP_DISABLE_COALESCE_ON_FREE
;
1335 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1336 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1338 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1339 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1341 if (RtlpGetMode() == UserMode
)
1343 /* Also check these flags if in usermode */
1344 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1345 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1347 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1348 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1350 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1351 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1354 Peb
= RtlGetCurrentPeb();
1356 /* Apply defaults for non-set parameters */
1357 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= Peb
->HeapSegmentCommit
;
1358 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= Peb
->HeapSegmentReserve
;
1359 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= Peb
->HeapDeCommitFreeBlockThreshold
;
1360 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= Peb
->HeapDeCommitTotalFreeThreshold
;
1364 /* Apply defaults for non-set parameters */
1366 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= MmHeapSegmentCommit
;
1367 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= MmHeapSegmentReserve
;
1368 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
1369 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
1373 // FIXME: Move to memory manager
1374 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= PAGE_SIZE
* 2;
1375 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= 1048576;
1376 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= PAGE_SIZE
;
1377 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= 65536;
1379 /* Get the max um address */
1380 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1382 sizeof(SystemInformation
),
1385 if (!NT_SUCCESS(Status
))
1387 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1391 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1393 /* Calculate max alloc size */
1394 if (!Parameters
->MaximumAllocationSize
)
1395 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1397 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1399 if (!Parameters
->VirtualMemoryThreshold
||
1400 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1402 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1405 /* Check reserve/commit sizes and set default values */
1408 CommitSize
= PAGE_SIZE
;
1410 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1412 TotalSize
= 64 * PAGE_SIZE
;
1416 /* Round up the commit size to be at least the page size */
1417 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1420 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1422 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1425 /* Call special heap */
1426 if (RtlpHeapIsSpecial(Flags
))
1427 return RtlDebugCreateHeap(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1429 /* Calculate header size */
1430 HeaderSize
= sizeof(HEAP
);
1431 if (!(Flags
& HEAP_NO_SERIALIZE
))
1435 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
1439 HeaderSize
+= sizeof(HEAP_LOCK
);
1440 AllocateLock
= TRUE
;
1445 /* Invalid parameters */
1449 /* See if we are already provided with an address for the heap */
1452 if (Parameters
->CommitRoutine
)
1454 /* There is a commit routine, so no problem here, check params */
1455 if ((Flags
& HEAP_GROWABLE
) ||
1456 !Parameters
->InitialCommit
||
1457 !Parameters
->InitialReserve
||
1458 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1464 /* Calculate committed and uncommitted addresses */
1465 CommittedAddress
= Addr
;
1466 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1467 TotalSize
= Parameters
->InitialReserve
;
1469 /* Zero the initial page ourselves */
1470 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1474 /* Commit routine is absent, so query how much memory caller reserved */
1475 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1477 MemoryBasicInformation
,
1482 if (!NT_SUCCESS(Status
))
1484 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1489 if (MemoryInfo
.BaseAddress
!= Addr
||
1490 MemoryInfo
.State
== MEM_FREE
)
1495 /* Validation checks passed, set committed/uncommitted addresses */
1496 CommittedAddress
= Addr
;
1498 /* Check if it's committed or not */
1499 if (MemoryInfo
.State
== MEM_COMMIT
)
1501 /* Zero it out because it's already committed */
1502 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1504 /* Calculate uncommitted address value */
1505 CommitSize
= MemoryInfo
.RegionSize
;
1506 TotalSize
= CommitSize
;
1507 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1509 /* Check if uncommitted address is reserved */
1510 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1512 MemoryBasicInformation
,
1517 if (NT_SUCCESS(Status
) &&
1518 MemoryInfo
.State
== MEM_RESERVE
)
1520 /* It is, so add it up to the reserve size */
1521 TotalSize
+= MemoryInfo
.RegionSize
;
1526 /* It's not committed, inform following code that a commit is necessary */
1527 CommitSize
= PAGE_SIZE
;
1528 UncommittedAddress
= Addr
;
1532 /* Mark this as a user-committed mem */
1533 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1538 /* Check commit routine */
1539 if (Parameters
->CommitRoutine
) return NULL
;
1541 /* Reserve memory */
1542 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1549 if (!NT_SUCCESS(Status
))
1551 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1555 /* Set base addresses */
1556 CommittedAddress
= Heap
;
1557 UncommittedAddress
= Heap
;
1560 /* Check if we need to commit something */
1561 if (CommittedAddress
== UncommittedAddress
)
1563 /* Commit the required size */
1564 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1571 DPRINT("Committed %d bytes at base %p\n", CommitSize
, CommittedAddress
);
1573 if (!NT_SUCCESS(Status
))
1575 DPRINT1("Failure, Status 0x%08X\n", Status
);
1577 /* Release memory if it was reserved */
1578 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1586 /* Calculate new uncommitted address */
1587 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1590 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1592 /* Initialize the heap */
1593 RtlpInitializeHeap(Heap
, &HeaderSize
, Flags
, AllocateLock
, Lock
);
1595 /* Initialize heap's first segment */
1596 if (!RtlpInitializeHeapSegment(Heap
,
1597 (PHEAP_SEGMENT
)((PCHAR
)Heap
+ HeaderSize
),
1602 (PCHAR
)CommittedAddress
+ TotalSize
))
1604 DPRINT1("Failed to initialize heap segment\n");
1608 /* Set other data */
1609 Heap
->ProcessHeapsListIndex
= 0;
1610 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
1611 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
1612 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
1613 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
1614 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
1615 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, HEAP_ENTRY_SIZE
) >> HEAP_ENTRY_SHIFT
;
1616 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
1619 if (Flags
& HEAP_CREATE_ALIGN_16
)
1621 Heap
->AlignMask
= (ULONG
)~15;
1622 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
1626 Heap
->AlignMask
= (ULONG
)~(HEAP_ENTRY_SIZE
- 1);
1627 Heap
->AlignRound
= HEAP_ENTRY_SIZE
- 1 + sizeof(HEAP_ENTRY
);
1630 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1631 Heap
->AlignRound
+= HEAP_ENTRY_SIZE
;
1633 /* Add heap to process list in case of usermode heap */
1634 if (RtlpGetMode() == UserMode
)
1636 RtlpAddHeapToProcessList(Heap
);
1638 // FIXME: What about lookasides?
1641 DPRINT("Heap %p, flags 0x%08x\n", Heap
, Heap
->Flags
);
1645 /***********************************************************************
1654 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1655 * Failure: The Heap handle, if heap is the process heap.
1658 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1660 PHEAP Heap
= (PHEAP
)HeapPtr
;
1661 PLIST_ENTRY Current
;
1662 PHEAP_UCR_SEGMENT UcrSegment
;
1663 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1667 PHEAP_SEGMENT Segment
;
1669 if (!HeapPtr
) return NULL
;
1671 /* Call page heap routine if required */
1672 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
) return RtlpPageHeapDestroy(HeapPtr
);
1674 /* Call special heap */
1675 if (RtlpHeapIsSpecial(Heap
->Flags
))
1677 if (!RtlDebugDestroyHeap(Heap
)) return HeapPtr
;
1680 /* Check for a process heap */
1681 if (RtlpGetMode() == UserMode
&&
1682 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1684 /* Free up all big allocations */
1685 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1686 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1688 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1689 BaseAddress
= (PVOID
)VirtualEntry
;
1690 Current
= Current
->Flink
;
1692 ZwFreeVirtualMemory(NtCurrentProcess(),
1698 /* Delete tags and remove heap from the process heaps list in user mode */
1699 if (RtlpGetMode() == UserMode
)
1701 // FIXME DestroyTags
1702 RtlpRemoveHeapFromProcessList(Heap
);
1705 /* Delete the heap lock */
1706 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1708 /* Delete it if it wasn't user allocated */
1709 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1710 RtlDeleteHeapLock(Heap
->LockVariable
);
1712 /* Clear out the lock variable */
1713 Heap
->LockVariable
= NULL
;
1716 /* Free UCR segments if any were created */
1717 Current
= Heap
->UCRSegments
.Flink
;
1718 while(Current
!= &Heap
->UCRSegments
)
1720 UcrSegment
= CONTAINING_RECORD(Current
, HEAP_UCR_SEGMENT
, ListEntry
);
1722 /* Advance to the next descriptor */
1723 Current
= Current
->Flink
;
1725 BaseAddress
= (PVOID
)UcrSegment
;
1728 /* Release that memory */
1729 ZwFreeVirtualMemory(NtCurrentProcess(),
1735 /* Go through segments and destroy them */
1736 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1738 Segment
= Heap
->Segments
[i
];
1739 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1746 RtlpSplitEntry(PHEAP Heap
,
1747 PHEAP_FREE_ENTRY FreeBlock
,
1748 SIZE_T AllocationSize
,
1752 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1754 PHEAP_ENTRY InUseEntry
;
1757 /* Save flags, update total free size */
1758 FreeFlags
= FreeBlock
->Flags
;
1759 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1761 /* Make this block an in-use one */
1762 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1763 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
;
1764 InUseEntry
->SmallTagIndex
= 0;
1766 /* Calculate the extra amount */
1767 FreeSize
= InUseEntry
->Size
- Index
;
1769 /* Update it's size fields (we don't need their data anymore) */
1770 InUseEntry
->Size
= Index
;
1771 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
1773 /* If there is something to split - do the split */
1776 /* Don't split if resulting entry can't contain any payload data
1777 (i.e. being just HEAP_ENTRY_SIZE) */
1780 /* Increase sizes of the in-use entry */
1782 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
1786 /* Calculate a pointer to the new entry */
1787 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
1790 SplitBlock
->Flags
= FreeFlags
;
1791 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
1792 SplitBlock
->Size
= FreeSize
;
1793 SplitBlock
->PreviousSize
= Index
;
1795 /* Check if it's the last entry */
1796 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1798 /* Insert it to the free list if it's the last entry */
1799 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1800 Heap
->TotalFreeSize
+= FreeSize
;
1804 /* Not so easy - need to update next's previous size too */
1805 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
1807 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
1809 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
1810 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1811 Heap
->TotalFreeSize
+= FreeSize
;
1815 /* Even more complex - the next entry is free, so we can merge them into one! */
1816 SplitBlock
->Flags
= SplitBlock2
->Flags
;
1818 /* Remove that next entry */
1819 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
1822 FreeSize
+= SplitBlock2
->Size
;
1823 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
1825 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1827 /* Insert it back */
1828 SplitBlock
->Size
= FreeSize
;
1830 /* Don't forget to update previous size of the next entry! */
1831 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1833 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
1836 /* Actually insert it */
1837 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, (USHORT
)FreeSize
, FALSE
);
1839 /* Update total size */
1840 Heap
->TotalFreeSize
+= FreeSize
;
1844 /* Resulting block is quite big */
1845 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
1850 /* Reset flags of the free entry */
1855 /* Set last entry flag */
1856 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1857 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1863 RtlpAllocateNonDedicated(PHEAP Heap
,
1866 SIZE_T AllocationSize
,
1870 PLIST_ENTRY FreeListHead
, Next
;
1871 PHEAP_FREE_ENTRY FreeBlock
;
1872 PHEAP_ENTRY InUseEntry
;
1873 PHEAP_ENTRY_EXTRA Extra
;
1874 EXCEPTION_RECORD ExceptionRecord
;
1876 /* Go through the zero list to find a place where to insert the new entry */
1877 FreeListHead
= &Heap
->FreeLists
[0];
1879 /* Start from the largest block to reduce time */
1880 Next
= FreeListHead
->Blink
;
1881 if (FreeListHead
!= Next
)
1883 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1885 if (FreeBlock
->Size
>= Index
)
1887 /* Our request is smaller than the largest entry in the zero list */
1889 /* Go through the list to find insertion place */
1890 Next
= FreeListHead
->Flink
;
1891 while (FreeListHead
!= Next
)
1893 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1895 if (FreeBlock
->Size
>= Index
)
1897 /* Found minimally fitting entry. Proceed to either using it as it is
1898 or splitting it to two entries */
1899 RemoveEntryList(&FreeBlock
->FreeList
);
1902 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
1904 /* Release the lock */
1905 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1907 /* Zero memory if that was requested */
1908 if (Flags
& HEAP_ZERO_MEMORY
)
1909 RtlZeroMemory(InUseEntry
+ 1, Size
);
1910 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1912 /* Fill this block with a special pattern */
1913 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1916 /* Fill tail of the block with a special pattern too if requested */
1917 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1919 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1920 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1923 /* Prepare extra if it's present */
1924 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1926 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1927 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1932 /* Return pointer to the */
1933 return InUseEntry
+ 1;
1936 /* Advance to the next entry */
1942 /* Extend the heap, 0 list didn't have anything suitable */
1943 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
1945 /* Use the new biggest entry we've got */
1948 RemoveEntryList(&FreeBlock
->FreeList
);
1951 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
1953 /* Release the lock */
1954 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1956 /* Zero memory if that was requested */
1957 if (Flags
& HEAP_ZERO_MEMORY
)
1958 RtlZeroMemory(InUseEntry
+ 1, Size
);
1959 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1961 /* Fill this block with a special pattern */
1962 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1965 /* Fill tail of the block with a special pattern too if requested */
1966 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1968 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1969 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1972 /* Prepare extra if it's present */
1973 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1975 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1976 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1981 /* Return pointer to the */
1982 return InUseEntry
+ 1;
1985 /* Really unfortunate, out of memory condition */
1986 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
1988 /* Generate an exception */
1989 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
1991 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
1992 ExceptionRecord
.ExceptionRecord
= NULL
;
1993 ExceptionRecord
.NumberParameters
= 1;
1994 ExceptionRecord
.ExceptionFlags
= 0;
1995 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
1997 RtlRaiseException(&ExceptionRecord
);
2000 /* Release the lock */
2001 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2002 DPRINT1("HEAP: Allocation failed!\n");
2003 DPRINT1("Flags %x\n", Heap
->Flags
);
2007 /***********************************************************************
2008 * HeapAlloc (KERNEL32.334)
2010 * Pointer to allocated memory block
2012 * 0x7d030f60--invalid flags in RtlHeapAllocate
2016 RtlAllocateHeap(IN PVOID HeapPtr
,
2020 PHEAP Heap
= (PHEAP
)HeapPtr
;
2021 PULONG FreeListsInUse
;
2022 ULONG FreeListsInUseUlong
;
2023 SIZE_T AllocationSize
;
2025 PLIST_ENTRY FreeListHead
;
2026 PHEAP_ENTRY InUseEntry
;
2027 PHEAP_FREE_ENTRY FreeBlock
;
2028 ULONG InUseIndex
, i
;
2030 EXCEPTION_RECORD ExceptionRecord
;
2031 BOOLEAN HeapLocked
= FALSE
;
2032 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
2033 PHEAP_ENTRY_EXTRA Extra
;
2037 Flags
|= Heap
->ForceFlags
;
2039 /* Call special heap */
2040 if (RtlpHeapIsSpecial(Flags
))
2041 return RtlDebugAllocateHeap(Heap
, Flags
, Size
);
2043 /* Check for the maximum size */
2044 if (Size
>= 0x80000000)
2046 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2047 DPRINT1("HEAP: Allocation failed!\n");
2051 if (Flags
& (HEAP_CREATE_ENABLE_TRACING
|
2052 HEAP_CREATE_ALIGN_16
))
2054 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
2057 //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
2059 /* Calculate allocation size and index */
2061 AllocationSize
= Size
;
2064 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2065 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2067 /* Acquire the lock if necessary */
2068 if (!(Flags
& HEAP_NO_SERIALIZE
))
2070 RtlEnterHeapLock(Heap
->LockVariable
);
2074 /* Depending on the size, the allocation is going to be done from dedicated,
2075 non-dedicated lists or a virtual block of memory */
2076 if (Index
< HEAP_FREELISTS
)
2078 FreeListHead
= &Heap
->FreeLists
[Index
];
2080 if (!IsListEmpty(FreeListHead
))
2082 /* There is a free entry in this list */
2083 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2087 /* Save flags and remove the free entry */
2088 FreeFlags
= FreeBlock
->Flags
;
2089 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2091 /* Update the total free size of the heap */
2092 Heap
->TotalFreeSize
-= Index
;
2094 /* Initialize this block */
2095 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2096 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2097 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2098 InUseEntry
->SmallTagIndex
= 0;
2102 /* Find smallest free block which this request could fit in */
2103 InUseIndex
= Index
>> 5;
2104 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2106 /* This bit magic disables all sizes which are less than the requested allocation size */
2107 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2109 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2111 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2113 /* Go through the list */
2114 for (i
= InUseIndex
; i
< 4; i
++)
2116 if (FreeListsInUseUlong
)
2118 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2122 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2125 /* Nothing found, search in the non-dedicated list */
2127 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2129 /* That list is found, now calculate exact block */
2130 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2132 /* Take this entry and remove it from the list of free blocks */
2133 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2136 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2139 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2142 /* Release the lock */
2143 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2145 /* Zero memory if that was requested */
2146 if (Flags
& HEAP_ZERO_MEMORY
)
2147 RtlZeroMemory(InUseEntry
+ 1, Size
);
2148 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2150 /* Fill this block with a special pattern */
2151 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2154 /* Fill tail of the block with a special pattern too if requested */
2155 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2157 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2158 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2161 /* Prepare extra if it's present */
2162 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2164 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2165 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2170 /* User data starts right after the entry's header */
2171 return InUseEntry
+ 1;
2173 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2175 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2176 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2178 else if (Heap
->Flags
& HEAP_GROWABLE
)
2180 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2181 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2183 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2184 (PVOID
*)&VirtualBlock
,
2190 if (!NT_SUCCESS(Status
))
2193 /* Release the lock */
2194 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2195 DPRINT1("HEAP: Allocation failed!\n");
2199 /* Initialize the newly allocated block */
2200 VirtualBlock
->BusyBlock
.Size
= (AllocationSize
- Size
);
2201 VirtualBlock
->BusyBlock
.Flags
= HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
| HEAP_ENTRY_BUSY
;
2202 VirtualBlock
->CommitSize
= AllocationSize
;
2203 VirtualBlock
->ReserveSize
= AllocationSize
;
2205 /* Insert it into the list of virtual allocations */
2206 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2208 /* Release the lock */
2209 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2211 /* Return pointer to user data */
2212 return VirtualBlock
+ 1;
2215 /* Generate an exception */
2216 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2218 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2219 ExceptionRecord
.ExceptionRecord
= NULL
;
2220 ExceptionRecord
.NumberParameters
= 1;
2221 ExceptionRecord
.ExceptionFlags
= 0;
2222 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2224 RtlRaiseException(&ExceptionRecord
);
2227 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL
);
2229 /* Release the lock */
2230 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2231 DPRINT1("HEAP: Allocation failed!\n");
2236 /***********************************************************************
2237 * HeapFree (KERNEL32.338)
2244 BOOLEAN NTAPI
RtlFreeHeap(
2245 HANDLE HeapPtr
, /* [in] Handle of heap */
2246 ULONG Flags
, /* [in] Heap freeing flags */
2247 PVOID Ptr
/* [in] Address of memory to free */
2251 PHEAP_ENTRY HeapEntry
;
2252 USHORT TagIndex
= 0;
2254 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2255 BOOLEAN Locked
= FALSE
;
2258 /* Freeing NULL pointer is a legal operation */
2259 if (!Ptr
) return TRUE
;
2261 /* Get pointer to the heap and force flags */
2262 Heap
= (PHEAP
)HeapPtr
;
2263 Flags
|= Heap
->ForceFlags
;
2265 /* Call special heap */
2266 if (RtlpHeapIsSpecial(Flags
))
2267 return RtlDebugFreeHeap(Heap
, Flags
, Ptr
);
2269 /* Lock if necessary */
2270 if (!(Flags
& HEAP_NO_SERIALIZE
))
2272 RtlEnterHeapLock(Heap
->LockVariable
);
2276 /* Get pointer to the heap entry */
2277 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2279 /* Check this entry, fail if it's invalid */
2280 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2281 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2282 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2284 /* This is an invalid block */
2285 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2286 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2288 /* Release the heap lock */
2289 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2293 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2295 /* Big allocation */
2296 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2298 /* Remove it from the list */
2299 RemoveEntryList(&VirtualEntry
->Entry
);
2304 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2305 (PVOID
*)&VirtualEntry
,
2309 if (!NT_SUCCESS(Status
))
2311 DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
2312 Status
, Heap
, Ptr
, VirtualEntry
);
2313 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status
);
2318 /* Normal allocation */
2319 BlockSize
= HeapEntry
->Size
;
2323 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2324 if (RtlpGetMode() == KernelMode
||
2325 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2327 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2328 (PHEAP_FREE_ENTRY
)HeapEntry
,
2333 /* If there is no need to decommit the block - put it into a free list */
2334 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2335 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2337 /* Check if it needs to go to a 0 list */
2338 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2340 /* General-purpose 0 list */
2341 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2345 /* Usual free list */
2346 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2348 /* Assert sizes are consistent */
2349 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2351 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2354 /* Increase the free size */
2355 Heap
->TotalFreeSize
+= BlockSize
;
2359 if (RtlpGetMode() == UserMode
&&
2368 /* Decommit this block */
2369 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2373 /* Release the heap lock */
2374 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2380 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2382 IN PHEAP_ENTRY InUseEntry
,
2386 UCHAR EntryFlags
, RememberFlags
;
2387 PHEAP_FREE_ENTRY FreeEntry
, UnusedEntry
, FollowingEntry
;
2388 SIZE_T FreeSize
, PrevSize
, TailPart
, AddedSize
= 0;
2389 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2391 /* We can't grow beyond specified threshold */
2392 if (Index
> Heap
->VirtualMemoryThreshold
)
2395 /* Get entry flags */
2396 EntryFlags
= InUseEntry
->Flags
;
2398 /* Get the next free entry */
2399 FreeEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ InUseEntry
->Size
);
2401 if (EntryFlags
& HEAP_ENTRY_LAST_ENTRY
)
2403 /* There is no next block, just uncommitted space. Calculate how much is needed */
2404 FreeSize
= (Index
- InUseEntry
->Size
) << HEAP_ENTRY_SHIFT
;
2405 FreeSize
= ROUND_UP(FreeSize
, PAGE_SIZE
);
2407 /* Find and commit those pages */
2408 FreeEntry
= RtlpFindAndCommitPages(Heap
,
2409 Heap
->Segments
[InUseEntry
->SegmentOffset
],
2413 /* Fail if it failed... */
2414 if (!FreeEntry
) return FALSE
;
2416 /* It was successful, perform coalescing */
2417 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
2418 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
2420 /* Check if it's enough */
2421 if (FreeSize
+ InUseEntry
->Size
< Index
)
2423 /* Still not enough */
2424 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
2425 Heap
->TotalFreeSize
+= FreeSize
;
2429 /* Remember flags of this free entry */
2430 RememberFlags
= FreeEntry
->Flags
;
2433 FreeSize
+= InUseEntry
->Size
;
2437 /* The next block indeed exists. Check if it's free or in use */
2438 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
) return FALSE
;
2440 /* Next entry is free, check if it can fit the block we need */
2441 FreeSize
= InUseEntry
->Size
+ FreeEntry
->Size
;
2442 if (FreeSize
< Index
) return FALSE
;
2444 /* Remember flags of this free entry */
2445 RememberFlags
= FreeEntry
->Flags
;
2447 /* Remove this block from the free list */
2448 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
2449 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
2452 PrevSize
= (InUseEntry
->Size
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2455 /* Don't produce too small blocks */
2462 /* Process extra stuff */
2463 if (RememberFlags
& HEAP_ENTRY_EXTRA_PRESENT
)
2465 /* Calculate pointers */
2466 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2467 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2470 *NewExtra
= *OldExtra
;
2476 InUseEntry
->Size
= Index
;
2477 InUseEntry
->UnusedBytes
= ((Index
<< HEAP_ENTRY_SHIFT
) - Size
);
2479 /* Check if there is a free space remaining after merging those blocks */
2482 /* Update flags and sizes */
2483 InUseEntry
->Flags
|= RememberFlags
& HEAP_ENTRY_LAST_ENTRY
;
2485 /* Either update previous size of the next entry or mark it as a last
2486 entry in the segment*/
2487 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2488 (InUseEntry
+ InUseEntry
->Size
)->PreviousSize
= InUseEntry
->Size
;
2492 /* Complex case, we need to split the block to give unused free space
2494 UnusedEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2495 UnusedEntry
->PreviousSize
= Index
;
2496 UnusedEntry
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2498 /* Update the following block or set the last entry in the segment */
2499 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2501 /* Set flags and size */
2502 UnusedEntry
->Flags
= RememberFlags
;
2503 UnusedEntry
->Size
= FreeSize
;
2505 /* Insert it to the heap and update total size */
2506 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2507 Heap
->TotalFreeSize
+= FreeSize
;
2511 /* There is a block after this one */
2512 FollowingEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
);
2514 if (FollowingEntry
->Flags
& HEAP_ENTRY_BUSY
)
2516 /* Update flags and set size of the unused space entry */
2517 UnusedEntry
->Flags
= RememberFlags
& (~HEAP_ENTRY_LAST_ENTRY
);
2518 UnusedEntry
->Size
= FreeSize
;
2520 /* Update previous size of the following entry */
2521 FollowingEntry
->PreviousSize
= FreeSize
;
2523 /* Insert it to the heap and update total free size */
2524 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2525 Heap
->TotalFreeSize
+= FreeSize
;
2529 /* That following entry is also free, what a fortune! */
2530 RememberFlags
= FollowingEntry
->Flags
;
2533 RtlpRemoveFreeBlock(Heap
, FollowingEntry
, FALSE
, FALSE
);
2534 Heap
->TotalFreeSize
-= FollowingEntry
->Size
;
2536 /* And make up a new combined block */
2537 FreeSize
+= FollowingEntry
->Size
;
2538 UnusedEntry
->Flags
= RememberFlags
;
2540 /* Check where to put it */
2541 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2543 /* Fine for a dedicated list */
2544 UnusedEntry
->Size
= FreeSize
;
2546 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2547 ((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
)->PreviousSize
= FreeSize
;
2549 /* Insert it back and update total size */
2550 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2551 Heap
->TotalFreeSize
+= FreeSize
;
2555 /* The block is very large, leave all the hassle to the insertion routine */
2556 RtlpInsertFreeBlock(Heap
, UnusedEntry
, FreeSize
);
2562 /* Copy user settable flags */
2563 InUseEntry
->Flags
&= ~HEAP_ENTRY_SETTABLE_FLAGS
;
2564 InUseEntry
->Flags
|= ((Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4);
2566 /* Properly "zero out" (and fill!) the space */
2567 if (Flags
& HEAP_ZERO_MEMORY
)
2569 RtlZeroMemory((PCHAR
)(InUseEntry
+ 1) + PrevSize
, Size
- PrevSize
);
2571 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2573 /* Calculate tail part which we need to fill */
2574 TailPart
= PrevSize
& (sizeof(ULONG
) - 1);
2576 /* "Invert" it as usual */
2577 if (TailPart
) TailPart
= 4 - TailPart
;
2579 if (Size
> (PrevSize
+ TailPart
))
2580 AddedSize
= (Size
- (PrevSize
+ TailPart
)) & ~(sizeof(ULONG
) - 1);
2584 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + PrevSize
+ TailPart
,
2586 ARENA_INUSE_FILLER
);
2590 /* Fill the new tail */
2591 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2593 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2598 /* Return success */
2602 PHEAP_ENTRY_EXTRA NTAPI
2603 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2605 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2607 /* Check if it's a big block */
2608 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2610 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2612 /* Return a pointer to the extra stuff*/
2613 return &VirtualEntry
->ExtraStuff
;
2617 /* This is a usual entry, which means extra stuff follows this block */
2618 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2623 /***********************************************************************
2626 * Heap [in] Handle of heap block
2627 * Flags [in] Heap reallocation flags
2628 * Ptr, [in] Address of memory to reallocate
2629 * Size [in] Number of bytes to reallocate
2632 * Pointer to reallocated memory block
2634 * 0x7d030f60--invalid flags in RtlHeapAllocate
2638 RtlReAllocateHeap(HANDLE HeapPtr
,
2643 PHEAP Heap
= (PHEAP
)HeapPtr
;
2644 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2645 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2646 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2647 BOOLEAN HeapLocked
= FALSE
;
2648 PVOID NewBaseAddress
;
2649 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2650 SIZE_T OldSize
, Index
, OldIndex
;
2654 SIZE_T RemainderBytes
, ExtraSize
;
2655 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2656 EXCEPTION_RECORD ExceptionRecord
;
2658 /* Return success in case of a null pointer */
2661 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS
);
2665 /* Force heap flags */
2666 Flags
|= Heap
->ForceFlags
;
2668 /* Call special heap */
2669 if (RtlpHeapIsSpecial(Flags
))
2670 return RtlDebugReAllocateHeap(Heap
, Flags
, Ptr
, Size
);
2672 /* Make sure size is valid */
2673 if (Size
>= 0x80000000)
2675 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2679 /* Calculate allocation size and index */
2681 AllocationSize
= Size
;
2684 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2686 /* Add up extra stuff, if it is present anywhere */
2687 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2688 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2689 Heap
->PseudoTagEntries
)
2691 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2694 /* Acquire the lock if necessary */
2695 if (!(Flags
& HEAP_NO_SERIALIZE
))
2697 RtlEnterHeapLock(Heap
->LockVariable
);
2699 Flags
&= ~HEAP_NO_SERIALIZE
;
2702 /* Get the pointer to the in-use entry */
2703 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2705 /* If that entry is not really in-use, we have a problem */
2706 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2708 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2710 /* Release the lock and return */
2712 RtlLeaveHeapLock(Heap
->LockVariable
);
2716 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2718 /* This is a virtually allocated block. Get its size */
2719 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2721 /* Convert it to an index */
2722 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2724 /* Calculate new allocation size and round it to the page size */
2725 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2726 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2731 OldIndex
= InUseEntry
->Size
;
2733 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2736 /* Calculate new index */
2737 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2739 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2740 if (Index
<= OldIndex
)
2742 /* Difference must be greater than 1, adjust if it's not so */
2743 if (Index
+ 1 == OldIndex
)
2746 AllocationSize
+= sizeof(HEAP_ENTRY
);
2749 /* Calculate new size */
2750 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2752 /* Simple in case of a virtual alloc - just an unused size */
2753 InUseEntry
->Size
= AllocationSize
- Size
;
2755 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2757 /* There is extra stuff, take it into account */
2758 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2759 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2760 *NewExtra
= *OldExtra
;
2762 // FIXME Tagging, TagIndex
2764 /* Update unused bytes count */
2765 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2769 // FIXME Tagging, SmallTagIndex
2770 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2773 /* If new size is bigger than the old size */
2776 /* Zero out that additional space if required */
2777 if (Flags
& HEAP_ZERO_MEMORY
)
2779 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2781 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2783 /* Fill it on free if required */
2784 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2787 RemainderBytes
= 4 - RemainderBytes
;
2789 if (Size
> (OldSize
+ RemainderBytes
))
2791 /* Calculate actual amount of extra bytes to fill */
2792 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2794 /* Fill them if there are any */
2797 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2799 ARENA_INUSE_FILLER
);
2805 /* Fill tail of the heap entry if required */
2806 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2808 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2813 /* Check if the difference is significant or not */
2814 if (Index
!= OldIndex
)
2817 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2819 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2821 /* This is a virtual block allocation */
2822 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2826 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2827 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2829 /* Release the memory */
2830 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2831 (PVOID
*)&DecommitBase
,
2835 if (!NT_SUCCESS(Status
))
2837 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2841 /* Otherwise reduce the commit size */
2842 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2847 /* Reduce size of the block and possibly split it */
2848 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2850 /* Initialize this entry */
2851 SplitBlock
->Flags
= FreeFlags
;
2852 SplitBlock
->PreviousSize
= Index
;
2853 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2855 /* Remember free size */
2856 FreeSize
= InUseEntry
->Size
- Index
;
2859 InUseEntry
->Size
= Index
;
2860 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2862 /* Is that the last entry */
2863 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2865 /* Set its size and insert it to the list */
2866 SplitBlock
->Size
= (USHORT
)FreeSize
;
2867 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2869 /* Update total free size */
2870 Heap
->TotalFreeSize
+= FreeSize
;
2874 /* Get the block after that one */
2875 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2877 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2879 /* It's in use, add it here*/
2880 SplitBlock
->Size
= (USHORT
)FreeSize
;
2882 /* Update previous size of the next entry */
2883 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2885 /* Insert it to the list */
2886 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2888 /* Update total size */
2889 Heap
->TotalFreeSize
+= FreeSize
;
2893 /* Next entry is free, so merge with it */
2894 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2896 /* Remove it, update total size */
2897 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2898 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2900 /* Calculate total free size */
2901 FreeSize
+= SplitBlock2
->Size
;
2903 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2905 SplitBlock
->Size
= FreeSize
;
2907 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2909 /* Update previous size of the next entry */
2910 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2913 /* Insert the new one back and update total size */
2914 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2915 Heap
->TotalFreeSize
+= FreeSize
;
2920 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2929 /* We're growing the block */
2930 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
2931 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
2933 /* Growing in place failed, so growing out of place */
2934 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
2936 DPRINT1("Realloc in place failed, but it was the only option\n");
2941 /* Clear tag bits */
2942 Flags
&= ~HEAP_TAG_MASK
;
2944 /* Process extra stuff */
2945 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2947 /* Preserve user settable flags */
2948 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
2950 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
2952 /* Get pointer to the old extra data */
2953 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2955 /* Save tag index if it was set */
2956 if (OldExtra
->TagIndex
&&
2957 !(OldExtra
->TagIndex
& HEAP_PSEUDO_TAG_FLAG
))
2959 Flags
|= OldExtra
->TagIndex
<< HEAP_TAG_SHIFT
;
2962 else if (InUseEntry
->SmallTagIndex
)
2964 /* Take small tag index into account */
2965 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
2968 /* Allocate new block from the heap */
2969 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
2970 Flags
& ~HEAP_ZERO_MEMORY
,
2973 /* Proceed if it didn't fail */
2976 /* Get new entry pointer */
2977 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
2979 /* Process extra stuff if it exists */
2980 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2982 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
2984 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2986 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2987 NewExtra
->Settable
= OldExtra
->Settable
;
2991 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
2995 /* Copy actual user bits */
2997 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
2999 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
3001 /* Zero remaining part if required */
3002 if (Size
> OldSize
&&
3003 (Flags
& HEAP_ZERO_MEMORY
))
3005 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
3008 /* Free the old block */
3009 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
3012 Ptr
= NewBaseAddress
;
3017 /* Did resizing fail? */
3018 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
3020 /* Generate an exception if required */
3021 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
3022 ExceptionRecord
.ExceptionRecord
= NULL
;
3023 ExceptionRecord
.NumberParameters
= 1;
3024 ExceptionRecord
.ExceptionFlags
= 0;
3025 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
3027 RtlRaiseException(&ExceptionRecord
);
3030 /* Release the heap lock if it was acquired */
3032 RtlLeaveHeapLock(Heap
->LockVariable
);
3038 /***********************************************************************
3044 RtlCompactHeap(HANDLE Heap
,
3052 /***********************************************************************
3054 * Attempts to acquire the critical section object for a specified heap.
3057 * Heap [in] Handle of heap to lock for exclusive access
3066 RtlLockHeap(IN HANDLE HeapPtr
)
3068 PHEAP Heap
= (PHEAP
)HeapPtr
;
3070 // FIXME Check for special heap
3072 /* Check if it's really a heap */
3073 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3075 /* Lock if it's lockable */
3076 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3078 RtlEnterHeapLock(Heap
->LockVariable
);
3085 /***********************************************************************
3087 * Releases ownership of the critical section object.
3090 * Heap [in] Handle to the heap to unlock
3099 RtlUnlockHeap(HANDLE HeapPtr
)
3101 PHEAP Heap
= (PHEAP
)HeapPtr
;
3103 // FIXME Check for special heap
3105 /* Check if it's really a heap */
3106 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3108 /* Unlock if it's lockable */
3109 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3111 RtlLeaveHeapLock(Heap
->LockVariable
);
3118 /***********************************************************************
3121 * Heap [in] Handle of heap
3122 * Flags [in] Heap size control flags
3123 * Ptr [in] Address of memory to return size for
3126 * Size in bytes of allocated memory
3127 * 0xffffffff: Failure
3138 PHEAP Heap
= (PHEAP
)HeapPtr
;
3139 PHEAP_ENTRY HeapEntry
;
3142 // FIXME This is a hack around missing SEH support!
3145 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3150 Flags
|= Heap
->ForceFlags
;
3152 /* Call special heap */
3153 if (RtlpHeapIsSpecial(Flags
))
3154 return RtlDebugSizeHeap(Heap
, Flags
, Ptr
);
3156 /* Get the heap entry pointer */
3157 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3159 /* Return -1 if that entry is free */
3160 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3162 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3166 /* Get size of this block depending if it's a usual or a big one */
3167 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3169 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3174 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3177 /* Return calculated size */
3182 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry
)
3184 SIZE_T Size
, Result
;
3187 /* Calculate size */
3188 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3189 Size
= RtlpGetSizeOfBigBlock(HeapEntry
);
3191 Size
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3193 /* Calculate pointer to the tail part of the block */
3194 TailPart
= (PCHAR
)(HeapEntry
+ 1) + Size
;
3196 /* Compare tail pattern */
3197 Result
= RtlCompareMemory(TailPart
,
3201 if (Result
!= HEAP_ENTRY_SIZE
)
3203 DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size
, HeapEntry
, TailPart
+ Result
);
3212 RtlpValidateHeapHeaders(
3214 BOOLEAN Recalculate
)
3216 // We skip header validation for now
3221 RtlpValidateHeapEntry(
3223 PHEAP_ENTRY HeapEntry
)
3225 BOOLEAN BigAllocation
, EntryFound
= FALSE
;
3226 PHEAP_SEGMENT Segment
;
3227 ULONG SegmentOffset
;
3229 /* Perform various consistency checks of this entry */
3230 if (!HeapEntry
) goto invalid_entry
;
3231 if ((ULONG_PTR
)HeapEntry
& (HEAP_ENTRY_SIZE
- 1)) goto invalid_entry
;
3232 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
)) goto invalid_entry
;
3234 BigAllocation
= HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
;
3235 Segment
= Heap
->Segments
[HeapEntry
->SegmentOffset
];
3237 if (BigAllocation
&&
3238 (((ULONG_PTR
)HeapEntry
& (PAGE_SIZE
- 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
)))
3241 if (!BigAllocation
&& (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
||
3243 HeapEntry
< Segment
->FirstEntry
||
3244 HeapEntry
>= Segment
->LastValidEntry
))
3247 if ((HeapEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
) &&
3248 !RtlpCheckInUsePattern(HeapEntry
))
3251 /* Checks are done, if this is a virtual entry, that's all */
3252 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) return TRUE
;
3254 /* Go through segments and check if this entry fits into any of them */
3255 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3257 Segment
= Heap
->Segments
[SegmentOffset
];
3258 if (!Segment
) continue;
3260 if ((HeapEntry
>= Segment
->FirstEntry
) &&
3261 (HeapEntry
< Segment
->LastValidEntry
))
3269 /* Return our result of finding entry in the segments */
3273 DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry
, Heap
);
3278 RtlpValidateHeapSegment(
3280 PHEAP_SEGMENT Segment
,
3281 UCHAR SegmentOffset
,
3282 PULONG FreeEntriesCount
,
3283 PSIZE_T TotalFreeSize
,
3285 PSIZE_T PseudoTagEntries
)
3287 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
3288 PLIST_ENTRY UcrEntry
;
3289 SIZE_T ByteSize
, Size
, Result
;
3290 PHEAP_ENTRY CurrentEntry
;
3291 ULONG UnCommittedPages
;
3292 ULONG UnCommittedRanges
;
3295 UnCommittedPages
= 0;
3296 UnCommittedRanges
= 0;
3298 if (IsListEmpty(&Segment
->UCRSegmentList
))
3301 UcrDescriptor
= NULL
;
3305 UcrEntry
= Segment
->UCRSegmentList
.Flink
;
3306 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3309 if (Segment
->BaseAddress
== Heap
)
3310 CurrentEntry
= &Heap
->Entry
;
3312 CurrentEntry
= &Segment
->Entry
;
3314 while (CurrentEntry
< Segment
->LastValidEntry
)
3316 if (UcrDescriptor
&&
3317 ((PVOID
)CurrentEntry
>= UcrDescriptor
->Address
))
3319 DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
3320 CurrentEntry
, UcrDescriptor
->Address
,
3321 (PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3328 while (CurrentEntry
< Segment
->LastValidEntry
)
3330 if (PreviousSize
!= CurrentEntry
->PreviousSize
)
3332 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
3333 CurrentEntry
, CurrentEntry
->PreviousSize
, PreviousSize
);
3338 PreviousSize
= CurrentEntry
->Size
;
3339 Size
= CurrentEntry
->Size
<< HEAP_ENTRY_SHIFT
;
3341 if (CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
)
3348 /* Check fill pattern */
3349 if (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
)
3351 if (!RtlpCheckInUsePattern(CurrentEntry
))
3357 /* The entry is free, increase free entries count and total free size */
3358 *FreeEntriesCount
= *FreeEntriesCount
+ 1;
3359 *TotalFreeSize
+= CurrentEntry
->Size
;
3361 if ((Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
) &&
3362 (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
3364 ByteSize
= Size
- sizeof(HEAP_FREE_ENTRY
);
3366 if ((CurrentEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) &&
3367 (ByteSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
)))
3369 ByteSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
3372 Result
= RtlCompareMemoryUlong((PCHAR
)((PHEAP_FREE_ENTRY
)CurrentEntry
+ 1),
3376 if (Result
!= ByteSize
)
3378 DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
3380 (PCHAR
)(CurrentEntry
+ 1) + Result
);
3387 if (CurrentEntry
->SegmentOffset
!= SegmentOffset
)
3389 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry
, SegmentOffset
, CurrentEntry
->SegmentOffset
);
3393 /* Check if it's the last entry */
3394 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
3396 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3400 /* Check if it's not really the last one */
3401 if (CurrentEntry
!= Segment
->LastValidEntry
)
3403 DPRINT1("HEAP: Heap entry %p is not last block in segment (%x)\n", CurrentEntry
, Segment
->LastValidEntry
);
3407 else if (CurrentEntry
!= UcrDescriptor
->Address
)
3409 DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
3410 CurrentEntry
, UcrDescriptor
->Address
);
3416 UnCommittedPages
+= (UcrDescriptor
->Size
/ PAGE_SIZE
);
3417 UnCommittedRanges
++;
3419 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3421 /* Go to the next UCR descriptor */
3422 UcrEntry
= UcrEntry
->Flink
;
3423 if (UcrEntry
== &Segment
->UCRSegmentList
)
3426 UcrDescriptor
= NULL
;
3430 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3437 /* Advance to the next entry */
3438 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3442 /* Check total numbers of UCP and UCR */
3443 if (Segment
->NumberOfUnCommittedPages
!= UnCommittedPages
)
3445 DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
3446 Segment
, Segment
->NumberOfUnCommittedPages
, UnCommittedPages
);
3451 if (Segment
->NumberOfUnCommittedRanges
!= UnCommittedRanges
)
3453 DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
3454 Segment
, Segment
->NumberOfUnCommittedRanges
, UnCommittedRanges
);
3463 RtlpValidateHeap(PHEAP Heap
,
3464 BOOLEAN ForceValidation
)
3466 PHEAP_SEGMENT Segment
;
3468 UCHAR SegmentOffset
;
3469 SIZE_T Size
, TotalFreeSize
;
3471 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
3472 PLIST_ENTRY ListHead
, NextEntry
;
3473 PHEAP_FREE_ENTRY FreeEntry
;
3474 ULONG FreeBlocksCount
, FreeListEntriesCount
;
3477 if (!RtlpValidateHeapHeaders(Heap
, FALSE
))
3480 /* Skip validation if it's not needed */
3481 if (!ForceValidation
&& !(Heap
->Flags
& HEAP_VALIDATE_ALL_ENABLED
))
3484 /* Check free lists bitmaps */
3485 FreeListEntriesCount
= 0;
3486 ListHead
= &Heap
->FreeLists
[0];
3488 for (Size
= 0; Size
< HEAP_FREELISTS
; Size
++)
3492 /* This is a dedicated list. Check if it's empty */
3493 EmptyList
= IsListEmpty(ListHead
);
3495 if (Heap
->u
.FreeListsInUseBytes
[Size
>> 3] & (1 << (Size
& 7)))
3499 DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size
);
3507 DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size
);
3513 /* Now check this list entries */
3514 NextEntry
= ListHead
->Flink
;
3517 while (ListHead
!= NextEntry
)
3519 FreeEntry
= CONTAINING_RECORD(NextEntry
, HEAP_FREE_ENTRY
, FreeList
);
3520 NextEntry
= NextEntry
->Flink
;
3522 /* If there is an in-use entry in a free list - that's quite a big problem */
3523 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
)
3525 DPRINT1("HEAP: %x-dedicated list free element %x is marked in-use\n", Size
, FreeEntry
);
3529 /* Check sizes according to that specific list's size */
3530 if ((Size
== 0) && (FreeEntry
->Size
< HEAP_FREELISTS
))
3532 DPRINT1("HEAP: Non dedicated list free element %x has size %x which would fit a dedicated list\n", FreeEntry
, FreeEntry
->Size
);
3535 else if (Size
&& (FreeEntry
->Size
!= Size
))
3537 DPRINT1("HEAP: %x-dedicated list free element %x has incorrect size %x\n", Size
, FreeEntry
, FreeEntry
->Size
);
3540 else if ((Size
== 0) && (FreeEntry
->Size
< PreviousSize
))
3542 DPRINT1("HEAP: Non dedicated list free element %x is not put in order\n", FreeEntry
);
3546 /* Remember previous size*/
3547 PreviousSize
= FreeEntry
->Size
;
3549 /* Add up to the total amount of free entries */
3550 FreeListEntriesCount
++;
3553 /* Go to the head of the next free list */
3557 /* Check big allocations */
3558 ListHead
= &Heap
->VirtualAllocdBlocks
;
3559 NextEntry
= ListHead
->Flink
;
3561 while (ListHead
!= NextEntry
)
3563 VirtualAllocBlock
= CONTAINING_RECORD(NextEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
3565 /* We can only check the fill pattern */
3566 if (VirtualAllocBlock
->BusyBlock
.Flags
& HEAP_ENTRY_FILL_PATTERN
)
3568 if (!RtlpCheckInUsePattern(&VirtualAllocBlock
->BusyBlock
))
3572 NextEntry
= NextEntry
->Flink
;
3575 /* Check all segments */
3576 FreeBlocksCount
= 0;
3579 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3581 Segment
= Heap
->Segments
[SegmentOffset
];
3583 /* Go to the next one if there is no segment */
3584 if (!Segment
) continue;
3586 if (!RtlpValidateHeapSegment(Heap
,
3598 if (FreeListEntriesCount
!= FreeBlocksCount
)
3600 DPRINT1("HEAP: Free blocks count in arena (%d) does not match free blocks number in the free lists (%d)\n", FreeBlocksCount
, FreeListEntriesCount
);
3604 if (Heap
->TotalFreeSize
!= TotalFreeSize
)
3606 DPRINT1("HEAP: Total size of free blocks in arena (%d) does not equal to the one in heap header (%d)\n", TotalFreeSize
, Heap
->TotalFreeSize
);
3613 /***********************************************************************
3615 * Validates a specified heap.
3618 * Heap [in] Handle to the heap
3619 * Flags [in] Bit flags that control access during operation
3620 * Block [in] Optional pointer to memory block to validate
3631 BOOLEAN NTAPI
RtlValidateHeap(
3637 PHEAP Heap
= (PHEAP
)HeapPtr
;
3638 BOOLEAN HeapLocked
= FALSE
;
3641 /* Check for page heap */
3642 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
)
3643 return RtlpDebugPageHeapValidate(HeapPtr
, Flags
, Block
);
3645 /* Check signature */
3646 if (Heap
->Signature
!= HEAP_SIGNATURE
)
3648 DPRINT1("HEAP: Signature %x is invalid for heap %p\n", Heap
->Signature
, Heap
);
3653 Flags
= Heap
->ForceFlags
;
3655 /* Acquire the lock if necessary */
3656 if (!(Flags
& HEAP_NO_SERIALIZE
))
3658 RtlEnterHeapLock(Heap
->LockVariable
);
3662 /* Either validate whole heap or just one entry */
3664 HeapValid
= RtlpValidateHeap(Heap
, TRUE
);
3666 HeapValid
= RtlpValidateHeapEntry(Heap
, (PHEAP_ENTRY
)Block
- 1);
3668 /* Unlock if it's lockable */
3671 RtlLeaveHeapLock(Heap
->LockVariable
);
3678 RtlInitializeHeapManager(VOID
)
3683 Peb
= RtlGetCurrentPeb();
3685 /* Initialize heap-related fields of PEB */
3686 Peb
->NumberOfHeaps
= 0;
3688 /* Initialize the process heaps list protecting lock */
3689 RtlInitializeHeapLock(&RtlpProcessHeapsListLock
);
3697 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3701 return STATUS_NOT_IMPLEMENTED
;
3709 RtlGetProcessHeaps(ULONG count
,
3721 RtlValidateProcessHeaps(VOID
)
3733 IN PVOID HeapHandle
,
3746 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3748 IN PVOID BaseAddress
,
3751 PHEAP Heap
= (PHEAP
)HeapHandle
;
3752 PHEAP_ENTRY HeapEntry
;
3753 PHEAP_ENTRY_EXTRA Extra
;
3754 BOOLEAN HeapLocked
= FALSE
;
3757 Flags
|= Heap
->Flags
;
3759 /* Call special heap */
3760 if (RtlpHeapIsSpecial(Flags
))
3761 return RtlDebugSetUserValueHeap(Heap
, Flags
, BaseAddress
, UserValue
);
3763 /* Lock if it's lockable */
3764 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3766 RtlEnterHeapLock(Heap
->LockVariable
);
3770 /* Get a pointer to the entry */
3771 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3773 /* If it's a free entry - return error */
3774 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3776 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3778 /* Release the heap lock if it was acquired */
3780 RtlLeaveHeapLock(Heap
->LockVariable
);
3785 /* Check if this entry has an extra stuff associated with it */
3786 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3788 /* Use extra to store the value */
3789 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3790 Extra
->Settable
= (ULONG_PTR
)UserValue
;
3793 /* Release the heap lock if it was acquired */
3795 RtlLeaveHeapLock(Heap
->LockVariable
);
3805 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3807 IN PVOID BaseAddress
,
3808 IN ULONG UserFlagsReset
,
3809 IN ULONG UserFlagsSet
)
3811 PHEAP Heap
= (PHEAP
)HeapHandle
;
3812 PHEAP_ENTRY HeapEntry
;
3813 BOOLEAN HeapLocked
= FALSE
;
3816 Flags
|= Heap
->Flags
;
3818 /* Call special heap */
3819 if (RtlpHeapIsSpecial(Flags
))
3820 return RtlDebugSetUserFlagsHeap(Heap
, Flags
, BaseAddress
, UserFlagsReset
, UserFlagsSet
);
3822 /* Lock if it's lockable */
3823 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3825 RtlEnterHeapLock(Heap
->LockVariable
);
3829 /* Get a pointer to the entry */
3830 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3832 /* If it's a free entry - return error */
3833 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3835 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3837 /* Release the heap lock if it was acquired */
3839 RtlLeaveHeapLock(Heap
->LockVariable
);
3844 /* Set / reset flags */
3845 HeapEntry
->Flags
&= ~(UserFlagsReset
>> 4);
3846 HeapEntry
->Flags
|= (UserFlagsSet
>> 4);
3848 /* Release the heap lock if it was acquired */
3850 RtlLeaveHeapLock(Heap
->LockVariable
);
3860 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3862 IN PVOID BaseAddress
,
3863 OUT PVOID
*UserValue
,
3864 OUT PULONG UserFlags
)
3866 PHEAP Heap
= (PHEAP
)HeapHandle
;
3867 PHEAP_ENTRY HeapEntry
;
3868 PHEAP_ENTRY_EXTRA Extra
;
3869 BOOLEAN HeapLocked
= FALSE
;
3872 Flags
|= Heap
->Flags
;
3874 /* Call special heap */
3875 if (RtlpHeapIsSpecial(Flags
))
3876 return RtlDebugGetUserInfoHeap(Heap
, Flags
, BaseAddress
, UserValue
, UserFlags
);
3878 /* Lock if it's lockable */
3879 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3881 RtlEnterHeapLock(Heap
->LockVariable
);
3885 /* Get a pointer to the entry */
3886 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3888 /* If it's a free entry - return error */
3889 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3891 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3893 /* Release the heap lock if it was acquired */
3895 RtlLeaveHeapLock(Heap
->LockVariable
);
3900 /* Check if this entry has an extra stuff associated with it */
3901 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3903 /* Get pointer to extra data */
3904 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3906 /* Pass user value */
3908 *UserValue
= (PVOID
)Extra
->Settable
;
3910 /* Decode and return user flags */
3912 *UserFlags
= (HeapEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4;
3915 /* Release the heap lock if it was acquired */
3917 RtlLeaveHeapLock(Heap
->LockVariable
);
3927 RtlUsageHeap(IN HANDLE Heap
,
3929 OUT PRTL_HEAP_USAGE Usage
)
3933 return STATUS_NOT_IMPLEMENTED
;
3938 RtlQueryTagHeap(IN PVOID HeapHandle
,
3941 IN BOOLEAN ResetCounters
,
3942 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
3951 RtlExtendHeap(IN HANDLE Heap
,
3963 RtlCreateTagHeap(IN HANDLE HeapHandle
,
3966 IN PWSTR TagSubName
)
3975 RtlWalkHeap(IN HANDLE HeapHandle
,
3979 return STATUS_NOT_IMPLEMENTED
;
3984 RtlProtectHeap(IN PVOID HeapHandle
,
3985 IN BOOLEAN ReadOnly
)
3993 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
3994 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
3995 IN PVOID HeapInformation
,
3996 IN SIZE_T HeapInformationLength
)
3998 /* Setting heap information is not really supported except for enabling LFH */
3999 if (HeapInformationClass
== 0) return STATUS_SUCCESS
;
4001 /* Check buffer length */
4002 if (HeapInformationLength
< sizeof(ULONG
))
4004 /* The provided buffer is too small */
4005 return STATUS_BUFFER_TOO_SMALL
;
4008 /* Check for a special magic value for enabling LFH */
4009 if (*(PULONG
)HeapInformation
== 2)
4011 DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
4012 return STATUS_SUCCESS
;
4015 return STATUS_UNSUCCESSFUL
;
4020 RtlQueryHeapInformation(HANDLE HeapHandle
,
4021 HEAP_INFORMATION_CLASS HeapInformationClass
,
4022 PVOID HeapInformation OPTIONAL
,
4023 SIZE_T HeapInformationLength OPTIONAL
,
4024 PSIZE_T ReturnLength OPTIONAL
)
4026 PHEAP Heap
= (PHEAP
)HeapHandle
;
4028 /* Only HeapCompatibilityInformation is supported */
4029 if (HeapInformationClass
!= HeapCompatibilityInformation
)
4030 return STATUS_UNSUCCESSFUL
;
4032 /* Set result length */
4033 if (ReturnLength
) *ReturnLength
= sizeof(ULONG
);
4035 /* Check buffer length */
4036 if (HeapInformationLength
< sizeof(ULONG
))
4038 /* It's too small, return needed length */
4039 return STATUS_BUFFER_TOO_SMALL
;
4042 /* Return front end heap type */
4043 *(PULONG
)HeapInformation
= Heap
->FrontEndHeapType
;
4045 return STATUS_SUCCESS
;
4050 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
4062 RtlMultipleFreeHeap(IN PVOID HeapHandle
,