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 /* Check if PreviousSize of the next entry matches ours */
239 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
241 ASSERT(((PHEAP_ENTRY
)FreeEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
244 /* Insert it either into dedicated or non-dedicated list */
245 if (BlockSize
< HEAP_FREELISTS
)
248 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
250 if (IsListEmpty(FreeListHead
))
252 RtlpSetFreeListsBit(Heap
, FreeEntry
);
257 /* Non-dedicated one */
258 FreeListHead
= &Heap
->FreeLists
[0];
259 Current
= FreeListHead
->Flink
;
261 /* Find a position where to insert it to (the list must be sorted) */
262 while (FreeListHead
!= Current
)
264 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
266 if (BlockSize
<= CurrentEntry
->Size
)
269 Current
= Current
->Flink
;
272 FreeListHead
= Current
;
275 /* Actually insert it into the list */
276 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
280 RtlpInsertFreeBlock(PHEAP Heap
,
281 PHEAP_FREE_ENTRY FreeEntry
,
284 USHORT Size
, PreviousSize
;
285 UCHAR SegmentOffset
, Flags
;
286 PHEAP_SEGMENT Segment
;
288 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
290 /* Increase the free size counter */
291 Heap
->TotalFreeSize
+= BlockSize
;
293 /* Remember certain values */
294 Flags
= FreeEntry
->Flags
;
295 PreviousSize
= FreeEntry
->PreviousSize
;
296 SegmentOffset
= FreeEntry
->SegmentOffset
;
297 Segment
= Heap
->Segments
[SegmentOffset
];
302 /* Check for the max size */
303 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
305 Size
= HEAP_MAX_BLOCK_SIZE
;
307 /* Special compensation if it goes above limit just by 1 */
308 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
311 FreeEntry
->Flags
= 0;
316 FreeEntry
->Flags
= Flags
;
319 /* Change its size and insert it into a free list */
320 FreeEntry
->Size
= Size
;
321 FreeEntry
->PreviousSize
= PreviousSize
;
322 FreeEntry
->SegmentOffset
= SegmentOffset
;
324 /* Call a helper to actually insert the block */
325 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
331 /* Go to the next entry */
332 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
334 /* Check if that's all */
335 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
338 /* Update previous size if needed */
339 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
340 FreeEntry
->PreviousSize
= PreviousSize
;
344 RtlpRemoveFreeBlock(PHEAP Heap
,
345 PHEAP_FREE_ENTRY FreeEntry
,
349 SIZE_T Result
, RealSize
;
350 PLIST_ENTRY OldBlink
, OldFlink
;
352 // FIXME: Maybe use RemoveEntryList?
354 /* Remove the free block */
355 OldFlink
= FreeEntry
->FreeList
.Flink
;
356 OldBlink
= FreeEntry
->FreeList
.Blink
;
357 OldBlink
->Flink
= OldFlink
;
358 OldFlink
->Blink
= OldBlink
;
360 /* Update the freelists bitmap */
361 if ((OldFlink
== OldBlink
) &&
362 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
364 RtlpClearFreeListsBit(Heap
, FreeEntry
);
367 /* Fill with pattern if necessary */
369 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
371 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
373 /* Deduct extra stuff from block's real size */
374 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
375 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
377 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
380 /* Check if the free filler is intact */
381 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
385 if (Result
!= RealSize
)
387 DPRINT1("Free heap block %p modified at %p after it was freed\n",
389 (PCHAR
)(FreeEntry
+ 1) + Result
);
395 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
397 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
399 /* Get pointer to the containing record */
400 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
402 /* Restore the real size */
403 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
406 PHEAP_UCR_DESCRIPTOR NTAPI
407 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
410 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
411 PHEAP_UCR_SEGMENT UcrSegment
;
412 PHEAP Heap
= Segment
->Heap
;
413 SIZE_T ReserveSize
= 16 * PAGE_SIZE
;
414 SIZE_T CommitSize
= 1 * PAGE_SIZE
;
417 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
419 /* Check if we have unused UCRs */
420 if (IsListEmpty(&Heap
->UCRList
))
422 /* Get a pointer to the first UCR segment */
423 UcrSegment
= CONTAINING_RECORD(&Heap
->UCRSegments
.Flink
, HEAP_UCR_SEGMENT
, ListEntry
);
425 /* Check the list of UCR segments */
426 if (IsListEmpty(&Heap
->UCRSegments
) ||
427 UcrSegment
->ReservedSize
== UcrSegment
->CommittedSize
)
429 /* We need to create a new one. Reserve 16 pages for it */
431 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
432 (PVOID
*)&UcrSegment
,
438 if (!NT_SUCCESS(Status
)) return NULL
;
440 /* Commit one page */
441 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
442 (PVOID
*)&UcrSegment
,
448 if (!NT_SUCCESS(Status
))
450 /* Release reserved memory */
451 ZwFreeVirtualMemory(NtCurrentProcess(),
452 (PVOID
*)&UcrDescriptor
,
459 UcrSegment
->ReservedSize
= ReserveSize
;
460 UcrSegment
->CommittedSize
= CommitSize
;
462 /* Add it to the head of the list */
463 InsertHeadList(&Heap
->UCRSegments
, &UcrSegment
->ListEntry
);
465 /* Get a pointer to the first available UCR descriptor */
466 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)(UcrSegment
+ 1);
470 /* It's possible to use existing UCR segment. Commit one more page */
471 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
);
472 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
473 (PVOID
*)&UcrDescriptor
,
479 if (!NT_SUCCESS(Status
)) return NULL
;
482 UcrSegment
->CommittedSize
+= CommitSize
;
485 /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
486 while ((PCHAR
)UcrDescriptor
< ((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
))
488 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
493 /* There are unused UCRs, just get the first one */
494 Entry
= RemoveHeadList(&Heap
->UCRList
);
495 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
496 return UcrDescriptor
;
500 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
501 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
504 UcrDescriptor
->Address
= NULL
;
505 UcrDescriptor
->Size
= 0;
507 /* Put it into the heap's list of unused UCRs */
508 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
512 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
517 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
519 DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment
, Address
, Size
);
521 /* Go through the list of UCR descriptors, they are sorted from lowest address
523 Current
= Segment
->UCRSegmentList
.Flink
;
524 while(Current
!= &Segment
->UCRSegmentList
)
526 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
528 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
530 /* Check for a really lucky case */
531 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
534 UcrDescriptor
->Address
= (PVOID
)Address
;
535 UcrDescriptor
->Size
+= Size
;
539 /* We found the block after which the new one should go */
542 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
544 /* Modify this entry */
545 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
546 Size
+= UcrDescriptor
->Size
;
548 /* Remove it from the list and destroy it */
549 RemoveEntryList(Current
);
550 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
552 Segment
->NumberOfUnCommittedRanges
--;
556 /* Advance to the next descriptor */
557 Current
= Current
->Flink
;
561 /* Create a new UCR descriptor */
562 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
563 if (!UcrDescriptor
) return;
565 UcrDescriptor
->Address
= (PVOID
)Address
;
566 UcrDescriptor
->Size
= Size
;
568 /* "Current" is the descriptor after which our one should go */
569 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
571 DPRINT("Added segment UCR with base %p, size 0x%x\n", Address
, Size
);
573 /* Increase counters */
574 Segment
->NumberOfUnCommittedRanges
++;
577 PHEAP_FREE_ENTRY NTAPI
578 RtlpFindAndCommitPages(PHEAP Heap
,
579 PHEAP_SEGMENT Segment
,
581 PVOID AddressRequested
)
584 ULONG_PTR Address
= 0;
585 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
586 PHEAP_ENTRY FirstEntry
, LastEntry
, PreviousLastEntry
;
589 DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap
, Segment
, *Size
, Address
);
591 /* Go through UCRs in a segment */
592 Current
= Segment
->UCRSegmentList
.Flink
;
593 while(Current
!= &Segment
->UCRSegmentList
)
595 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
597 /* Check if we can use that one right away */
598 if (UcrDescriptor
->Size
>= *Size
&&
599 (UcrDescriptor
->Address
== AddressRequested
|| !AddressRequested
))
601 /* Get the address */
602 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
605 if (Heap
->CommitRoutine
)
607 Status
= Heap
->CommitRoutine(Heap
, (PVOID
*)&Address
, Size
);
611 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
619 DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size
, Address
, UcrDescriptor
->Size
);
621 /* Fail in unsuccessful case */
622 if (!NT_SUCCESS(Status
))
624 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
628 /* Update tracking numbers */
629 Segment
->NumberOfUnCommittedPages
-= *Size
/ PAGE_SIZE
;
631 /* Calculate first and last entries */
632 FirstEntry
= (PHEAP_ENTRY
)Address
;
634 if ((Segment
->LastEntryInSegment
->Flags
& HEAP_ENTRY_LAST_ENTRY
) &&
635 (ULONG_PTR
)(Segment
->LastEntryInSegment
+ Segment
->LastEntryInSegment
->Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
637 LastEntry
= Segment
->LastEntryInSegment
;
641 /* Go through the entries to find the last one */
644 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
646 LastEntry
= Segment
->FirstEntry
;
648 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
650 PreviousLastEntry
= LastEntry
;
651 LastEntry
+= LastEntry
->Size
;
653 if ((ULONG_PTR
)LastEntry
>= (ULONG_PTR
)Segment
->LastValidEntry
||
654 LastEntry
->Size
== 0)
656 if (LastEntry
== (PHEAP_ENTRY
)Address
)
659 LastEntry
= PreviousLastEntry
;
663 DPRINT1("Last entry not found in a committed range near to %p\n", PreviousLastEntry
);
669 /* Unmark it as a last entry */
670 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
672 /* Update UCR descriptor */
673 UcrDescriptor
->Address
= (PVOID
)((ULONG_PTR
)UcrDescriptor
->Address
+ *Size
);
674 UcrDescriptor
->Size
-= *Size
;
676 DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
677 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
679 /* Check if anything left in this UCR */
680 if (UcrDescriptor
->Size
== 0)
682 /* It's fully exhausted */
683 if (UcrDescriptor
->Address
== Segment
->LastValidEntry
)
685 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
686 Segment
->LastEntryInSegment
= FirstEntry
;
690 FirstEntry
->Flags
= 0;
691 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
694 /* This UCR needs to be removed because it became useless */
695 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
697 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
698 Segment
->NumberOfUnCommittedRanges
--;
702 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
703 Segment
->LastEntryInSegment
= FirstEntry
;
706 /* Set various first entry fields*/
707 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
708 FirstEntry
->Size
= *Size
>> HEAP_ENTRY_SHIFT
;
709 FirstEntry
->PreviousSize
= LastEntry
->Size
;
711 /* Update previous size */
712 if (!(FirstEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
713 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
716 return (PHEAP_FREE_ENTRY
)FirstEntry
;
719 /* Advance to the next descriptor */
720 PreviousUcr
= UcrDescriptor
;
721 Current
= Current
->Flink
;
728 RtlpDeCommitFreeBlock(PHEAP Heap
,
729 PHEAP_FREE_ENTRY FreeEntry
,
732 PHEAP_SEGMENT Segment
;
733 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
734 PHEAP_FREE_ENTRY NextFreeEntry
;
735 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
736 ULONG PrecedingSize
, NextSize
, DecommitSize
;
737 ULONG_PTR DecommitBase
;
740 DPRINT("Decommitting %p %p %x\n", Heap
, FreeEntry
, Size
);
742 /* We can't decommit if there is a commit routine! */
743 if (Heap
->CommitRoutine
)
745 /* Just add it back the usual way */
746 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
750 /* Get the segment */
751 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
753 /* Get the preceding entry */
754 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
755 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
757 if (PrecedingSize
== 1)
759 /* Just 1 heap entry, increase the base/size */
760 DecommitBase
+= PAGE_SIZE
;
761 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
763 else if (FreeEntry
->PreviousSize
&&
764 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
766 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
769 /* Get the next entry */
770 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
771 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
772 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
776 /* Just 1 heap entry, increase the size */
777 DecommitSize
-= PAGE_SIZE
;
778 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
780 else if (NextSize
== 0 &&
781 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
783 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
786 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
788 /* Calculate real decommit size */
789 if (DecommitSize
> DecommitBase
)
791 DecommitSize
-= DecommitBase
;
795 /* Nothing to decommit */
796 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
800 /* A decommit is necessary. Create a UCR descriptor */
801 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
804 DPRINT1("HEAP: Failed to create UCR descriptor\n");
805 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
809 /* Decommit the memory */
810 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
811 (PVOID
*)&DecommitBase
,
815 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
816 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
818 if (!NT_SUCCESS(Status
))
820 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
824 /* Insert uncommitted pages */
825 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
826 Segment
->NumberOfUnCommittedPages
+= (DecommitSize
/ PAGE_SIZE
);
830 /* Adjust size of this free entry and insert it */
831 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
832 FreeEntry
->Size
= PrecedingSize
;
833 Heap
->TotalFreeSize
+= PrecedingSize
;
835 /* Set last entry in the segment to this entry */
836 Segment
->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
838 /* Insert it into the free list */
839 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
, FALSE
);
841 else if (PrecedingInUseEntry
)
843 /* Adjust preceding in use entry */
844 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
845 Segment
->LastEntryInSegment
= PrecedingInUseEntry
;
846 } else if ((ULONG_PTR
)Segment
->LastEntryInSegment
>= DecommitBase
&&
847 ((PCHAR
)Segment
->LastEntryInSegment
< ((PCHAR
)DecommitBase
+ DecommitSize
)))
849 /* Update this segment's last entry */
850 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
853 /* Now the next one */
856 /* Adjust size of this free entry and insert it */
857 NextFreeEntry
->Flags
= 0;
858 NextFreeEntry
->PreviousSize
= 0;
859 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
860 NextFreeEntry
->Size
= NextSize
;
862 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= NextSize
;
864 Heap
->TotalFreeSize
+= NextSize
;
865 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
, FALSE
);
867 else if (NextInUseEntry
)
869 NextInUseEntry
->PreviousSize
= 0;
874 RtlpInitializeHeapSegment(PHEAP Heap
,
875 PHEAP_SEGMENT Segment
,
879 PVOID UncommittedBase
,
882 ULONG Pages
, CommitSize
;
883 PHEAP_ENTRY HeapEntry
;
884 USHORT PreviousSize
= 0, NewSize
;
887 Pages
= ((PCHAR
)LimitAddress
- (PCHAR
)BaseAddress
) / PAGE_SIZE
;
889 HeapEntry
= (PHEAP_ENTRY
)ROUND_UP(Segment
+ 1, HEAP_ENTRY_SIZE
);
891 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %p %p %p)\n", Heap
, Segment
, SegmentIndex
, Flags
, BaseAddress
, UncommittedBase
, LimitAddress
);
892 DPRINT("Pages %x, HeapEntry %p, sizeof(HEAP_SEGMENT) %x\n", Pages
, HeapEntry
, sizeof(HEAP_SEGMENT
));
894 /* Check if it's the first segment and remember its size */
895 if (Heap
== BaseAddress
)
896 PreviousSize
= Heap
->Entry
.Size
;
898 NewSize
= ((PCHAR
)HeapEntry
- (PCHAR
)Segment
) >> HEAP_ENTRY_SHIFT
;
900 if ((PVOID
)(HeapEntry
+ 1) >= UncommittedBase
)
902 /* Check if it goes beyond the limit */
903 if ((PVOID
)(HeapEntry
+ 1) >= LimitAddress
)
906 /* Need to commit memory */
907 CommitSize
= (PCHAR
)(HeapEntry
+ 1) - (PCHAR
)UncommittedBase
;
908 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
909 (PVOID
)&UncommittedBase
,
914 if (!NT_SUCCESS(Status
))
916 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
920 DPRINT("Committed %d bytes at base %p\n", CommitSize
, UncommittedBase
);
922 /* Calcule the new uncommitted base */
923 UncommittedBase
= (PVOID
)((PCHAR
)UncommittedBase
+ CommitSize
);
926 /* Initialize the segment entry */
927 Segment
->Entry
.PreviousSize
= PreviousSize
;
928 Segment
->Entry
.Size
= NewSize
;
929 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
930 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
932 /* Initialize the segment itself */
933 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
934 Segment
->Heap
= Heap
;
935 Segment
->BaseAddress
= BaseAddress
;
936 Segment
->FirstEntry
= HeapEntry
;
937 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((PCHAR
)BaseAddress
+ Pages
* PAGE_SIZE
);
938 Segment
->NumberOfPages
= Pages
;
939 Segment
->NumberOfUnCommittedPages
= ((PCHAR
)LimitAddress
- (PCHAR
)UncommittedBase
) / PAGE_SIZE
;
940 InitializeListHead(&Segment
->UCRSegmentList
);
942 /* Insert uncommitted pages into UCR (uncommitted ranges) list */
943 if (Segment
->NumberOfUnCommittedPages
)
945 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
)UncommittedBase
, Segment
->NumberOfUnCommittedPages
* PAGE_SIZE
);
948 /* Set the segment index pointer */
949 Heap
->Segments
[SegmentIndex
] = Segment
;
951 /* Prepare a free heap entry */
952 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
953 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
954 HeapEntry
->SegmentOffset
= SegmentIndex
;
956 /* Set last entry in segment */
957 Segment
->LastEntryInSegment
= HeapEntry
;
960 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, (PHEAP_ENTRY
)UncommittedBase
- HeapEntry
);
966 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
972 /* Make sure it's not user allocated */
973 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
975 BaseAddress
= Segment
->BaseAddress
;
976 DPRINT("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
978 /* Release virtual memory */
979 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
984 if (!NT_SUCCESS(Status
))
986 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
992 RtlpAddHeapToProcessList(PHEAP Heap
)
997 Peb
= RtlGetCurrentPeb();
999 /* Acquire the lock */
1000 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1003 /* Check if max number of heaps reached */
1004 if (Peb
->NumberOfHeaps
== Peb
->MaximumNumberOfHeaps
)
1006 // TODO: Handle this case
1010 /* Add the heap to the process heaps */
1011 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = Heap
;
1012 Peb
->NumberOfHeaps
++;
1013 Heap
->ProcessHeapsListIndex
= Peb
->NumberOfHeaps
;
1014 // } _SEH2_FINALLY {
1016 /* Release the lock */
1017 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1022 /* Usermode only! */
1024 RtlpRemoveHeapFromProcessList(PHEAP Heap
)
1027 PHEAP
*Current
, *Next
;
1031 Peb
= RtlGetCurrentPeb();
1033 /* Acquire the lock */
1034 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1036 /* Check if we don't need anything to do */
1037 if ((Heap
->ProcessHeapsListIndex
== 0) ||
1038 (Heap
->ProcessHeapsListIndex
> Peb
->NumberOfHeaps
) ||
1039 (Peb
->NumberOfHeaps
== 0))
1041 /* Release the lock */
1042 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1047 /* The process actually has more than one heap.
1048 Use classic, lernt from university times algorithm for removing an entry
1049 from a static array */
1051 Current
= (PHEAP
*)&Peb
->ProcessHeaps
[Heap
->ProcessHeapsListIndex
- 1];
1054 /* How many items we need to shift to the left */
1055 Count
= Peb
->NumberOfHeaps
- (Heap
->ProcessHeapsListIndex
- 1);
1057 /* Move them all in a loop */
1060 /* Copy it and advance next pointer */
1063 /* Update its index */
1064 (*Current
)->ProcessHeapsListIndex
-= 1;
1066 /* Advance pointers */
1071 /* Decrease total number of heaps */
1072 Peb
->NumberOfHeaps
--;
1074 /* Zero last unused item */
1075 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = NULL
;
1076 Heap
->ProcessHeapsListIndex
= 0;
1078 /* Release the lock */
1079 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1082 PHEAP_FREE_ENTRY NTAPI
1083 RtlpCoalesceHeap(PHEAP Heap
)
1089 PHEAP_FREE_ENTRY NTAPI
1090 RtlpCoalesceFreeBlocks (PHEAP Heap
,
1091 PHEAP_FREE_ENTRY FreeEntry
,
1095 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
1097 /* Get the previous entry */
1098 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
1101 if (CurrentEntry
!= FreeEntry
&&
1102 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1103 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
1105 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
1107 /* Remove it if asked for */
1110 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1111 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1113 /* Remove it only once! */
1117 /* Remove previous entry too */
1118 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1121 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1123 /* Update last entry in the segment */
1124 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1125 Heap
->Segments
[CurrentEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)CurrentEntry
;
1127 /* Advance FreeEntry and update sizes */
1128 FreeEntry
= CurrentEntry
;
1129 *FreeSize
= *FreeSize
+ CurrentEntry
->Size
;
1130 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1131 FreeEntry
->Size
= *FreeSize
;
1133 /* Also update previous size if needed */
1134 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1136 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1140 /* Check the next block if it exists */
1141 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1143 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1145 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1146 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1148 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1150 /* Remove it if asked for */
1153 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1154 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1158 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1160 /* Update last entry in the segment */
1161 if (FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1162 Heap
->Segments
[FreeEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
1164 /* Remove next entry now */
1165 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1168 *FreeSize
= *FreeSize
+ NextEntry
->Size
;
1169 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1170 FreeEntry
->Size
= *FreeSize
;
1172 /* Also update previous size if needed */
1173 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1175 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1182 PHEAP_FREE_ENTRY NTAPI
1183 RtlpExtendHeap(PHEAP Heap
,
1187 UCHAR Index
, EmptyIndex
;
1188 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1189 PHEAP_SEGMENT Segment
;
1190 PHEAP_FREE_ENTRY FreeEntry
;
1193 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1195 /* Calculate amount in pages */
1196 Pages
= (Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
1197 FreeSize
= Pages
* PAGE_SIZE
;
1198 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1200 /* Find an empty segment */
1201 EmptyIndex
= HEAP_SEGMENTS
;
1202 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1204 Segment
= Heap
->Segments
[Index
];
1206 if (Segment
) DPRINT("Segment[%d] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1208 /* Check if its size suits us */
1210 Pages
<= Segment
->NumberOfUnCommittedPages
)
1212 DPRINT("This segment is suitable\n");
1214 /* Commit needed amount */
1215 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1217 /* Coalesce it with adjacent entries */
1220 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1221 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1222 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1226 else if (!Segment
&&
1227 EmptyIndex
== HEAP_SEGMENTS
)
1229 /* Remember the first unused segment index */
1234 /* No luck, need to grow the heap */
1235 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1236 (EmptyIndex
!= HEAP_SEGMENTS
))
1240 /* Reserve the memory */
1241 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1242 ReserveSize
= Heap
->SegmentReserve
;
1244 ReserveSize
= Size
+ PAGE_SIZE
;
1246 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1253 /* If it failed, retry again with a half division algorithm */
1254 while (!NT_SUCCESS(Status
) &&
1255 ReserveSize
!= Size
+ PAGE_SIZE
)
1259 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1260 ReserveSize
= Size
+ PAGE_SIZE
;
1262 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1270 /* Proceed only if it's success */
1271 if (NT_SUCCESS(Status
))
1273 Heap
->SegmentReserve
+= ReserveSize
;
1275 /* Now commit the memory */
1276 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1277 CommitSize
= Heap
->SegmentCommit
;
1279 CommitSize
= Size
+ PAGE_SIZE
;
1281 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1288 DPRINT("Committed %d bytes at base %p\n", CommitSize
, Segment
);
1290 /* Initialize heap segment if commit was successful */
1291 if (NT_SUCCESS(Status
))
1293 if (!RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, Segment
,
1294 (PCHAR
)Segment
+ CommitSize
, (PCHAR
)Segment
+ ReserveSize
))
1296 Status
= STATUS_NO_MEMORY
;
1300 /* If everything worked - cool */
1301 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1303 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1305 /* Nope, we failed. Free memory */
1306 ZwFreeVirtualMemory(NtCurrentProcess(),
1313 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1317 if (RtlpGetMode() == UserMode
)
1319 /* If coalescing on free is disabled in usermode, then do it here */
1320 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1322 FreeEntry
= RtlpCoalesceHeap(Heap
);
1324 /* If it's a suitable one - return it */
1326 FreeEntry
->Size
>= Size
)
1336 /***********************************************************************
1339 * Handle of heap: Success
1345 RtlCreateHeap(ULONG Flags
,
1350 PRTL_HEAP_PARAMETERS Parameters
)
1352 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1354 RTL_HEAP_PARAMETERS SafeParams
= {0};
1356 ULONG_PTR MaximumUserModeAddress
;
1357 SYSTEM_BASIC_INFORMATION SystemInformation
;
1358 MEMORY_BASIC_INFORMATION MemoryInfo
;
1359 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1360 ULONG HeapSegmentFlags
= 0;
1362 ULONG MaxBlockSize
, HeaderSize
;
1363 BOOLEAN AllocateLock
= FALSE
;
1365 /* Check for a special heap */
1366 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1368 Heap
= RtlpPageHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1369 if (Heap
) return Heap
;
1372 DPRINT1("Enabling page heap failed\n");
1375 /* Check validation flags */
1376 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1378 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1379 Flags
&= HEAP_CREATE_VALID_MASK
;
1382 /* TODO: Capture parameters, once we decide to use SEH */
1383 if (!Parameters
) Parameters
= &SafeParams
;
1385 /* Check global flags */
1386 if (NtGlobalFlags
& FLG_HEAP_DISABLE_COALESCING
)
1387 Flags
|= HEAP_DISABLE_COALESCE_ON_FREE
;
1389 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1390 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1392 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1393 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1395 if (RtlpGetMode() == UserMode
)
1397 /* Also check these flags if in usermode */
1398 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1399 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1401 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1402 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1404 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1405 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1408 Peb
= RtlGetCurrentPeb();
1410 /* Apply defaults for non-set parameters */
1411 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= Peb
->HeapSegmentCommit
;
1412 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= Peb
->HeapSegmentReserve
;
1413 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= Peb
->HeapDeCommitFreeBlockThreshold
;
1414 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= Peb
->HeapDeCommitTotalFreeThreshold
;
1418 /* Apply defaults for non-set parameters */
1420 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= MmHeapSegmentCommit
;
1421 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= MmHeapSegmentReserve
;
1422 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
1423 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
1427 // FIXME: Move to memory manager
1428 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= PAGE_SIZE
* 2;
1429 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= 1048576;
1430 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= PAGE_SIZE
;
1431 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= 65536;
1433 /* Get the max um address */
1434 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1436 sizeof(SystemInformation
),
1439 if (!NT_SUCCESS(Status
))
1441 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1445 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1447 /* Calculate max alloc size */
1448 if (!Parameters
->MaximumAllocationSize
)
1449 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1451 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1453 if (!Parameters
->VirtualMemoryThreshold
||
1454 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1456 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1459 /* Check reserve/commit sizes and set default values */
1462 CommitSize
= PAGE_SIZE
;
1464 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1466 TotalSize
= 64 * PAGE_SIZE
;
1470 /* Round up the commit size to be at least the page size */
1471 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1474 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1476 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1479 /* Call special heap */
1480 if (RtlpHeapIsSpecial(Flags
))
1481 return RtlDebugCreateHeap(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1483 /* Calculate header size */
1484 HeaderSize
= sizeof(HEAP
);
1485 if (!(Flags
& HEAP_NO_SERIALIZE
))
1489 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
1493 HeaderSize
+= sizeof(HEAP_LOCK
);
1494 AllocateLock
= TRUE
;
1499 /* Invalid parameters */
1503 /* See if we are already provided with an address for the heap */
1506 if (Parameters
->CommitRoutine
)
1508 /* There is a commit routine, so no problem here, check params */
1509 if ((Flags
& HEAP_GROWABLE
) ||
1510 !Parameters
->InitialCommit
||
1511 !Parameters
->InitialReserve
||
1512 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1518 /* Calculate committed and uncommitted addresses */
1519 CommittedAddress
= Addr
;
1520 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1521 TotalSize
= Parameters
->InitialReserve
;
1523 /* Zero the initial page ourselves */
1524 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1528 /* Commit routine is absent, so query how much memory caller reserved */
1529 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1531 MemoryBasicInformation
,
1536 if (!NT_SUCCESS(Status
))
1538 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1543 if (MemoryInfo
.BaseAddress
!= Addr
||
1544 MemoryInfo
.State
== MEM_FREE
)
1549 /* Validation checks passed, set committed/uncommitted addresses */
1550 CommittedAddress
= Addr
;
1552 /* Check if it's committed or not */
1553 if (MemoryInfo
.State
== MEM_COMMIT
)
1555 /* Zero it out because it's already committed */
1556 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1558 /* Calculate uncommitted address value */
1559 CommitSize
= MemoryInfo
.RegionSize
;
1560 TotalSize
= CommitSize
;
1561 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1563 /* Check if uncommitted address is reserved */
1564 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1566 MemoryBasicInformation
,
1571 if (NT_SUCCESS(Status
) &&
1572 MemoryInfo
.State
== MEM_RESERVE
)
1574 /* It is, so add it up to the reserve size */
1575 TotalSize
+= MemoryInfo
.RegionSize
;
1580 /* It's not committed, inform following code that a commit is necessary */
1581 CommitSize
= PAGE_SIZE
;
1582 UncommittedAddress
= Addr
;
1586 /* Mark this as a user-committed mem */
1587 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1592 /* Check commit routine */
1593 if (Parameters
->CommitRoutine
) return NULL
;
1595 /* Reserve memory */
1596 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1603 if (!NT_SUCCESS(Status
))
1605 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1609 /* Set base addresses */
1610 CommittedAddress
= Heap
;
1611 UncommittedAddress
= Heap
;
1614 /* Check if we need to commit something */
1615 if (CommittedAddress
== UncommittedAddress
)
1617 /* Commit the required size */
1618 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1625 DPRINT("Committed %d bytes at base %p\n", CommitSize
, CommittedAddress
);
1627 if (!NT_SUCCESS(Status
))
1629 DPRINT1("Failure, Status 0x%08X\n", Status
);
1631 /* Release memory if it was reserved */
1632 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1640 /* Calculate new uncommitted address */
1641 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1644 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1646 /* Initialize the heap */
1647 RtlpInitializeHeap(Heap
, &HeaderSize
, Flags
, AllocateLock
, Lock
);
1649 /* Initialize heap's first segment */
1650 if (!RtlpInitializeHeapSegment(Heap
,
1651 (PHEAP_SEGMENT
)((PCHAR
)Heap
+ HeaderSize
),
1656 (PCHAR
)CommittedAddress
+ TotalSize
))
1658 DPRINT1("Failed to initialize heap segment\n");
1662 /* Set other data */
1663 Heap
->ProcessHeapsListIndex
= 0;
1664 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
1665 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
1666 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
1667 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
1668 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
1669 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, HEAP_ENTRY_SIZE
) >> HEAP_ENTRY_SHIFT
;
1670 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
1673 if (Flags
& HEAP_CREATE_ALIGN_16
)
1675 Heap
->AlignMask
= (ULONG
)~15;
1676 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
1680 Heap
->AlignMask
= (ULONG
)~(HEAP_ENTRY_SIZE
- 1);
1681 Heap
->AlignRound
= HEAP_ENTRY_SIZE
- 1 + sizeof(HEAP_ENTRY
);
1684 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1685 Heap
->AlignRound
+= HEAP_ENTRY_SIZE
;
1687 /* Add heap to process list in case of usermode heap */
1688 if (RtlpGetMode() == UserMode
)
1690 RtlpAddHeapToProcessList(Heap
);
1692 // FIXME: What about lookasides?
1695 DPRINT("Heap %p, flags 0x%08x\n", Heap
, Heap
->Flags
);
1699 /***********************************************************************
1708 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1709 * Failure: The Heap handle, if heap is the process heap.
1712 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1714 PHEAP Heap
= (PHEAP
)HeapPtr
;
1715 PLIST_ENTRY Current
;
1716 PHEAP_UCR_SEGMENT UcrSegment
;
1717 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1721 PHEAP_SEGMENT Segment
;
1723 if (!HeapPtr
) return NULL
;
1725 /* Call special heap */
1726 if (RtlpHeapIsSpecial(Heap
->Flags
))
1728 if (!RtlDebugDestroyHeap(Heap
)) return HeapPtr
;
1731 /* Check for a process heap */
1732 if (RtlpGetMode() == UserMode
&&
1733 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1735 /* Free up all big allocations */
1736 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1737 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1739 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1740 BaseAddress
= (PVOID
)VirtualEntry
;
1741 Current
= Current
->Flink
;
1743 ZwFreeVirtualMemory(NtCurrentProcess(),
1749 /* Delete tags and remove heap from the process heaps list in user mode */
1750 if (RtlpGetMode() == UserMode
)
1752 // FIXME DestroyTags
1753 RtlpRemoveHeapFromProcessList(Heap
);
1756 /* Delete the heap lock */
1757 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1759 /* Delete it if it wasn't user allocated */
1760 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1761 RtlDeleteHeapLock(Heap
->LockVariable
);
1763 /* Clear out the lock variable */
1764 Heap
->LockVariable
= NULL
;
1767 /* Free UCR segments if any were created */
1768 Current
= Heap
->UCRSegments
.Flink
;
1769 while(Current
!= &Heap
->UCRSegments
)
1771 UcrSegment
= CONTAINING_RECORD(Current
, HEAP_UCR_SEGMENT
, ListEntry
);
1773 /* Advance to the next descriptor */
1774 Current
= Current
->Flink
;
1776 BaseAddress
= (PVOID
)UcrSegment
;
1779 /* Release that memory */
1780 ZwFreeVirtualMemory(NtCurrentProcess(),
1786 /* Go through segments and destroy them */
1787 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1789 Segment
= Heap
->Segments
[i
];
1790 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1797 RtlpSplitEntry(PHEAP Heap
,
1798 PHEAP_FREE_ENTRY FreeBlock
,
1799 SIZE_T AllocationSize
,
1803 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1805 PHEAP_ENTRY InUseEntry
;
1808 /* Save flags, update total free size */
1809 FreeFlags
= FreeBlock
->Flags
;
1810 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1812 /* Make this block an in-use one */
1813 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1814 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
;
1815 InUseEntry
->SmallTagIndex
= 0;
1817 /* Calculate the extra amount */
1818 FreeSize
= InUseEntry
->Size
- Index
;
1820 /* Update it's size fields (we don't need their data anymore) */
1821 InUseEntry
->Size
= Index
;
1822 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
1824 /* If there is something to split - do the split */
1827 /* Don't split if resulting entry can't contain any payload data
1828 (i.e. being just HEAP_ENTRY_SIZE) */
1831 /* Increase sizes of the in-use entry */
1833 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
1837 /* Calculate a pointer to the new entry */
1838 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
1841 SplitBlock
->Flags
= FreeFlags
;
1842 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
1843 SplitBlock
->Size
= FreeSize
;
1844 SplitBlock
->PreviousSize
= Index
;
1846 /* Check if it's the last entry */
1847 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1849 /* Insert it to the free list if it's the last entry */
1850 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1851 Heap
->TotalFreeSize
+= FreeSize
;
1855 /* Not so easy - need to update next's previous size too */
1856 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
1858 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
1860 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
1861 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1862 Heap
->TotalFreeSize
+= FreeSize
;
1866 /* Even more complex - the next entry is free, so we can merge them into one! */
1867 SplitBlock
->Flags
= SplitBlock2
->Flags
;
1869 /* Remove that next entry */
1870 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
1873 FreeSize
+= SplitBlock2
->Size
;
1874 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
1876 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1878 /* Insert it back */
1879 SplitBlock
->Size
= FreeSize
;
1881 /* Don't forget to update previous size of the next entry! */
1882 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1884 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
1887 /* Actually insert it */
1888 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, (USHORT
)FreeSize
, FALSE
);
1890 /* Update total size */
1891 Heap
->TotalFreeSize
+= FreeSize
;
1895 /* Resulting block is quite big */
1896 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
1901 /* Reset flags of the free entry */
1904 /* Update last entry in segment */
1905 if (SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1907 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
1912 /* Set last entry flag */
1913 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1914 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1920 RtlpAllocateNonDedicated(PHEAP Heap
,
1923 SIZE_T AllocationSize
,
1927 PLIST_ENTRY FreeListHead
, Next
;
1928 PHEAP_FREE_ENTRY FreeBlock
;
1929 PHEAP_ENTRY InUseEntry
;
1930 PHEAP_ENTRY_EXTRA Extra
;
1931 EXCEPTION_RECORD ExceptionRecord
;
1933 /* Go through the zero list to find a place where to insert the new entry */
1934 FreeListHead
= &Heap
->FreeLists
[0];
1936 /* Start from the largest block to reduce time */
1937 Next
= FreeListHead
->Blink
;
1938 if (FreeListHead
!= Next
)
1940 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1942 if (FreeBlock
->Size
>= Index
)
1944 /* Our request is smaller than the largest entry in the zero list */
1946 /* Go through the list to find insertion place */
1947 Next
= FreeListHead
->Flink
;
1948 while (FreeListHead
!= Next
)
1950 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1952 if (FreeBlock
->Size
>= Index
)
1954 /* Found minimally fitting entry. Proceed to either using it as it is
1955 or splitting it to two entries */
1956 RemoveEntryList(&FreeBlock
->FreeList
);
1959 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
1961 /* Release the lock */
1962 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1964 /* Zero memory if that was requested */
1965 if (Flags
& HEAP_ZERO_MEMORY
)
1966 RtlZeroMemory(InUseEntry
+ 1, Size
);
1967 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1969 /* Fill this block with a special pattern */
1970 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1973 /* Fill tail of the block with a special pattern too if requested */
1974 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1976 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1977 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1980 /* Prepare extra if it's present */
1981 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1983 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1984 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1989 /* Return pointer to the */
1990 return InUseEntry
+ 1;
1993 /* Advance to the next entry */
1999 /* Extend the heap, 0 list didn't have anything suitable */
2000 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
2002 /* Use the new biggest entry we've got */
2005 RemoveEntryList(&FreeBlock
->FreeList
);
2008 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2010 /* Release the lock */
2011 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2013 /* Zero memory if that was requested */
2014 if (Flags
& HEAP_ZERO_MEMORY
)
2015 RtlZeroMemory(InUseEntry
+ 1, Size
);
2016 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2018 /* Fill this block with a special pattern */
2019 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2022 /* Fill tail of the block with a special pattern too if requested */
2023 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2025 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2026 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2029 /* Prepare extra if it's present */
2030 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2032 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2033 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2038 /* Return pointer to the */
2039 return InUseEntry
+ 1;
2042 /* Really unfortunate, out of memory condition */
2043 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2045 /* Generate an exception */
2046 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2048 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2049 ExceptionRecord
.ExceptionRecord
= NULL
;
2050 ExceptionRecord
.NumberParameters
= 1;
2051 ExceptionRecord
.ExceptionFlags
= 0;
2052 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2054 RtlRaiseException(&ExceptionRecord
);
2057 /* Release the lock */
2058 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2059 DPRINT1("HEAP: Allocation failed!\n");
2060 DPRINT1("Flags %x\n", Heap
->Flags
);
2064 /***********************************************************************
2065 * HeapAlloc (KERNEL32.334)
2067 * Pointer to allocated memory block
2069 * 0x7d030f60--invalid flags in RtlHeapAllocate
2073 RtlAllocateHeap(IN PVOID HeapPtr
,
2077 PHEAP Heap
= (PHEAP
)HeapPtr
;
2078 PULONG FreeListsInUse
;
2079 ULONG FreeListsInUseUlong
;
2080 SIZE_T AllocationSize
;
2082 PLIST_ENTRY FreeListHead
;
2083 PHEAP_ENTRY InUseEntry
;
2084 PHEAP_FREE_ENTRY FreeBlock
;
2085 ULONG InUseIndex
, i
;
2087 EXCEPTION_RECORD ExceptionRecord
;
2088 BOOLEAN HeapLocked
= FALSE
;
2089 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
2090 PHEAP_ENTRY_EXTRA Extra
;
2094 Flags
|= Heap
->ForceFlags
;
2096 /* Call special heap */
2097 if (RtlpHeapIsSpecial(Flags
))
2098 return RtlDebugAllocateHeap(Heap
, Flags
, Size
);
2100 /* Check for the maximum size */
2101 if (Size
>= 0x80000000)
2103 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2104 DPRINT1("HEAP: Allocation failed!\n");
2108 if (Flags
& (HEAP_CREATE_ENABLE_TRACING
|
2109 HEAP_CREATE_ALIGN_16
))
2111 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
2114 //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
2116 /* Calculate allocation size and index */
2118 AllocationSize
= Size
;
2121 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2122 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2124 /* Acquire the lock if necessary */
2125 if (!(Flags
& HEAP_NO_SERIALIZE
))
2127 RtlEnterHeapLock(Heap
->LockVariable
);
2131 /* Depending on the size, the allocation is going to be done from dedicated,
2132 non-dedicated lists or a virtual block of memory */
2133 if (Index
< HEAP_FREELISTS
)
2135 FreeListHead
= &Heap
->FreeLists
[Index
];
2137 if (!IsListEmpty(FreeListHead
))
2139 /* There is a free entry in this list */
2140 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2144 /* Save flags and remove the free entry */
2145 FreeFlags
= FreeBlock
->Flags
;
2146 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2148 /* Update the total free size of the heap */
2149 Heap
->TotalFreeSize
-= Index
;
2151 /* Initialize this block */
2152 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2153 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2154 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2155 InUseEntry
->SmallTagIndex
= 0;
2159 /* Find smallest free block which this request could fit in */
2160 InUseIndex
= Index
>> 5;
2161 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2163 /* This bit magic disables all sizes which are less than the requested allocation size */
2164 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2166 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2168 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2170 /* Go through the list */
2171 for (i
= InUseIndex
; i
< 4; i
++)
2173 if (FreeListsInUseUlong
)
2175 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2179 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2182 /* Nothing found, search in the non-dedicated list */
2184 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2186 /* That list is found, now calculate exact block */
2187 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2189 /* Take this entry and remove it from the list of free blocks */
2190 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2193 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2196 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2199 /* Release the lock */
2200 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2202 /* Zero memory if that was requested */
2203 if (Flags
& HEAP_ZERO_MEMORY
)
2204 RtlZeroMemory(InUseEntry
+ 1, Size
);
2205 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2207 /* Fill this block with a special pattern */
2208 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2211 /* Fill tail of the block with a special pattern too if requested */
2212 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2214 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2215 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2218 /* Prepare extra if it's present */
2219 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2221 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2222 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2227 /* User data starts right after the entry's header */
2228 return InUseEntry
+ 1;
2230 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2232 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2233 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2235 else if (Heap
->Flags
& HEAP_GROWABLE
)
2237 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2238 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2240 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2241 (PVOID
*)&VirtualBlock
,
2247 if (!NT_SUCCESS(Status
))
2250 /* Release the lock */
2251 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2252 DPRINT1("HEAP: Allocation failed!\n");
2256 /* Initialize the newly allocated block */
2257 VirtualBlock
->BusyBlock
.Size
= (AllocationSize
- Size
);
2258 VirtualBlock
->BusyBlock
.Flags
= HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
| HEAP_ENTRY_BUSY
;
2259 VirtualBlock
->CommitSize
= AllocationSize
;
2260 VirtualBlock
->ReserveSize
= AllocationSize
;
2262 /* Insert it into the list of virtual allocations */
2263 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2265 /* Release the lock */
2266 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2268 /* Return pointer to user data */
2269 return VirtualBlock
+ 1;
2272 /* Generate an exception */
2273 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2275 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2276 ExceptionRecord
.ExceptionRecord
= NULL
;
2277 ExceptionRecord
.NumberParameters
= 1;
2278 ExceptionRecord
.ExceptionFlags
= 0;
2279 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2281 RtlRaiseException(&ExceptionRecord
);
2284 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL
);
2286 /* Release the lock */
2287 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2288 DPRINT1("HEAP: Allocation failed!\n");
2293 /***********************************************************************
2294 * HeapFree (KERNEL32.338)
2301 BOOLEAN NTAPI
RtlFreeHeap(
2302 HANDLE HeapPtr
, /* [in] Handle of heap */
2303 ULONG Flags
, /* [in] Heap freeing flags */
2304 PVOID Ptr
/* [in] Address of memory to free */
2308 PHEAP_ENTRY HeapEntry
;
2309 USHORT TagIndex
= 0;
2311 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2312 BOOLEAN Locked
= FALSE
;
2315 /* Freeing NULL pointer is a legal operation */
2316 if (!Ptr
) return TRUE
;
2318 /* Get pointer to the heap and force flags */
2319 Heap
= (PHEAP
)HeapPtr
;
2320 Flags
|= Heap
->ForceFlags
;
2322 /* Call special heap */
2323 if (RtlpHeapIsSpecial(Flags
))
2324 return RtlDebugFreeHeap(Heap
, Flags
, Ptr
);
2326 /* Lock if necessary */
2327 if (!(Flags
& HEAP_NO_SERIALIZE
))
2329 RtlEnterHeapLock(Heap
->LockVariable
);
2333 /* Get pointer to the heap entry */
2334 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2336 /* Check this entry, fail if it's invalid */
2337 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2338 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2339 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2341 /* This is an invalid block */
2342 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2343 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2345 /* Release the heap lock */
2346 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2351 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2353 /* Big allocation */
2354 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2356 /* Remove it from the list */
2357 RemoveEntryList(&VirtualEntry
->Entry
);
2362 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2363 (PVOID
*)&VirtualEntry
,
2367 if (!NT_SUCCESS(Status
))
2369 DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
2370 Status
, Heap
, Ptr
, VirtualEntry
);
2371 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status
);
2376 /* Normal allocation */
2377 BlockSize
= HeapEntry
->Size
;
2381 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2382 if (RtlpGetMode() == KernelMode
||
2383 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2385 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2386 (PHEAP_FREE_ENTRY
)HeapEntry
,
2391 /* If there is no need to decommit the block - put it into a free list */
2392 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2393 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2395 /* Check if it needs to go to a 0 list */
2396 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2398 /* General-purpose 0 list */
2399 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2403 /* Usual free list */
2404 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2406 /* Assert sizes are consistent */
2407 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2409 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2412 /* Increase the free size */
2413 Heap
->TotalFreeSize
+= BlockSize
;
2417 if (RtlpGetMode() == UserMode
&&
2426 /* Decommit this block */
2427 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2431 /* Release the heap lock */
2432 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2438 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2440 IN PHEAP_ENTRY InUseEntry
,
2444 UCHAR EntryFlags
, RememberFlags
;
2445 PHEAP_FREE_ENTRY FreeEntry
, UnusedEntry
, FollowingEntry
;
2446 SIZE_T FreeSize
, PrevSize
, TailPart
, AddedSize
= 0;
2447 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2449 /* We can't grow beyond specified threshold */
2450 if (Index
> Heap
->VirtualMemoryThreshold
)
2453 /* Get entry flags */
2454 EntryFlags
= InUseEntry
->Flags
;
2456 /* Get the next free entry */
2457 FreeEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ InUseEntry
->Size
);
2459 if (EntryFlags
& HEAP_ENTRY_LAST_ENTRY
)
2461 /* There is no next block, just uncommitted space. Calculate how much is needed */
2462 FreeSize
= (Index
- InUseEntry
->Size
) << HEAP_ENTRY_SHIFT
;
2463 FreeSize
= ROUND_UP(FreeSize
, PAGE_SIZE
);
2465 /* Find and commit those pages */
2466 FreeEntry
= RtlpFindAndCommitPages(Heap
,
2467 Heap
->Segments
[InUseEntry
->SegmentOffset
],
2471 /* Fail if it failed... */
2472 if (!FreeEntry
) return FALSE
;
2474 /* It was successful, perform coalescing */
2475 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
2476 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
2478 /* Check if it's enough */
2479 if (FreeSize
+ InUseEntry
->Size
< Index
)
2481 /* Still not enough */
2482 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
2483 Heap
->TotalFreeSize
+= FreeSize
;
2487 /* Remember flags of this free entry */
2488 RememberFlags
= FreeEntry
->Flags
;
2491 FreeSize
+= InUseEntry
->Size
;
2495 /* The next block indeed exists. Check if it's free or in use */
2496 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
) return FALSE
;
2498 /* Next entry is free, check if it can fit the block we need */
2499 FreeSize
= InUseEntry
->Size
+ FreeEntry
->Size
;
2500 if (FreeSize
< Index
) return FALSE
;
2502 /* Remember flags of this free entry */
2503 RememberFlags
= FreeEntry
->Flags
;
2505 /* Remove this block from the free list */
2506 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
2507 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
2510 PrevSize
= (InUseEntry
->Size
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2513 /* Don't produce too small blocks */
2520 /* Process extra stuff */
2521 if (RememberFlags
& HEAP_ENTRY_EXTRA_PRESENT
)
2523 /* Calculate pointers */
2524 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2525 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2528 *NewExtra
= *OldExtra
;
2534 InUseEntry
->Size
= Index
;
2535 InUseEntry
->UnusedBytes
= ((Index
<< HEAP_ENTRY_SHIFT
) - Size
);
2537 /* Check if there is a free space remaining after merging those blocks */
2540 /* Update flags and sizes */
2541 InUseEntry
->Flags
|= RememberFlags
& HEAP_ENTRY_LAST_ENTRY
;
2543 /* Either update previous size of the next entry or mark it as a last
2544 entry in the segment*/
2545 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2546 Heap
->Segments
[InUseEntry
->SegmentOffset
]->LastEntryInSegment
= InUseEntry
;
2548 (InUseEntry
+ InUseEntry
->Size
)->PreviousSize
= InUseEntry
->Size
;
2552 /* Complex case, we need to split the block to give unused free space
2554 UnusedEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2555 UnusedEntry
->PreviousSize
= Index
;
2556 UnusedEntry
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2558 /* Update the following block or set the last entry in the segment */
2559 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2561 /* Set last entry and set flags and size */
2562 Heap
->Segments
[InUseEntry
->SegmentOffset
]->LastEntryInSegment
= InUseEntry
;
2563 UnusedEntry
->Flags
= RememberFlags
;
2564 UnusedEntry
->Size
= FreeSize
;
2566 /* Insert it to the heap and update total size */
2567 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2568 Heap
->TotalFreeSize
+= FreeSize
;
2572 /* There is a block after this one */
2573 FollowingEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
);
2575 if (FollowingEntry
->Flags
& HEAP_ENTRY_BUSY
)
2577 /* Update flags and set size of the unused space entry */
2578 UnusedEntry
->Flags
= RememberFlags
& (~HEAP_ENTRY_LAST_ENTRY
);
2579 UnusedEntry
->Size
= FreeSize
;
2581 /* Update previous size of the following entry */
2582 FollowingEntry
->PreviousSize
= FreeSize
;
2584 /* Insert it to the heap and update total free size */
2585 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2586 Heap
->TotalFreeSize
+= FreeSize
;
2590 /* That following entry is also free, what a fortune! */
2591 RememberFlags
= FollowingEntry
->Flags
;
2594 RtlpRemoveFreeBlock(Heap
, FollowingEntry
, FALSE
, FALSE
);
2595 Heap
->TotalFreeSize
-= FollowingEntry
->Size
;
2597 /* And make up a new combined block */
2598 FreeSize
+= FollowingEntry
->Size
;
2599 UnusedEntry
->Flags
= RememberFlags
;
2601 /* Check where to put it */
2602 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2604 /* Fine for a dedicated list */
2605 UnusedEntry
->Size
= FreeSize
;
2607 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2608 Heap
->Segments
[UnusedEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)UnusedEntry
;
2610 ((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
)->PreviousSize
= FreeSize
;
2612 /* Insert it back and update total size */
2613 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2614 Heap
->TotalFreeSize
+= FreeSize
;
2618 /* The block is very large, leave all the hassle to the insertion routine */
2619 RtlpInsertFreeBlock(Heap
, UnusedEntry
, FreeSize
);
2625 /* Copy user settable flags */
2626 InUseEntry
->Flags
&= ~HEAP_ENTRY_SETTABLE_FLAGS
;
2627 InUseEntry
->Flags
|= ((Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4);
2629 /* Properly "zero out" (and fill!) the space */
2630 if (Flags
& HEAP_ZERO_MEMORY
)
2632 RtlZeroMemory((PCHAR
)(InUseEntry
+ 1) + PrevSize
, Size
- PrevSize
);
2634 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2636 /* Calculate tail part which we need to fill */
2637 TailPart
= PrevSize
& (sizeof(ULONG
) - 1);
2639 /* "Invert" it as usual */
2640 if (TailPart
) TailPart
= 4 - TailPart
;
2642 if (Size
> (PrevSize
+ TailPart
))
2643 AddedSize
= (Size
- (PrevSize
+ TailPart
)) & ~(sizeof(ULONG
) - 1);
2647 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + PrevSize
+ TailPart
,
2649 ARENA_INUSE_FILLER
);
2653 /* Fill the new tail */
2654 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2656 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2661 /* Return success */
2665 PHEAP_ENTRY_EXTRA NTAPI
2666 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2668 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2670 /* Check if it's a big block */
2671 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2673 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2675 /* Return a pointer to the extra stuff*/
2676 return &VirtualEntry
->ExtraStuff
;
2680 /* This is a usual entry, which means extra stuff follows this block */
2681 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2686 /***********************************************************************
2689 * Heap [in] Handle of heap block
2690 * Flags [in] Heap reallocation flags
2691 * Ptr, [in] Address of memory to reallocate
2692 * Size [in] Number of bytes to reallocate
2695 * Pointer to reallocated memory block
2697 * 0x7d030f60--invalid flags in RtlHeapAllocate
2701 RtlReAllocateHeap(HANDLE HeapPtr
,
2706 PHEAP Heap
= (PHEAP
)HeapPtr
;
2707 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2708 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2709 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2710 BOOLEAN HeapLocked
= FALSE
;
2711 PVOID NewBaseAddress
;
2712 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2713 SIZE_T OldSize
, Index
, OldIndex
;
2717 SIZE_T RemainderBytes
, ExtraSize
;
2718 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2719 EXCEPTION_RECORD ExceptionRecord
;
2721 /* Return success in case of a null pointer */
2724 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS
);
2728 /* Force heap flags */
2729 Flags
|= Heap
->ForceFlags
;
2731 /* Call special heap */
2732 if (RtlpHeapIsSpecial(Flags
))
2733 return RtlDebugReAllocateHeap(Heap
, Flags
, Ptr
, Size
);
2735 /* Make sure size is valid */
2736 if (Size
>= 0x80000000)
2738 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2742 /* Calculate allocation size and index */
2744 AllocationSize
= Size
;
2747 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2749 /* Add up extra stuff, if it is present anywhere */
2750 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2751 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2752 Heap
->PseudoTagEntries
)
2754 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2757 /* Acquire the lock if necessary */
2758 if (!(Flags
& HEAP_NO_SERIALIZE
))
2760 RtlEnterHeapLock(Heap
->LockVariable
);
2762 Flags
^= HEAP_NO_SERIALIZE
;
2765 /* Get the pointer to the in-use entry */
2766 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2768 /* If that entry is not really in-use, we have a problem */
2769 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2771 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2773 /* Release the lock and return */
2775 RtlLeaveHeapLock(Heap
->LockVariable
);
2779 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2781 /* This is a virtually allocated block. Get its size */
2782 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2784 /* Convert it to an index */
2785 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2787 /* Calculate new allocation size and round it to the page size */
2788 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2789 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2794 OldIndex
= InUseEntry
->Size
;
2796 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2799 /* Calculate new index */
2800 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2802 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2803 if (Index
<= OldIndex
)
2805 /* Difference must be greater than 1, adjust if it's not so */
2806 if (Index
+ 1 == OldIndex
)
2809 AllocationSize
+= sizeof(HEAP_ENTRY
);
2812 /* Calculate new size */
2813 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2815 /* Simple in case of a virtual alloc - just an unused size */
2816 InUseEntry
->Size
= AllocationSize
- Size
;
2818 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2820 /* There is extra stuff, take it into account */
2821 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2822 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2823 *NewExtra
= *OldExtra
;
2825 // FIXME Tagging, TagIndex
2827 /* Update unused bytes count */
2828 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2832 // FIXME Tagging, SmallTagIndex
2833 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2836 /* If new size is bigger than the old size */
2839 /* Zero out that additional space if required */
2840 if (Flags
& HEAP_ZERO_MEMORY
)
2842 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2844 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2846 /* Fill it on free if required */
2847 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2850 RemainderBytes
= 4 - RemainderBytes
;
2852 if (Size
> (OldSize
+ RemainderBytes
))
2854 /* Calculate actual amount of extra bytes to fill */
2855 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2857 /* Fill them if there are any */
2860 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2862 ARENA_INUSE_FILLER
);
2868 /* Fill tail of the heap entry if required */
2869 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2871 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2876 /* Check if the difference is significant or not */
2877 if (Index
!= OldIndex
)
2880 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2882 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2884 /* This is a virtual block allocation */
2885 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2889 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2890 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2892 /* Release the memory */
2893 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2894 (PVOID
*)&DecommitBase
,
2898 if (!NT_SUCCESS(Status
))
2900 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2904 /* Otherwise reduce the commit size */
2905 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2910 /* Reduce size of the block and possibly split it */
2911 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2913 /* Initialize this entry */
2914 SplitBlock
->Flags
= FreeFlags
;
2915 SplitBlock
->PreviousSize
= Index
;
2916 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2918 /* Remember free size */
2919 FreeSize
= InUseEntry
->Size
- Index
;
2922 InUseEntry
->Size
= Index
;
2923 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2925 /* Is that the last entry */
2926 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2928 /* Update segment's last entry */
2929 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2931 /* Set its size and insert it to the list */
2932 SplitBlock
->Size
= (USHORT
)FreeSize
;
2933 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2935 /* Update total free size */
2936 Heap
->TotalFreeSize
+= FreeSize
;
2940 /* Get the block after that one */
2941 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2943 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2945 /* It's in use, add it here*/
2946 SplitBlock
->Size
= (USHORT
)FreeSize
;
2948 /* Update previous size of the next entry */
2949 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2951 /* Insert it to the list */
2952 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2954 /* Update total size */
2955 Heap
->TotalFreeSize
+= FreeSize
;
2959 /* Next entry is free, so merge with it */
2960 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2962 /* Remove it, update total size */
2963 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2964 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2966 /* Calculate total free size */
2967 FreeSize
+= SplitBlock2
->Size
;
2969 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2971 SplitBlock
->Size
= FreeSize
;
2973 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2975 /* Update previous size of the next entry */
2976 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2980 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2983 /* Insert the new one back and update total size */
2984 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2985 Heap
->TotalFreeSize
+= FreeSize
;
2990 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2999 /* We're growing the block */
3000 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
3001 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
3003 /* Growing in place failed, so growing out of place */
3004 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
3006 DPRINT1("Realloc in place failed, but it was the only option\n");
3011 /* Clear tag bits */
3012 Flags
&= ~HEAP_TAG_MASK
;
3014 /* Process extra stuff */
3015 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3017 /* Preserve user settable flags */
3018 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
3020 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
3022 /* Get pointer to the old extra data */
3023 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
3025 /* Save tag index if it was set */
3026 if (OldExtra
->TagIndex
&&
3027 !(OldExtra
->TagIndex
& HEAP_PSEUDO_TAG_FLAG
))
3029 Flags
|= OldExtra
->TagIndex
<< HEAP_TAG_SHIFT
;
3032 else if (InUseEntry
->SmallTagIndex
)
3034 /* Take small tag index into account */
3035 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
3038 /* Allocate new block from the heap */
3039 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
3040 Flags
& ~HEAP_ZERO_MEMORY
,
3043 /* Proceed if it didn't fail */
3046 /* Get new entry pointer */
3047 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
3049 /* Process extra stuff if it exists */
3050 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3052 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
3054 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3056 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
3057 NewExtra
->Settable
= OldExtra
->Settable
;
3061 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
3065 /* Copy actual user bits */
3067 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
3069 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
3071 /* Zero remaining part if required */
3072 if (Size
> OldSize
&&
3073 (Flags
& HEAP_ZERO_MEMORY
))
3075 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
3078 /* Free the old block */
3079 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
3082 Ptr
= NewBaseAddress
;
3087 /* Did resizing fail? */
3088 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
3090 /* Generate an exception if required */
3091 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
3092 ExceptionRecord
.ExceptionRecord
= NULL
;
3093 ExceptionRecord
.NumberParameters
= 1;
3094 ExceptionRecord
.ExceptionFlags
= 0;
3095 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
3097 RtlRaiseException(&ExceptionRecord
);
3100 /* Release the heap lock if it was acquired */
3102 RtlLeaveHeapLock(Heap
->LockVariable
);
3108 /***********************************************************************
3114 RtlCompactHeap(HANDLE Heap
,
3122 /***********************************************************************
3124 * Attempts to acquire the critical section object for a specified heap.
3127 * Heap [in] Handle of heap to lock for exclusive access
3136 RtlLockHeap(IN HANDLE HeapPtr
)
3138 PHEAP Heap
= (PHEAP
)HeapPtr
;
3140 // FIXME Check for special heap
3142 /* Check if it's really a heap */
3143 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3145 /* Lock if it's lockable */
3146 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3148 RtlEnterHeapLock(Heap
->LockVariable
);
3155 /***********************************************************************
3157 * Releases ownership of the critical section object.
3160 * Heap [in] Handle to the heap to unlock
3169 RtlUnlockHeap(HANDLE HeapPtr
)
3171 PHEAP Heap
= (PHEAP
)HeapPtr
;
3173 // FIXME Check for special heap
3175 /* Check if it's really a heap */
3176 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3178 /* Unlock if it's lockable */
3179 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3181 RtlLeaveHeapLock(Heap
->LockVariable
);
3188 /***********************************************************************
3191 * Heap [in] Handle of heap
3192 * Flags [in] Heap size control flags
3193 * Ptr [in] Address of memory to return size for
3196 * Size in bytes of allocated memory
3197 * 0xffffffff: Failure
3208 PHEAP Heap
= (PHEAP
)HeapPtr
;
3209 PHEAP_ENTRY HeapEntry
;
3212 // FIXME This is a hack around missing SEH support!
3215 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3220 Flags
|= Heap
->ForceFlags
;
3222 /* Call special heap */
3223 if (RtlpHeapIsSpecial(Flags
))
3224 return RtlDebugSizeHeap(Heap
, Flags
, Ptr
);
3226 /* Get the heap entry pointer */
3227 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3229 /* Return -1 if that entry is free */
3230 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3232 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3236 /* Get size of this block depending if it's a usual or a big one */
3237 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3239 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3244 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3247 /* Return calculated size */
3252 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry
)
3254 SIZE_T Size
, Result
;
3257 /* Calculate size */
3258 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3259 Size
= RtlpGetSizeOfBigBlock(HeapEntry
);
3261 Size
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3263 /* Calculate pointer to the tail part of the block */
3264 TailPart
= (PCHAR
)(HeapEntry
+ 1) + Size
;
3266 /* Compare tail pattern */
3267 Result
= RtlCompareMemory(TailPart
,
3271 if (Result
!= HEAP_ENTRY_SIZE
)
3273 DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size
, HeapEntry
, TailPart
+ Result
);
3282 RtlpValidateHeapHeaders(
3284 BOOLEAN Recalculate
)
3286 // We skip header validation for now
3291 RtlpValidateHeapEntry(
3293 PHEAP_ENTRY HeapEntry
)
3295 BOOLEAN BigAllocation
, EntryFound
= FALSE
;
3296 PHEAP_SEGMENT Segment
;
3297 ULONG SegmentOffset
;
3299 /* Perform various consistency checks of this entry */
3300 if (!HeapEntry
) goto invalid_entry
;
3301 if ((ULONG_PTR
)HeapEntry
& (HEAP_ENTRY_SIZE
- 1)) goto invalid_entry
;
3302 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
)) goto invalid_entry
;
3304 BigAllocation
= HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
;
3305 Segment
= Heap
->Segments
[HeapEntry
->SegmentOffset
];
3307 if (BigAllocation
&&
3308 (((ULONG_PTR
)HeapEntry
& (PAGE_SIZE
- 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
)))
3311 if (!BigAllocation
&& (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
||
3313 HeapEntry
< Segment
->FirstEntry
||
3314 HeapEntry
>= Segment
->LastValidEntry
))
3317 if ((HeapEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
) &&
3318 !RtlpCheckInUsePattern(HeapEntry
))
3321 /* Checks are done, if this is a virtual entry, that's all */
3322 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) return TRUE
;
3324 /* Go through segments and check if this entry fits into any of them */
3325 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3327 Segment
= Heap
->Segments
[SegmentOffset
];
3328 if (!Segment
) continue;
3330 if ((HeapEntry
>= Segment
->FirstEntry
) &&
3331 (HeapEntry
< Segment
->LastValidEntry
))
3339 /* Return our result of finding entry in the segments */
3343 DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry
, Heap
);
3348 RtlpValidateHeapSegment(
3350 PHEAP_SEGMENT Segment
,
3351 UCHAR SegmentOffset
,
3352 PULONG FreeEntriesCount
,
3353 PSIZE_T TotalFreeSize
,
3355 PSIZE_T PseudoTagEntries
)
3357 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
3358 PLIST_ENTRY UcrEntry
;
3359 SIZE_T ByteSize
, Size
, Result
;
3360 PHEAP_ENTRY CurrentEntry
;
3361 ULONG UnCommittedPages
;
3362 ULONG UnCommittedRanges
;
3365 UnCommittedPages
= 0;
3366 UnCommittedRanges
= 0;
3368 if (IsListEmpty(&Segment
->UCRSegmentList
))
3371 UcrDescriptor
= NULL
;
3375 UcrEntry
= Segment
->UCRSegmentList
.Flink
;
3376 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3379 if (Segment
->BaseAddress
== Heap
)
3380 CurrentEntry
= &Heap
->Entry
;
3382 CurrentEntry
= &Segment
->Entry
;
3384 while (CurrentEntry
< Segment
->LastValidEntry
)
3386 if (UcrDescriptor
&&
3387 ((PVOID
)CurrentEntry
>= UcrDescriptor
->Address
))
3389 DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
3390 CurrentEntry
, UcrDescriptor
->Address
,
3391 (PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3398 while (CurrentEntry
< Segment
->LastValidEntry
)
3400 if (PreviousSize
!= CurrentEntry
->PreviousSize
)
3402 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
3403 CurrentEntry
, CurrentEntry
->PreviousSize
, PreviousSize
);
3408 PreviousSize
= CurrentEntry
->Size
;
3409 Size
= CurrentEntry
->Size
<< HEAP_ENTRY_SHIFT
;
3411 if (CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
)
3418 /* Check fill pattern */
3419 if (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
)
3421 if (!RtlpCheckInUsePattern(CurrentEntry
))
3427 /* The entry is free, increase free entries count and total free size */
3428 *FreeEntriesCount
= *FreeEntriesCount
+ 1;
3429 *TotalFreeSize
+= CurrentEntry
->Size
;
3431 if ((Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
) &&
3432 (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
3434 ByteSize
= Size
- sizeof(HEAP_FREE_ENTRY
);
3436 if ((CurrentEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) &&
3437 (ByteSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
)))
3439 ByteSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
3442 Result
= RtlCompareMemoryUlong((PCHAR
)((PHEAP_FREE_ENTRY
)CurrentEntry
+ 1),
3446 if (Result
!= ByteSize
)
3448 DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
3450 (PCHAR
)(CurrentEntry
+ 1) + Result
);
3457 if (CurrentEntry
->SegmentOffset
!= SegmentOffset
)
3459 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry
, SegmentOffset
, CurrentEntry
->SegmentOffset
);
3463 /* Check if it's the last entry */
3464 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
3466 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3470 /* Check if it's not really the last one */
3471 if (CurrentEntry
!= Segment
->LastValidEntry
)
3473 DPRINT1("HEAP: Heap entry %p is not last block in segment (%x)\n", CurrentEntry
, Segment
->LastValidEntry
);
3477 else if (CurrentEntry
!= UcrDescriptor
->Address
)
3479 DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
3480 CurrentEntry
, UcrDescriptor
->Address
);
3486 UnCommittedPages
+= (UcrDescriptor
->Size
/ PAGE_SIZE
);
3487 UnCommittedRanges
++;
3489 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3491 /* Go to the next UCR descriptor */
3492 UcrEntry
= UcrEntry
->Flink
;
3493 if (UcrEntry
== &Segment
->UCRSegmentList
)
3496 UcrDescriptor
= NULL
;
3500 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3507 /* Advance to the next entry */
3508 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3512 /* Check total numbers of UCP and UCR */
3513 if (Segment
->NumberOfUnCommittedPages
!= UnCommittedPages
)
3515 DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
3516 Segment
, Segment
->NumberOfUnCommittedPages
, UnCommittedPages
);
3521 if (Segment
->NumberOfUnCommittedRanges
!= UnCommittedRanges
)
3523 DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
3524 Segment
, Segment
->NumberOfUnCommittedRanges
, UnCommittedRanges
);
3533 RtlpValidateHeap(PHEAP Heap
,
3534 BOOLEAN ForceValidation
)
3536 PHEAP_SEGMENT Segment
;
3538 UCHAR SegmentOffset
;
3539 SIZE_T Size
, TotalFreeSize
;
3541 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
3542 PLIST_ENTRY ListHead
, NextEntry
;
3543 PHEAP_FREE_ENTRY FreeEntry
;
3544 ULONG FreeBlocksCount
, FreeListEntriesCount
;
3547 if (!RtlpValidateHeapHeaders(Heap
, FALSE
))
3550 /* Skip validation if it's not needed */
3551 if (!ForceValidation
&& !(Heap
->Flags
& HEAP_VALIDATE_ALL_ENABLED
))
3554 /* Check free lists bitmaps */
3555 FreeListEntriesCount
= 0;
3556 ListHead
= &Heap
->FreeLists
[0];
3558 for (Size
= 0; Size
< HEAP_FREELISTS
; Size
++)
3562 /* This is a dedicated list. Check if it's empty */
3563 EmptyList
= IsListEmpty(ListHead
);
3565 if (Heap
->u
.FreeListsInUseBytes
[Size
>> 3] & (1 << (Size
& 7)))
3569 DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size
);
3577 DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size
);
3583 /* Now check this list entries */
3584 NextEntry
= ListHead
->Flink
;
3587 while (ListHead
!= NextEntry
)
3589 FreeEntry
= CONTAINING_RECORD(NextEntry
, HEAP_FREE_ENTRY
, FreeList
);
3590 NextEntry
= NextEntry
->Flink
;
3592 /* If there is an in-use entry in a free list - that's quite a big problem */
3593 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
)
3595 DPRINT1("HEAP: %x-dedicated list free element %x is marked in-use\n", Size
, FreeEntry
);
3599 /* Check sizes according to that specific list's size */
3600 if ((Size
== 0) && (FreeEntry
->Size
< HEAP_FREELISTS
))
3602 DPRINT1("HEAP: Non dedicated list free element %x has size %x which would fit a dedicated list\n", FreeEntry
, FreeEntry
->Size
);
3605 else if (Size
&& (FreeEntry
->Size
!= Size
))
3607 DPRINT1("HEAP: %x-dedicated list free element %x has incorrect size %x\n", Size
, FreeEntry
, FreeEntry
->Size
);
3610 else if ((Size
== 0) && (FreeEntry
->Size
< PreviousSize
))
3612 DPRINT1("HEAP: Non dedicated list free element %x is not put in order\n", FreeEntry
);
3616 /* Remember previous size*/
3617 PreviousSize
= FreeEntry
->Size
;
3619 /* Add up to the total amount of free entries */
3620 FreeListEntriesCount
++;
3623 /* Go to the head of the next free list */
3627 /* Check big allocations */
3628 ListHead
= &Heap
->VirtualAllocdBlocks
;
3629 NextEntry
= ListHead
->Flink
;
3631 while (ListHead
!= NextEntry
)
3633 VirtualAllocBlock
= CONTAINING_RECORD(NextEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
3635 /* We can only check the fill pattern */
3636 if (VirtualAllocBlock
->BusyBlock
.Flags
& HEAP_ENTRY_FILL_PATTERN
)
3638 if (!RtlpCheckInUsePattern(&VirtualAllocBlock
->BusyBlock
))
3642 NextEntry
= NextEntry
->Flink
;
3645 /* Check all segments */
3646 FreeBlocksCount
= 0;
3649 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3651 Segment
= Heap
->Segments
[SegmentOffset
];
3653 /* Go to the next one if there is no segment */
3654 if (!Segment
) continue;
3656 if (!RtlpValidateHeapSegment(Heap
,
3668 if (FreeListEntriesCount
!= FreeBlocksCount
)
3670 DPRINT1("HEAP: Free blocks count in arena (%d) does not match free blocks number in the free lists (%d)\n", FreeBlocksCount
, FreeListEntriesCount
);
3674 if (Heap
->TotalFreeSize
!= TotalFreeSize
)
3676 DPRINT1("HEAP: Total size of free blocks in arena (%d) does not equal to the one in heap header (%d)\n", TotalFreeSize
, Heap
->TotalFreeSize
);
3683 /***********************************************************************
3685 * Validates a specified heap.
3688 * Heap [in] Handle to the heap
3689 * Flags [in] Bit flags that control access during operation
3690 * Block [in] Optional pointer to memory block to validate
3701 BOOLEAN NTAPI
RtlValidateHeap(
3707 PHEAP Heap
= (PHEAP
)HeapPtr
;
3708 BOOLEAN HeapLocked
= FALSE
;
3711 // FIXME Check for special heap
3713 /* Check signature */
3714 if (Heap
->Signature
!= HEAP_SIGNATURE
)
3716 DPRINT1("HEAP: Signature %x is invalid for heap %p\n", Heap
->Signature
, Heap
);
3721 Flags
= Heap
->ForceFlags
;
3723 /* Acquire the lock if necessary */
3724 if (!(Flags
& HEAP_NO_SERIALIZE
))
3726 RtlEnterHeapLock(Heap
->LockVariable
);
3730 /* Either validate whole heap or just one entry */
3732 HeapValid
= RtlpValidateHeap(Heap
, TRUE
);
3734 HeapValid
= RtlpValidateHeapEntry(Heap
, (PHEAP_ENTRY
)Block
- 1);
3736 /* Unlock if it's lockable */
3739 RtlLeaveHeapLock(Heap
->LockVariable
);
3746 RtlInitializeHeapManager(VOID
)
3751 Peb
= RtlGetCurrentPeb();
3753 /* Initialize heap-related fields of PEB */
3754 Peb
->NumberOfHeaps
= 0;
3756 /* Initialize the process heaps list protecting lock */
3757 RtlInitializeHeapLock(&RtlpProcessHeapsListLock
);
3765 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3769 return STATUS_NOT_IMPLEMENTED
;
3777 RtlGetProcessHeaps(ULONG count
,
3789 RtlValidateProcessHeaps(VOID
)
3801 IN PVOID HeapHandle
,
3814 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3816 IN PVOID BaseAddress
,
3819 PHEAP Heap
= (PHEAP
)HeapHandle
;
3820 PHEAP_ENTRY HeapEntry
;
3821 PHEAP_ENTRY_EXTRA Extra
;
3822 BOOLEAN HeapLocked
= FALSE
;
3825 Flags
|= Heap
->Flags
;
3827 /* Call special heap */
3828 if (RtlpHeapIsSpecial(Flags
))
3829 return RtlDebugSetUserValueHeap(Heap
, Flags
, BaseAddress
, UserValue
);
3831 /* Lock if it's lockable */
3832 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3834 RtlEnterHeapLock(Heap
->LockVariable
);
3838 /* Get a pointer to the entry */
3839 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3841 /* If it's a free entry - return error */
3842 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3844 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3846 /* Release the heap lock if it was acquired */
3848 RtlLeaveHeapLock(Heap
->LockVariable
);
3853 /* Check if this entry has an extra stuff associated with it */
3854 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3856 /* Use extra to store the value */
3857 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3858 Extra
->Settable
= (ULONG_PTR
)UserValue
;
3861 /* Release the heap lock if it was acquired */
3863 RtlLeaveHeapLock(Heap
->LockVariable
);
3873 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3875 IN PVOID BaseAddress
,
3876 IN ULONG UserFlagsReset
,
3877 IN ULONG UserFlagsSet
)
3879 PHEAP Heap
= (PHEAP
)HeapHandle
;
3880 PHEAP_ENTRY HeapEntry
;
3881 BOOLEAN HeapLocked
= FALSE
;
3884 Flags
|= Heap
->Flags
;
3886 /* Call special heap */
3887 if (RtlpHeapIsSpecial(Flags
))
3888 return RtlDebugSetUserFlagsHeap(Heap
, Flags
, BaseAddress
, UserFlagsReset
, UserFlagsSet
);
3890 /* Lock if it's lockable */
3891 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3893 RtlEnterHeapLock(Heap
->LockVariable
);
3897 /* Get a pointer to the entry */
3898 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3900 /* If it's a free entry - return error */
3901 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3903 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3905 /* Release the heap lock if it was acquired */
3907 RtlLeaveHeapLock(Heap
->LockVariable
);
3912 /* Set / reset flags */
3913 HeapEntry
->Flags
&= ~(UserFlagsReset
>> 4);
3914 HeapEntry
->Flags
|= (UserFlagsSet
>> 4);
3916 /* Release the heap lock if it was acquired */
3918 RtlLeaveHeapLock(Heap
->LockVariable
);
3928 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3930 IN PVOID BaseAddress
,
3931 OUT PVOID
*UserValue
,
3932 OUT PULONG UserFlags
)
3934 PHEAP Heap
= (PHEAP
)HeapHandle
;
3935 PHEAP_ENTRY HeapEntry
;
3936 PHEAP_ENTRY_EXTRA Extra
;
3937 BOOLEAN HeapLocked
= FALSE
;
3940 Flags
|= Heap
->Flags
;
3942 /* Call special heap */
3943 if (RtlpHeapIsSpecial(Flags
))
3944 return RtlDebugGetUserInfoHeap(Heap
, Flags
, BaseAddress
, UserValue
, UserFlags
);
3946 /* Lock if it's lockable */
3947 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3949 RtlEnterHeapLock(Heap
->LockVariable
);
3953 /* Get a pointer to the entry */
3954 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3956 /* If it's a free entry - return error */
3957 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3959 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3961 /* Release the heap lock if it was acquired */
3963 RtlLeaveHeapLock(Heap
->LockVariable
);
3968 /* Check if this entry has an extra stuff associated with it */
3969 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3971 /* Get pointer to extra data */
3972 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3974 /* Pass user value */
3976 *UserValue
= (PVOID
)Extra
->Settable
;
3978 /* Decode and return user flags */
3980 *UserFlags
= (HeapEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4;
3983 /* Release the heap lock if it was acquired */
3985 RtlLeaveHeapLock(Heap
->LockVariable
);
3995 RtlUsageHeap(IN HANDLE Heap
,
3997 OUT PRTL_HEAP_USAGE Usage
)
4001 return STATUS_NOT_IMPLEMENTED
;
4006 RtlQueryTagHeap(IN PVOID HeapHandle
,
4009 IN BOOLEAN ResetCounters
,
4010 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
4019 RtlExtendHeap(IN HANDLE Heap
,
4031 RtlCreateTagHeap(IN HANDLE HeapHandle
,
4034 IN PWSTR TagSubName
)
4043 RtlWalkHeap(IN HANDLE HeapHandle
,
4047 return STATUS_NOT_IMPLEMENTED
;
4052 RtlProtectHeap(IN PVOID HeapHandle
,
4053 IN BOOLEAN ReadOnly
)
4061 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
4062 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
4063 IN PVOID HeapInformation
,
4064 IN SIZE_T HeapInformationLength
)
4066 /* Setting heap information is not really supported except for enabling LFH */
4067 if (HeapInformationClass
== 0) return STATUS_SUCCESS
;
4069 /* Check buffer length */
4070 if (HeapInformationLength
< sizeof(ULONG
))
4072 /* The provided buffer is too small */
4073 return STATUS_BUFFER_TOO_SMALL
;
4076 /* Check for a special magic value for enabling LFH */
4077 if (*(PULONG
)HeapInformation
== 2)
4079 DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
4080 return STATUS_SUCCESS
;
4083 return STATUS_UNSUCCESSFUL
;
4088 RtlQueryHeapInformation(HANDLE HeapHandle
,
4089 HEAP_INFORMATION_CLASS HeapInformationClass
,
4090 PVOID HeapInformation OPTIONAL
,
4091 SIZE_T HeapInformationLength OPTIONAL
,
4092 PSIZE_T ReturnLength OPTIONAL
)
4094 PHEAP Heap
= (PHEAP
)HeapHandle
;
4096 /* Only HeapCompatibilityInformation is supported */
4097 if (HeapInformationClass
!= HeapCompatibilityInformation
)
4098 return STATUS_UNSUCCESSFUL
;
4100 /* Set result length */
4101 if (ReturnLength
) *ReturnLength
= sizeof(ULONG
);
4103 /* Check buffer length */
4104 if (HeapInformationLength
< sizeof(ULONG
))
4106 /* It's too small, return needed length */
4107 return STATUS_BUFFER_TOO_SMALL
;
4110 /* Return front end heap type */
4111 *(PULONG
)HeapInformation
= Heap
->FrontEndHeapType
;
4113 return STATUS_SUCCESS
;
4118 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
4130 RtlMultipleFreeHeap(IN PVOID HeapHandle
,