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
] =
83 /* FUNCTIONS *****************************************************************/
86 RtlpInitializeHeap(PHEAP Heap
,
92 PVOID NextHeapBase
= Heap
+ 1;
93 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
99 *HeaderSize
+= NumUCRs
* sizeof(*UcrDescriptor
);
101 /* Prepare a list of UCRs */
102 InitializeListHead(&Heap
->UCRList
);
103 InitializeListHead(&Heap
->UCRSegments
);
104 UcrDescriptor
= NextHeapBase
;
106 for (i
=0; i
<NumUCRs
; i
++, UcrDescriptor
++)
108 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
111 NextHeapBase
= UcrDescriptor
;
114 /* Round up header size again */
115 *HeaderSize
= ROUND_UP(*HeaderSize
, HEAP_ENTRY_SIZE
);
117 ASSERT(*HeaderSize
<= PAGE_SIZE
);
119 /* Initialize heap's header */
120 Heap
->Entry
.Size
= (USHORT
)((*HeaderSize
) >> HEAP_ENTRY_SHIFT
);
121 Heap
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
123 Heap
->Signature
= HEAP_SIGNATURE
;
125 Heap
->ForceFlags
= (Flags
& (HEAP_NO_SERIALIZE
|
126 HEAP_GENERATE_EXCEPTIONS
|
128 HEAP_REALLOC_IN_PLACE_ONLY
|
129 HEAP_VALIDATE_PARAMETERS_ENABLED
|
130 HEAP_VALIDATE_ALL_ENABLED
|
131 HEAP_TAIL_CHECKING_ENABLED
|
132 HEAP_CREATE_ALIGN_16
|
133 HEAP_FREE_CHECKING_ENABLED
));
134 Heap
->HeaderValidateCopy
= NULL
;
135 Heap
->HeaderValidateLength
= ((PCHAR
)NextHeapBase
- (PCHAR
)Heap
);
137 /* Initialize free lists */
138 for (i
=0; i
<HEAP_FREELISTS
; i
++)
140 InitializeListHead(&Heap
->FreeLists
[i
]);
143 /* Initialize "big" allocations list */
144 InitializeListHead(&Heap
->VirtualAllocdBlocks
);
146 /* Initialize lock */
150 Status
= RtlInitializeHeapLock((PHEAP_LOCK
)Lock
);
151 if (!NT_SUCCESS(Status
))
153 DPRINT1("Initializing the lock failed!\n");
154 return /*NULL*/; // FIXME!
158 /* Set the lock variable */
159 Heap
->LockVariable
= Lock
;
163 RtlpSetFreeListsBit(PHEAP Heap
,
164 PHEAP_FREE_ENTRY FreeEntry
)
168 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
170 /* Calculate offset in the free list bitmap */
171 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
172 Bit
= 1 << (FreeEntry
->Size
& 7);
174 /* Assure it's not already set */
175 ASSERT((Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
) == 0);
178 Heap
->u
.FreeListsInUseBytes
[Index
] |= Bit
;
182 RtlpClearFreeListsBit(PHEAP Heap
,
183 PHEAP_FREE_ENTRY FreeEntry
)
187 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
189 /* Calculate offset in the free list bitmap */
190 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
191 Bit
= 1 << (FreeEntry
->Size
& 7);
193 /* Assure it was set and the corresponding free list is empty */
194 ASSERT(Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
);
195 ASSERT(IsListEmpty(&Heap
->FreeLists
[FreeEntry
->Size
]));
198 Heap
->u
.FreeListsInUseBytes
[Index
] ^= Bit
;
202 RtlpInsertFreeBlockHelper(PHEAP Heap
,
203 PHEAP_FREE_ENTRY FreeEntry
,
207 PLIST_ENTRY FreeListHead
, Current
;
208 PHEAP_FREE_ENTRY CurrentEntry
;
210 ASSERT(FreeEntry
->Size
== BlockSize
);
212 /* Fill if it's not denied */
215 FreeEntry
->Flags
&= ~(HEAP_ENTRY_FILL_PATTERN
|
216 HEAP_ENTRY_EXTRA_PRESENT
|
219 if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
221 RtlFillMemoryUlong((PCHAR
)(FreeEntry
+ 1),
222 (BlockSize
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
),
225 FreeEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
230 /* Clear out all flags except the last entry one */
231 FreeEntry
->Flags
&= HEAP_ENTRY_LAST_ENTRY
;
234 /* Insert it either into dedicated or non-dedicated list */
235 if (BlockSize
< HEAP_FREELISTS
)
238 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
240 if (IsListEmpty(FreeListHead
))
242 RtlpSetFreeListsBit(Heap
, FreeEntry
);
247 /* Non-dedicated one */
248 FreeListHead
= &Heap
->FreeLists
[0];
249 Current
= FreeListHead
->Flink
;
251 /* Find a position where to insert it to (the list must be sorted) */
252 while (FreeListHead
!= Current
)
254 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
256 if (BlockSize
<= CurrentEntry
->Size
)
259 Current
= Current
->Flink
;
262 FreeListHead
= Current
;
265 /* Actually insert it into the list */
266 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
270 RtlpInsertFreeBlock(PHEAP Heap
,
271 PHEAP_FREE_ENTRY FreeEntry
,
274 USHORT Size
, PreviousSize
;
275 UCHAR SegmentOffset
, Flags
;
276 PHEAP_SEGMENT Segment
;
278 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
280 /* Increase the free size counter */
281 Heap
->TotalFreeSize
+= BlockSize
;
283 /* Remember certain values */
284 Flags
= FreeEntry
->Flags
;
285 PreviousSize
= FreeEntry
->PreviousSize
;
286 SegmentOffset
= FreeEntry
->SegmentOffset
;
287 Segment
= Heap
->Segments
[SegmentOffset
];
292 /* Check for the max size */
293 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
295 Size
= HEAP_MAX_BLOCK_SIZE
;
297 /* Special compensation if it goes above limit just by 1 */
298 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
301 FreeEntry
->Flags
= 0;
305 Size
= (USHORT
)BlockSize
;
306 FreeEntry
->Flags
= Flags
;
309 /* Change its size and insert it into a free list */
310 FreeEntry
->Size
= Size
;
311 FreeEntry
->PreviousSize
= PreviousSize
;
312 FreeEntry
->SegmentOffset
= SegmentOffset
;
314 /* Call a helper to actually insert the block */
315 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
321 /* Go to the next entry */
322 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
324 /* Check if that's all */
325 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
328 /* Update previous size if needed */
329 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
330 FreeEntry
->PreviousSize
= PreviousSize
;
334 RtlpRemoveFreeBlock(PHEAP Heap
,
335 PHEAP_FREE_ENTRY FreeEntry
,
339 SIZE_T Result
, RealSize
;
341 /* Remove the free block and update the freelists bitmap */
342 if (RemoveEntryList(&FreeEntry
->FreeList
) &&
343 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
345 RtlpClearFreeListsBit(Heap
, FreeEntry
);
348 /* Fill with pattern if necessary */
350 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
352 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
354 /* Deduct extra stuff from block's real size */
355 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
356 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
358 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
361 /* Check if the free filler is intact */
362 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
366 if (Result
!= RealSize
)
368 DPRINT1("Free heap block %p modified at %p after it was freed\n",
370 (PCHAR
)(FreeEntry
+ 1) + Result
);
376 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
378 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
380 /* Get pointer to the containing record */
381 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
383 /* Restore the real size */
384 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
387 PHEAP_UCR_DESCRIPTOR NTAPI
388 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
391 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
392 PHEAP_UCR_SEGMENT UcrSegment
;
393 PHEAP Heap
= Segment
->Heap
;
394 SIZE_T ReserveSize
= 16 * PAGE_SIZE
;
395 SIZE_T CommitSize
= 1 * PAGE_SIZE
;
398 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
400 /* Check if we have unused UCRs */
401 if (IsListEmpty(&Heap
->UCRList
))
403 /* Get a pointer to the first UCR segment */
404 UcrSegment
= CONTAINING_RECORD(Heap
->UCRSegments
.Flink
, HEAP_UCR_SEGMENT
, ListEntry
);
406 /* Check the list of UCR segments */
407 if (IsListEmpty(&Heap
->UCRSegments
) ||
408 UcrSegment
->ReservedSize
== UcrSegment
->CommittedSize
)
410 /* We need to create a new one. Reserve 16 pages for it */
412 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
413 (PVOID
*)&UcrSegment
,
419 if (!NT_SUCCESS(Status
)) return NULL
;
421 /* Commit one page */
422 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
423 (PVOID
*)&UcrSegment
,
429 if (!NT_SUCCESS(Status
))
431 /* Release reserved memory */
432 ZwFreeVirtualMemory(NtCurrentProcess(),
433 (PVOID
*)&UcrDescriptor
,
440 UcrSegment
->ReservedSize
= ReserveSize
;
441 UcrSegment
->CommittedSize
= CommitSize
;
443 /* Add it to the head of the list */
444 InsertHeadList(&Heap
->UCRSegments
, &UcrSegment
->ListEntry
);
446 /* Get a pointer to the first available UCR descriptor */
447 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)(UcrSegment
+ 1);
451 /* It's possible to use existing UCR segment. Commit one more page */
452 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
);
453 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
454 (PVOID
*)&UcrDescriptor
,
460 if (!NT_SUCCESS(Status
)) return NULL
;
463 UcrSegment
->CommittedSize
+= CommitSize
;
466 /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
467 while ((PCHAR
)UcrDescriptor
< ((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
))
469 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
474 /* There are unused UCRs, just get the first one */
475 Entry
= RemoveHeadList(&Heap
->UCRList
);
476 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
477 return UcrDescriptor
;
481 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
482 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
485 UcrDescriptor
->Address
= NULL
;
486 UcrDescriptor
->Size
= 0;
488 /* Put it into the heap's list of unused UCRs */
489 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
493 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
498 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
500 DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment
, Address
, Size
);
502 /* Go through the list of UCR descriptors, they are sorted from lowest address
504 Current
= Segment
->UCRSegmentList
.Flink
;
505 while(Current
!= &Segment
->UCRSegmentList
)
507 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
509 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
511 /* Check for a really lucky case */
512 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
515 UcrDescriptor
->Address
= (PVOID
)Address
;
516 UcrDescriptor
->Size
+= Size
;
520 /* We found the block after which the new one should go */
523 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
525 /* Modify this entry */
526 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
527 Size
+= UcrDescriptor
->Size
;
529 /* Advance to the next descriptor */
530 Current
= Current
->Flink
;
532 /* Remove the current descriptor from the list and destroy it */
533 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
534 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
536 Segment
->NumberOfUnCommittedRanges
--;
540 /* Advance to the next descriptor */
541 Current
= Current
->Flink
;
545 /* Create a new UCR descriptor */
546 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
547 if (!UcrDescriptor
) return;
549 UcrDescriptor
->Address
= (PVOID
)Address
;
550 UcrDescriptor
->Size
= Size
;
552 /* "Current" is the descriptor after which our one should go */
553 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
555 DPRINT("Added segment UCR with base %p, size 0x%x\n", Address
, Size
);
557 /* Increase counters */
558 Segment
->NumberOfUnCommittedRanges
++;
561 PHEAP_FREE_ENTRY NTAPI
562 RtlpFindAndCommitPages(PHEAP Heap
,
563 PHEAP_SEGMENT Segment
,
565 PVOID AddressRequested
)
568 ULONG_PTR Address
= 0;
569 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
570 PHEAP_ENTRY FirstEntry
, LastEntry
;
573 DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap
, Segment
, *Size
, Address
);
575 /* Go through UCRs in a segment */
576 Current
= Segment
->UCRSegmentList
.Flink
;
577 while(Current
!= &Segment
->UCRSegmentList
)
579 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
581 /* Check if we can use that one right away */
582 if (UcrDescriptor
->Size
>= *Size
&&
583 (UcrDescriptor
->Address
== AddressRequested
|| !AddressRequested
))
585 /* Get the address */
586 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
589 if (Heap
->CommitRoutine
)
591 Status
= Heap
->CommitRoutine(Heap
, (PVOID
*)&Address
, Size
);
595 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
603 DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size
, Address
, UcrDescriptor
->Size
);
605 /* Fail in unsuccessful case */
606 if (!NT_SUCCESS(Status
))
608 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
612 /* Update tracking numbers */
613 Segment
->NumberOfUnCommittedPages
-= *Size
/ PAGE_SIZE
;
615 /* Calculate first and last entries */
616 FirstEntry
= (PHEAP_ENTRY
)Address
;
618 /* Go through the entries to find the last one */
620 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
622 LastEntry
= &Segment
->Entry
;
624 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
626 LastEntry
+= LastEntry
->Size
;
628 ASSERT((LastEntry
+ LastEntry
->Size
) == FirstEntry
);
630 /* Unmark it as a last entry */
631 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
633 /* Update UCR descriptor */
634 UcrDescriptor
->Address
= (PVOID
)((ULONG_PTR
)UcrDescriptor
->Address
+ *Size
);
635 UcrDescriptor
->Size
-= *Size
;
637 DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
638 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
640 /* Set various first entry fields*/
641 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
642 FirstEntry
->Size
= (USHORT
)(*Size
>> HEAP_ENTRY_SHIFT
);
643 FirstEntry
->PreviousSize
= LastEntry
->Size
;
645 /* Check if anything left in this UCR */
646 if (UcrDescriptor
->Size
== 0)
648 /* It's fully exhausted */
650 /* Check if this is the end of the segment */
651 if(UcrDescriptor
->Address
== Segment
->LastValidEntry
)
653 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
657 FirstEntry
->Flags
= 0;
658 /* Update field of next entry */
659 ASSERT((FirstEntry
+ FirstEntry
->Size
)->PreviousSize
== 0);
660 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
663 /* This UCR needs to be removed because it became useless */
664 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
666 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
667 Segment
->NumberOfUnCommittedRanges
--;
671 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
675 return (PHEAP_FREE_ENTRY
)FirstEntry
;
678 /* Advance to the next descriptor */
679 PreviousUcr
= UcrDescriptor
;
680 Current
= Current
->Flink
;
687 RtlpDeCommitFreeBlock(PHEAP Heap
,
688 PHEAP_FREE_ENTRY FreeEntry
,
691 PHEAP_SEGMENT Segment
;
692 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
693 PHEAP_FREE_ENTRY NextFreeEntry
;
694 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
695 ULONG PrecedingSize
, NextSize
, DecommitSize
;
696 ULONG_PTR DecommitBase
;
699 DPRINT("Decommitting %p %p %x\n", Heap
, FreeEntry
, Size
);
701 /* We can't decommit if there is a commit routine! */
702 if (Heap
->CommitRoutine
)
704 /* Just add it back the usual way */
705 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
709 /* Get the segment */
710 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
712 /* Get the preceding entry */
713 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
714 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
716 if (PrecedingSize
== 1)
718 /* Just 1 heap entry, increase the base/size */
719 DecommitBase
+= PAGE_SIZE
;
720 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
722 else if (FreeEntry
->PreviousSize
&&
723 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
725 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
728 /* Get the next entry */
729 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
730 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
731 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
735 /* Just 1 heap entry, increase the size */
736 DecommitSize
-= PAGE_SIZE
;
737 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
739 else if (NextSize
== 0 &&
740 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
742 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
745 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
747 /* Calculate real decommit size */
748 if (DecommitSize
> DecommitBase
)
750 DecommitSize
-= DecommitBase
;
754 /* Nothing to decommit */
755 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
759 /* A decommit is necessary. Create a UCR descriptor */
760 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
763 DPRINT1("HEAP: Failed to create UCR descriptor\n");
764 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
768 /* Decommit the memory */
769 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
770 (PVOID
*)&DecommitBase
,
774 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
775 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
777 if (!NT_SUCCESS(Status
))
779 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
783 /* Insert uncommitted pages */
784 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
785 Segment
->NumberOfUnCommittedPages
+= (DecommitSize
/ PAGE_SIZE
);
789 /* Adjust size of this free entry and insert it */
790 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
791 FreeEntry
->Size
= (USHORT
)PrecedingSize
;
792 Heap
->TotalFreeSize
+= PrecedingSize
;
794 /* Insert it into the free list */
795 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
, FALSE
);
797 else if (PrecedingInUseEntry
)
799 /* Adjust preceding in use entry */
800 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
803 /* Now the next one */
806 /* Adjust size of this free entry and insert it */
807 NextFreeEntry
->Flags
= 0;
808 NextFreeEntry
->PreviousSize
= 0;
809 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
810 NextFreeEntry
->Size
= (USHORT
)NextSize
;
812 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= (USHORT
)NextSize
;
814 Heap
->TotalFreeSize
+= NextSize
;
815 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
, FALSE
);
817 else if (NextInUseEntry
)
819 NextInUseEntry
->PreviousSize
= 0;
824 RtlpInitializeHeapSegment(PHEAP Heap
,
825 PHEAP_SEGMENT Segment
,
829 PVOID UncommittedBase
,
832 ULONG Pages
, CommitSize
;
833 PHEAP_ENTRY HeapEntry
;
834 USHORT PreviousSize
= 0, NewSize
;
837 Pages
= ((PCHAR
)LimitAddress
- (PCHAR
)BaseAddress
) / PAGE_SIZE
;
839 HeapEntry
= (PHEAP_ENTRY
)ROUND_UP(Segment
+ 1, HEAP_ENTRY_SIZE
);
841 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %p %p %p)\n", Heap
, Segment
, SegmentIndex
, Flags
, BaseAddress
, UncommittedBase
, LimitAddress
);
842 DPRINT("Pages %x, HeapEntry %p, sizeof(HEAP_SEGMENT) %x\n", Pages
, HeapEntry
, sizeof(HEAP_SEGMENT
));
844 /* Check if it's the first segment and remember its size */
845 if (Heap
== BaseAddress
)
846 PreviousSize
= Heap
->Entry
.Size
;
848 NewSize
= ((PCHAR
)HeapEntry
- (PCHAR
)Segment
) >> HEAP_ENTRY_SHIFT
;
850 if ((PVOID
)(HeapEntry
+ 1) >= UncommittedBase
)
852 /* Check if it goes beyond the limit */
853 if ((PVOID
)(HeapEntry
+ 1) >= LimitAddress
)
856 /* Need to commit memory */
857 CommitSize
= (PCHAR
)(HeapEntry
+ 1) - (PCHAR
)UncommittedBase
;
858 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
859 (PVOID
)&UncommittedBase
,
864 if (!NT_SUCCESS(Status
))
866 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
870 DPRINT("Committed %d bytes at base %p\n", CommitSize
, UncommittedBase
);
872 /* Calcule the new uncommitted base */
873 UncommittedBase
= (PVOID
)((PCHAR
)UncommittedBase
+ CommitSize
);
876 /* Initialize the segment entry */
877 Segment
->Entry
.PreviousSize
= PreviousSize
;
878 Segment
->Entry
.Size
= NewSize
;
879 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
880 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
882 /* Initialize the segment itself */
883 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
884 Segment
->Heap
= Heap
;
885 Segment
->BaseAddress
= BaseAddress
;
886 Segment
->FirstEntry
= HeapEntry
;
887 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((PCHAR
)BaseAddress
+ Pages
* PAGE_SIZE
);
888 Segment
->NumberOfPages
= Pages
;
889 Segment
->NumberOfUnCommittedPages
= ((PCHAR
)LimitAddress
- (PCHAR
)UncommittedBase
) / PAGE_SIZE
;
890 InitializeListHead(&Segment
->UCRSegmentList
);
892 /* Insert uncommitted pages into UCR (uncommitted ranges) list */
893 if (Segment
->NumberOfUnCommittedPages
)
895 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
)UncommittedBase
, Segment
->NumberOfUnCommittedPages
* PAGE_SIZE
);
898 /* Set the segment index pointer */
899 Heap
->Segments
[SegmentIndex
] = Segment
;
901 /* Prepare a free heap entry */
902 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
903 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
904 HeapEntry
->SegmentOffset
= SegmentIndex
;
907 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, (PHEAP_ENTRY
)UncommittedBase
- HeapEntry
);
913 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
919 /* Make sure it's not user allocated */
920 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
922 BaseAddress
= Segment
->BaseAddress
;
923 DPRINT("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
925 /* Release virtual memory */
926 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
931 if (!NT_SUCCESS(Status
))
933 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
939 RtlpAddHeapToProcessList(PHEAP Heap
)
944 Peb
= RtlGetCurrentPeb();
946 /* Acquire the lock */
947 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
950 /* Check if max number of heaps reached */
951 if (Peb
->NumberOfHeaps
== Peb
->MaximumNumberOfHeaps
)
953 // TODO: Handle this case
957 /* Add the heap to the process heaps */
958 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = Heap
;
959 Peb
->NumberOfHeaps
++;
960 Heap
->ProcessHeapsListIndex
= (USHORT
)Peb
->NumberOfHeaps
;
963 /* Release the lock */
964 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
971 RtlpRemoveHeapFromProcessList(PHEAP Heap
)
974 PHEAP
*Current
, *Next
;
978 Peb
= RtlGetCurrentPeb();
980 /* Acquire the lock */
981 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
983 /* Check if we don't need anything to do */
984 if ((Heap
->ProcessHeapsListIndex
== 0) ||
985 (Heap
->ProcessHeapsListIndex
> Peb
->NumberOfHeaps
) ||
986 (Peb
->NumberOfHeaps
== 0))
988 /* Release the lock */
989 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
994 /* The process actually has more than one heap.
995 Use classic, lernt from university times algorithm for removing an entry
996 from a static array */
998 Current
= (PHEAP
*)&Peb
->ProcessHeaps
[Heap
->ProcessHeapsListIndex
- 1];
1001 /* How many items we need to shift to the left */
1002 Count
= Peb
->NumberOfHeaps
- (Heap
->ProcessHeapsListIndex
- 1);
1004 /* Move them all in a loop */
1007 /* Copy it and advance next pointer */
1010 /* Update its index */
1011 (*Current
)->ProcessHeapsListIndex
-= 1;
1013 /* Advance pointers */
1018 /* Decrease total number of heaps */
1019 Peb
->NumberOfHeaps
--;
1021 /* Zero last unused item */
1022 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = NULL
;
1023 Heap
->ProcessHeapsListIndex
= 0;
1025 /* Release the lock */
1026 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1029 PHEAP_FREE_ENTRY NTAPI
1030 RtlpCoalesceHeap(PHEAP Heap
)
1036 PHEAP_FREE_ENTRY NTAPI
1037 RtlpCoalesceFreeBlocks (PHEAP Heap
,
1038 PHEAP_FREE_ENTRY FreeEntry
,
1042 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
1044 /* Get the previous entry */
1045 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
1048 if (CurrentEntry
!= FreeEntry
&&
1049 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1050 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
1052 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
1054 /* Remove it if asked for */
1057 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1058 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1060 /* Remove it only once! */
1064 /* Remove previous entry too */
1065 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1068 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1070 /* Advance FreeEntry and update sizes */
1071 FreeEntry
= CurrentEntry
;
1072 *FreeSize
= *FreeSize
+ CurrentEntry
->Size
;
1073 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1074 FreeEntry
->Size
= (USHORT
)(*FreeSize
);
1076 /* Also update previous size if needed */
1077 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1079 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= (USHORT
)(*FreeSize
);
1083 /* Check the next block if it exists */
1084 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1086 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1088 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1089 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1091 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1093 /* Remove it if asked for */
1096 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1097 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1101 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1103 /* Remove next entry now */
1104 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1107 *FreeSize
= *FreeSize
+ NextEntry
->Size
;
1108 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1109 FreeEntry
->Size
= (USHORT
)(*FreeSize
);
1111 /* Also update previous size if needed */
1112 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1114 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= (USHORT
)(*FreeSize
);
1121 PHEAP_FREE_ENTRY NTAPI
1122 RtlpExtendHeap(PHEAP Heap
,
1126 UCHAR Index
, EmptyIndex
;
1127 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1128 PHEAP_SEGMENT Segment
;
1129 PHEAP_FREE_ENTRY FreeEntry
;
1132 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1134 /* Calculate amount in pages */
1135 Pages
= (Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
1136 FreeSize
= Pages
* PAGE_SIZE
;
1137 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1139 /* Find an empty segment */
1140 EmptyIndex
= HEAP_SEGMENTS
;
1141 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1143 Segment
= Heap
->Segments
[Index
];
1145 if (Segment
) DPRINT("Segment[%d] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1147 /* Check if its size suits us */
1149 Pages
<= Segment
->NumberOfUnCommittedPages
)
1151 DPRINT("This segment is suitable\n");
1153 /* Commit needed amount */
1154 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1156 /* Coalesce it with adjacent entries */
1159 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1160 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1161 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1165 else if (!Segment
&&
1166 EmptyIndex
== HEAP_SEGMENTS
)
1168 /* Remember the first unused segment index */
1173 /* No luck, need to grow the heap */
1174 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1175 (EmptyIndex
!= HEAP_SEGMENTS
))
1179 /* Reserve the memory */
1180 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1181 ReserveSize
= Heap
->SegmentReserve
;
1183 ReserveSize
= Size
+ PAGE_SIZE
;
1185 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1192 /* If it failed, retry again with a half division algorithm */
1193 while (!NT_SUCCESS(Status
) &&
1194 ReserveSize
!= Size
+ PAGE_SIZE
)
1198 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1199 ReserveSize
= Size
+ PAGE_SIZE
;
1201 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1209 /* Proceed only if it's success */
1210 if (NT_SUCCESS(Status
))
1212 Heap
->SegmentReserve
+= ReserveSize
;
1214 /* Now commit the memory */
1215 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1216 CommitSize
= Heap
->SegmentCommit
;
1218 CommitSize
= Size
+ PAGE_SIZE
;
1220 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1227 DPRINT("Committed %d bytes at base %p\n", CommitSize
, Segment
);
1229 /* Initialize heap segment if commit was successful */
1230 if (NT_SUCCESS(Status
))
1232 if (!RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, Segment
,
1233 (PCHAR
)Segment
+ CommitSize
, (PCHAR
)Segment
+ ReserveSize
))
1235 Status
= STATUS_NO_MEMORY
;
1239 /* If everything worked - cool */
1240 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1242 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1244 /* Nope, we failed. Free memory */
1245 ZwFreeVirtualMemory(NtCurrentProcess(),
1252 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1256 if (RtlpGetMode() == UserMode
)
1258 /* If coalescing on free is disabled in usermode, then do it here */
1259 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1261 FreeEntry
= RtlpCoalesceHeap(Heap
);
1263 /* If it's a suitable one - return it */
1265 FreeEntry
->Size
>= Size
)
1275 /***********************************************************************
1278 * Handle of heap: Success
1284 RtlCreateHeap(ULONG Flags
,
1289 PRTL_HEAP_PARAMETERS Parameters
)
1291 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1293 RTL_HEAP_PARAMETERS SafeParams
= {0};
1295 ULONG_PTR MaximumUserModeAddress
;
1296 SYSTEM_BASIC_INFORMATION SystemInformation
;
1297 MEMORY_BASIC_INFORMATION MemoryInfo
;
1298 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1299 ULONG HeapSegmentFlags
= 0;
1301 ULONG MaxBlockSize
, HeaderSize
;
1302 BOOLEAN AllocateLock
= FALSE
;
1304 /* Check for a special heap */
1305 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1307 Heap
= RtlpPageHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1308 if (Heap
) return Heap
;
1310 /* Reset a special Parameters == -1 hack */
1311 if ((ULONG_PTR
)Parameters
== (ULONG_PTR
)-1)
1314 DPRINT1("Enabling page heap failed\n");
1317 /* Check validation flags */
1318 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1320 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1321 Flags
&= HEAP_CREATE_VALID_MASK
;
1324 /* TODO: Capture parameters, once we decide to use SEH */
1325 if (!Parameters
) Parameters
= &SafeParams
;
1327 /* Check global flags */
1328 if (NtGlobalFlags
& FLG_HEAP_DISABLE_COALESCING
)
1329 Flags
|= HEAP_DISABLE_COALESCE_ON_FREE
;
1331 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1332 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1334 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1335 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1337 if (RtlpGetMode() == UserMode
)
1339 /* Also check these flags if in usermode */
1340 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1341 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1343 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1344 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1346 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1347 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1350 Peb
= RtlGetCurrentPeb();
1352 /* Apply defaults for non-set parameters */
1353 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= Peb
->HeapSegmentCommit
;
1354 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= Peb
->HeapSegmentReserve
;
1355 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= Peb
->HeapDeCommitFreeBlockThreshold
;
1356 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= Peb
->HeapDeCommitTotalFreeThreshold
;
1360 /* Apply defaults for non-set parameters */
1362 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= MmHeapSegmentCommit
;
1363 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= MmHeapSegmentReserve
;
1364 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
1365 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
1369 // FIXME: Move to memory manager
1370 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= PAGE_SIZE
* 2;
1371 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= 1048576;
1372 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= PAGE_SIZE
;
1373 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= 65536;
1375 /* Get the max um address */
1376 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1378 sizeof(SystemInformation
),
1381 if (!NT_SUCCESS(Status
))
1383 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1387 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1389 /* Calculate max alloc size */
1390 if (!Parameters
->MaximumAllocationSize
)
1391 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1393 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1395 if (!Parameters
->VirtualMemoryThreshold
||
1396 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1398 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1401 /* Check reserve/commit sizes and set default values */
1404 CommitSize
= PAGE_SIZE
;
1406 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1408 TotalSize
= 64 * PAGE_SIZE
;
1412 /* Round up the commit size to be at least the page size */
1413 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1416 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1418 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1421 /* Call special heap */
1422 if (RtlpHeapIsSpecial(Flags
))
1423 return RtlDebugCreateHeap(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1425 /* Calculate header size */
1426 HeaderSize
= sizeof(HEAP
);
1427 if (!(Flags
& HEAP_NO_SERIALIZE
))
1431 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
1435 HeaderSize
+= sizeof(HEAP_LOCK
);
1436 AllocateLock
= TRUE
;
1441 /* Invalid parameters */
1445 /* See if we are already provided with an address for the heap */
1448 if (Parameters
->CommitRoutine
)
1450 /* There is a commit routine, so no problem here, check params */
1451 if ((Flags
& HEAP_GROWABLE
) ||
1452 !Parameters
->InitialCommit
||
1453 !Parameters
->InitialReserve
||
1454 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1460 /* Calculate committed and uncommitted addresses */
1461 CommittedAddress
= Addr
;
1462 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1463 TotalSize
= Parameters
->InitialReserve
;
1465 /* Zero the initial page ourselves */
1466 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1470 /* Commit routine is absent, so query how much memory caller reserved */
1471 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1473 MemoryBasicInformation
,
1478 if (!NT_SUCCESS(Status
))
1480 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1485 if (MemoryInfo
.BaseAddress
!= Addr
||
1486 MemoryInfo
.State
== MEM_FREE
)
1491 /* Validation checks passed, set committed/uncommitted addresses */
1492 CommittedAddress
= Addr
;
1494 /* Check if it's committed or not */
1495 if (MemoryInfo
.State
== MEM_COMMIT
)
1497 /* Zero it out because it's already committed */
1498 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1500 /* Calculate uncommitted address value */
1501 CommitSize
= MemoryInfo
.RegionSize
;
1502 TotalSize
= CommitSize
;
1503 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1505 /* Check if uncommitted address is reserved */
1506 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1508 MemoryBasicInformation
,
1513 if (NT_SUCCESS(Status
) &&
1514 MemoryInfo
.State
== MEM_RESERVE
)
1516 /* It is, so add it up to the reserve size */
1517 TotalSize
+= MemoryInfo
.RegionSize
;
1522 /* It's not committed, inform following code that a commit is necessary */
1523 CommitSize
= PAGE_SIZE
;
1524 UncommittedAddress
= Addr
;
1528 /* Mark this as a user-committed mem */
1529 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1534 /* Check commit routine */
1535 if (Parameters
->CommitRoutine
) return NULL
;
1537 /* Reserve memory */
1538 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1545 if (!NT_SUCCESS(Status
))
1547 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1551 /* Set base addresses */
1552 CommittedAddress
= Heap
;
1553 UncommittedAddress
= Heap
;
1556 /* Check if we need to commit something */
1557 if (CommittedAddress
== UncommittedAddress
)
1559 /* Commit the required size */
1560 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1567 DPRINT("Committed %d bytes at base %p\n", CommitSize
, CommittedAddress
);
1569 if (!NT_SUCCESS(Status
))
1571 DPRINT1("Failure, Status 0x%08X\n", Status
);
1573 /* Release memory if it was reserved */
1574 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1582 /* Calculate new uncommitted address */
1583 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1586 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1588 /* Initialize the heap */
1589 RtlpInitializeHeap(Heap
, &HeaderSize
, Flags
, AllocateLock
, Lock
);
1591 /* Initialize heap's first segment */
1592 if (!RtlpInitializeHeapSegment(Heap
,
1593 (PHEAP_SEGMENT
)((PCHAR
)Heap
+ HeaderSize
),
1598 (PCHAR
)CommittedAddress
+ TotalSize
))
1600 DPRINT1("Failed to initialize heap segment\n");
1604 /* Set other data */
1605 Heap
->ProcessHeapsListIndex
= 0;
1606 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
1607 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
1608 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
1609 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
1610 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
1611 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, HEAP_ENTRY_SIZE
) >> HEAP_ENTRY_SHIFT
;
1612 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
1615 if (Flags
& HEAP_CREATE_ALIGN_16
)
1617 Heap
->AlignMask
= (ULONG
)~15;
1618 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
1622 Heap
->AlignMask
= (ULONG
)~(HEAP_ENTRY_SIZE
- 1);
1623 Heap
->AlignRound
= HEAP_ENTRY_SIZE
- 1 + sizeof(HEAP_ENTRY
);
1626 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1627 Heap
->AlignRound
+= HEAP_ENTRY_SIZE
;
1629 /* Add heap to process list in case of usermode heap */
1630 if (RtlpGetMode() == UserMode
)
1632 RtlpAddHeapToProcessList(Heap
);
1634 // FIXME: What about lookasides?
1637 DPRINT("Heap %p, flags 0x%08x\n", Heap
, Heap
->Flags
);
1641 /***********************************************************************
1650 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1651 * Failure: The Heap handle, if heap is the process heap.
1654 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1656 PHEAP Heap
= (PHEAP
)HeapPtr
;
1657 PLIST_ENTRY Current
;
1658 PHEAP_UCR_SEGMENT UcrSegment
;
1659 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1663 PHEAP_SEGMENT Segment
;
1665 if (!HeapPtr
) return NULL
;
1667 /* Call page heap routine if required */
1668 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
) return RtlpPageHeapDestroy(HeapPtr
);
1670 /* Call special heap */
1671 if (RtlpHeapIsSpecial(Heap
->Flags
))
1673 if (!RtlDebugDestroyHeap(Heap
)) return HeapPtr
;
1676 /* Check for a process heap */
1677 if (RtlpGetMode() == UserMode
&&
1678 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1680 /* Free up all big allocations */
1681 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1682 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1684 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1685 BaseAddress
= (PVOID
)VirtualEntry
;
1686 Current
= Current
->Flink
;
1688 ZwFreeVirtualMemory(NtCurrentProcess(),
1694 /* Delete tags and remove heap from the process heaps list in user mode */
1695 if (RtlpGetMode() == UserMode
)
1697 // FIXME DestroyTags
1698 RtlpRemoveHeapFromProcessList(Heap
);
1701 /* Delete the heap lock */
1702 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1704 /* Delete it if it wasn't user allocated */
1705 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1706 RtlDeleteHeapLock(Heap
->LockVariable
);
1708 /* Clear out the lock variable */
1709 Heap
->LockVariable
= NULL
;
1712 /* Free UCR segments if any were created */
1713 Current
= Heap
->UCRSegments
.Flink
;
1714 while(Current
!= &Heap
->UCRSegments
)
1716 UcrSegment
= CONTAINING_RECORD(Current
, HEAP_UCR_SEGMENT
, ListEntry
);
1718 /* Advance to the next descriptor */
1719 Current
= Current
->Flink
;
1721 BaseAddress
= (PVOID
)UcrSegment
;
1724 /* Release that memory */
1725 ZwFreeVirtualMemory(NtCurrentProcess(),
1731 /* Go through segments and destroy them */
1732 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1734 Segment
= Heap
->Segments
[i
];
1735 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1742 RtlpSplitEntry(PHEAP Heap
,
1743 PHEAP_FREE_ENTRY FreeBlock
,
1744 SIZE_T AllocationSize
,
1748 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1750 PHEAP_ENTRY InUseEntry
;
1753 /* Save flags, update total free size */
1754 FreeFlags
= FreeBlock
->Flags
;
1755 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1757 /* Make this block an in-use one */
1758 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1759 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
;
1760 InUseEntry
->SmallTagIndex
= 0;
1762 /* Calculate the extra amount */
1763 FreeSize
= InUseEntry
->Size
- Index
;
1765 /* Update it's size fields (we don't need their data anymore) */
1766 InUseEntry
->Size
= (USHORT
)Index
;
1767 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
1769 /* If there is something to split - do the split */
1772 /* Don't split if resulting entry can't contain any payload data
1773 (i.e. being just HEAP_ENTRY_SIZE) */
1776 /* Increase sizes of the in-use entry */
1778 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
1782 /* Calculate a pointer to the new entry */
1783 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
1786 SplitBlock
->Flags
= FreeFlags
;
1787 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
1788 SplitBlock
->Size
= (USHORT
)FreeSize
;
1789 SplitBlock
->PreviousSize
= (USHORT
)Index
;
1791 /* Check if it's the last entry */
1792 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1794 /* Insert it to the free list if it's the last entry */
1795 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1796 Heap
->TotalFreeSize
+= FreeSize
;
1800 /* Not so easy - need to update next's previous size too */
1801 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
1803 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
1805 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
1806 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1807 Heap
->TotalFreeSize
+= FreeSize
;
1811 /* Even more complex - the next entry is free, so we can merge them into one! */
1812 SplitBlock
->Flags
= SplitBlock2
->Flags
;
1814 /* Remove that next entry */
1815 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
1818 FreeSize
+= SplitBlock2
->Size
;
1819 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
1821 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1823 /* Insert it back */
1824 SplitBlock
->Size
= (USHORT
)FreeSize
;
1826 /* Don't forget to update previous size of the next entry! */
1827 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1829 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
1832 /* Actually insert it */
1833 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, (USHORT
)FreeSize
, FALSE
);
1835 /* Update total size */
1836 Heap
->TotalFreeSize
+= FreeSize
;
1840 /* Resulting block is quite big */
1841 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
1846 /* Reset flags of the free entry */
1851 /* Set last entry flag */
1852 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1853 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1859 RtlpAllocateNonDedicated(PHEAP Heap
,
1862 SIZE_T AllocationSize
,
1866 PLIST_ENTRY FreeListHead
, Next
;
1867 PHEAP_FREE_ENTRY FreeBlock
;
1868 PHEAP_ENTRY InUseEntry
;
1869 PHEAP_ENTRY_EXTRA Extra
;
1870 EXCEPTION_RECORD ExceptionRecord
;
1872 /* Go through the zero list to find a place where to insert the new entry */
1873 FreeListHead
= &Heap
->FreeLists
[0];
1875 /* Start from the largest block to reduce time */
1876 Next
= FreeListHead
->Blink
;
1877 if (FreeListHead
!= Next
)
1879 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1881 if (FreeBlock
->Size
>= Index
)
1883 /* Our request is smaller than the largest entry in the zero list */
1885 /* Go through the list to find insertion place */
1886 Next
= FreeListHead
->Flink
;
1887 while (FreeListHead
!= Next
)
1889 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1891 if (FreeBlock
->Size
>= Index
)
1893 /* Found minimally fitting entry. Proceed to either using it as it is
1894 or splitting it to two entries */
1895 RemoveEntryList(&FreeBlock
->FreeList
);
1898 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
1900 /* Release the lock */
1901 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1903 /* Zero memory if that was requested */
1904 if (Flags
& HEAP_ZERO_MEMORY
)
1905 RtlZeroMemory(InUseEntry
+ 1, Size
);
1906 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1908 /* Fill this block with a special pattern */
1909 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1912 /* Fill tail of the block with a special pattern too if requested */
1913 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1915 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1916 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1919 /* Prepare extra if it's present */
1920 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1922 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1923 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1928 /* Return pointer to the */
1929 return InUseEntry
+ 1;
1932 /* Advance to the next entry */
1938 /* Extend the heap, 0 list didn't have anything suitable */
1939 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
1941 /* Use the new biggest entry we've got */
1944 RemoveEntryList(&FreeBlock
->FreeList
);
1947 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
1949 /* Release the lock */
1950 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1952 /* Zero memory if that was requested */
1953 if (Flags
& HEAP_ZERO_MEMORY
)
1954 RtlZeroMemory(InUseEntry
+ 1, Size
);
1955 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1957 /* Fill this block with a special pattern */
1958 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1961 /* Fill tail of the block with a special pattern too if requested */
1962 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1964 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1965 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1968 /* Prepare extra if it's present */
1969 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1971 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1972 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1977 /* Return pointer to the */
1978 return InUseEntry
+ 1;
1981 /* Really unfortunate, out of memory condition */
1982 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
1984 /* Generate an exception */
1985 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
1987 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
1988 ExceptionRecord
.ExceptionRecord
= NULL
;
1989 ExceptionRecord
.NumberParameters
= 1;
1990 ExceptionRecord
.ExceptionFlags
= 0;
1991 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
1993 RtlRaiseException(&ExceptionRecord
);
1996 /* Release the lock */
1997 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1998 DPRINT1("HEAP: Allocation failed!\n");
1999 DPRINT1("Flags %x\n", Heap
->Flags
);
2003 /***********************************************************************
2004 * HeapAlloc (KERNEL32.334)
2006 * Pointer to allocated memory block
2008 * 0x7d030f60--invalid flags in RtlHeapAllocate
2012 RtlAllocateHeap(IN PVOID HeapPtr
,
2016 PHEAP Heap
= (PHEAP
)HeapPtr
;
2017 PULONG FreeListsInUse
;
2018 ULONG FreeListsInUseUlong
;
2019 SIZE_T AllocationSize
;
2021 PLIST_ENTRY FreeListHead
;
2022 PHEAP_ENTRY InUseEntry
;
2023 PHEAP_FREE_ENTRY FreeBlock
;
2024 ULONG InUseIndex
, i
;
2026 EXCEPTION_RECORD ExceptionRecord
;
2027 BOOLEAN HeapLocked
= FALSE
;
2028 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
2029 PHEAP_ENTRY_EXTRA Extra
;
2033 Flags
|= Heap
->ForceFlags
;
2035 /* Call special heap */
2036 if (RtlpHeapIsSpecial(Flags
))
2037 return RtlDebugAllocateHeap(Heap
, Flags
, Size
);
2039 /* Check for the maximum size */
2040 if (Size
>= 0x80000000)
2042 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2043 DPRINT1("HEAP: Allocation failed!\n");
2047 if (Flags
& (HEAP_CREATE_ENABLE_TRACING
|
2048 HEAP_CREATE_ALIGN_16
))
2050 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
2053 //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
2055 /* Calculate allocation size and index */
2057 AllocationSize
= Size
;
2060 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2061 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2063 /* Acquire the lock if necessary */
2064 if (!(Flags
& HEAP_NO_SERIALIZE
))
2066 RtlEnterHeapLock(Heap
->LockVariable
);
2070 /* Depending on the size, the allocation is going to be done from dedicated,
2071 non-dedicated lists or a virtual block of memory */
2072 if (Index
< HEAP_FREELISTS
)
2074 FreeListHead
= &Heap
->FreeLists
[Index
];
2076 if (!IsListEmpty(FreeListHead
))
2078 /* There is a free entry in this list */
2079 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2083 /* Save flags and remove the free entry */
2084 FreeFlags
= FreeBlock
->Flags
;
2085 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2087 /* Update the total free size of the heap */
2088 Heap
->TotalFreeSize
-= Index
;
2090 /* Initialize this block */
2091 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2092 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2093 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2094 InUseEntry
->SmallTagIndex
= 0;
2098 /* Find smallest free block which this request could fit in */
2099 InUseIndex
= Index
>> 5;
2100 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2102 /* This bit magic disables all sizes which are less than the requested allocation size */
2103 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2105 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2107 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2109 /* Go through the list */
2110 for (i
= InUseIndex
; i
< 4; i
++)
2112 if (FreeListsInUseUlong
)
2114 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2118 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2121 /* Nothing found, search in the non-dedicated list */
2123 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2125 /* That list is found, now calculate exact block */
2126 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2128 /* Take this entry and remove it from the list of free blocks */
2129 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2132 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2135 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2138 /* Release the lock */
2139 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2141 /* Zero memory if that was requested */
2142 if (Flags
& HEAP_ZERO_MEMORY
)
2143 RtlZeroMemory(InUseEntry
+ 1, Size
);
2144 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2146 /* Fill this block with a special pattern */
2147 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2150 /* Fill tail of the block with a special pattern too if requested */
2151 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2153 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2154 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2157 /* Prepare extra if it's present */
2158 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2160 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2161 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2166 /* User data starts right after the entry's header */
2167 return InUseEntry
+ 1;
2169 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2171 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2172 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2174 else if (Heap
->Flags
& HEAP_GROWABLE
)
2176 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2177 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2179 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2180 (PVOID
*)&VirtualBlock
,
2186 if (!NT_SUCCESS(Status
))
2189 /* Release the lock */
2190 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2191 DPRINT1("HEAP: Allocation failed!\n");
2195 /* Initialize the newly allocated block */
2196 VirtualBlock
->BusyBlock
.Size
= (USHORT
)(AllocationSize
- Size
);
2197 VirtualBlock
->BusyBlock
.Flags
= HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
| HEAP_ENTRY_BUSY
;
2198 VirtualBlock
->CommitSize
= AllocationSize
;
2199 VirtualBlock
->ReserveSize
= AllocationSize
;
2201 /* Insert it into the list of virtual allocations */
2202 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2204 /* Release the lock */
2205 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2207 /* Return pointer to user data */
2208 return VirtualBlock
+ 1;
2211 /* Generate an exception */
2212 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2214 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2215 ExceptionRecord
.ExceptionRecord
= NULL
;
2216 ExceptionRecord
.NumberParameters
= 1;
2217 ExceptionRecord
.ExceptionFlags
= 0;
2218 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2220 RtlRaiseException(&ExceptionRecord
);
2223 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL
);
2225 /* Release the lock */
2226 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2227 DPRINT1("HEAP: Allocation failed!\n");
2232 /***********************************************************************
2233 * HeapFree (KERNEL32.338)
2240 BOOLEAN NTAPI
RtlFreeHeap(
2241 HANDLE HeapPtr
, /* [in] Handle of heap */
2242 ULONG Flags
, /* [in] Heap freeing flags */
2243 PVOID Ptr
/* [in] Address of memory to free */
2247 PHEAP_ENTRY HeapEntry
;
2248 USHORT TagIndex
= 0;
2250 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2251 BOOLEAN Locked
= FALSE
;
2254 /* Freeing NULL pointer is a legal operation */
2255 if (!Ptr
) return TRUE
;
2257 /* Get pointer to the heap and force flags */
2258 Heap
= (PHEAP
)HeapPtr
;
2259 Flags
|= Heap
->ForceFlags
;
2261 /* Call special heap */
2262 if (RtlpHeapIsSpecial(Flags
))
2263 return RtlDebugFreeHeap(Heap
, Flags
, Ptr
);
2265 /* Lock if necessary */
2266 if (!(Flags
& HEAP_NO_SERIALIZE
))
2268 RtlEnterHeapLock(Heap
->LockVariable
);
2272 /* Get pointer to the heap entry */
2273 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2275 /* Check this entry, fail if it's invalid */
2276 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2277 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2278 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2280 /* This is an invalid block */
2281 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2282 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2284 /* Release the heap lock */
2285 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2289 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2291 /* Big allocation */
2292 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2294 /* Remove it from the list */
2295 RemoveEntryList(&VirtualEntry
->Entry
);
2300 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2301 (PVOID
*)&VirtualEntry
,
2305 if (!NT_SUCCESS(Status
))
2307 DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
2308 Status
, Heap
, Ptr
, VirtualEntry
);
2309 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status
);
2314 /* Normal allocation */
2315 BlockSize
= HeapEntry
->Size
;
2319 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2320 if (RtlpGetMode() == KernelMode
||
2321 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2323 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2324 (PHEAP_FREE_ENTRY
)HeapEntry
,
2329 /* If there is no need to decommit the block - put it into a free list */
2330 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2331 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2333 /* Check if it needs to go to a 0 list */
2334 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2336 /* General-purpose 0 list */
2337 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2341 /* Usual free list */
2342 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2344 /* Assert sizes are consistent */
2345 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2347 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2350 /* Increase the free size */
2351 Heap
->TotalFreeSize
+= BlockSize
;
2355 if (RtlpGetMode() == UserMode
&&
2364 /* Decommit this block */
2365 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2369 /* Release the heap lock */
2370 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2376 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2378 IN PHEAP_ENTRY InUseEntry
,
2382 UCHAR EntryFlags
, RememberFlags
;
2383 PHEAP_FREE_ENTRY FreeEntry
, UnusedEntry
, FollowingEntry
;
2384 SIZE_T FreeSize
, PrevSize
, TailPart
, AddedSize
= 0;
2385 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2387 /* We can't grow beyond specified threshold */
2388 if (Index
> Heap
->VirtualMemoryThreshold
)
2391 /* Get entry flags */
2392 EntryFlags
= InUseEntry
->Flags
;
2394 /* Get the next free entry */
2395 FreeEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ InUseEntry
->Size
);
2397 if (EntryFlags
& HEAP_ENTRY_LAST_ENTRY
)
2399 /* There is no next block, just uncommitted space. Calculate how much is needed */
2400 FreeSize
= (Index
- InUseEntry
->Size
) << HEAP_ENTRY_SHIFT
;
2401 FreeSize
= ROUND_UP(FreeSize
, PAGE_SIZE
);
2403 /* Find and commit those pages */
2404 FreeEntry
= RtlpFindAndCommitPages(Heap
,
2405 Heap
->Segments
[InUseEntry
->SegmentOffset
],
2409 /* Fail if it failed... */
2410 if (!FreeEntry
) return FALSE
;
2412 /* It was successful, perform coalescing */
2413 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
2414 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
2416 /* Check if it's enough */
2417 if (FreeSize
+ InUseEntry
->Size
< Index
)
2419 /* Still not enough */
2420 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
2421 Heap
->TotalFreeSize
+= FreeSize
;
2425 /* Remember flags of this free entry */
2426 RememberFlags
= FreeEntry
->Flags
;
2429 FreeSize
+= InUseEntry
->Size
;
2433 /* The next block indeed exists. Check if it's free or in use */
2434 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
) return FALSE
;
2436 /* Next entry is free, check if it can fit the block we need */
2437 FreeSize
= InUseEntry
->Size
+ FreeEntry
->Size
;
2438 if (FreeSize
< Index
) return FALSE
;
2440 /* Remember flags of this free entry */
2441 RememberFlags
= FreeEntry
->Flags
;
2443 /* Remove this block from the free list */
2444 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
2445 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
2448 PrevSize
= (InUseEntry
->Size
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2451 /* Don't produce too small blocks */
2458 /* Process extra stuff */
2459 if (RememberFlags
& HEAP_ENTRY_EXTRA_PRESENT
)
2461 /* Calculate pointers */
2462 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2463 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2466 *NewExtra
= *OldExtra
;
2472 InUseEntry
->Size
= (USHORT
)Index
;
2473 InUseEntry
->UnusedBytes
= (UCHAR
)((Index
<< HEAP_ENTRY_SHIFT
) - Size
);
2475 /* Check if there is a free space remaining after merging those blocks */
2478 /* Update flags and sizes */
2479 InUseEntry
->Flags
|= RememberFlags
& HEAP_ENTRY_LAST_ENTRY
;
2481 /* Either update previous size of the next entry or mark it as a last
2482 entry in the segment*/
2483 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2484 (InUseEntry
+ InUseEntry
->Size
)->PreviousSize
= InUseEntry
->Size
;
2488 /* Complex case, we need to split the block to give unused free space
2490 UnusedEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2491 UnusedEntry
->PreviousSize
= (USHORT
)Index
;
2492 UnusedEntry
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2494 /* Update the following block or set the last entry in the segment */
2495 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2497 /* Set flags and size */
2498 UnusedEntry
->Flags
= RememberFlags
;
2499 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2501 /* Insert it to the heap and update total size */
2502 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2503 Heap
->TotalFreeSize
+= FreeSize
;
2507 /* There is a block after this one */
2508 FollowingEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
);
2510 if (FollowingEntry
->Flags
& HEAP_ENTRY_BUSY
)
2512 /* Update flags and set size of the unused space entry */
2513 UnusedEntry
->Flags
= RememberFlags
& (~HEAP_ENTRY_LAST_ENTRY
);
2514 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2516 /* Update previous size of the following entry */
2517 FollowingEntry
->PreviousSize
= (USHORT
)FreeSize
;
2519 /* Insert it to the heap and update total free size */
2520 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2521 Heap
->TotalFreeSize
+= FreeSize
;
2525 /* That following entry is also free, what a fortune! */
2526 RememberFlags
= FollowingEntry
->Flags
;
2529 RtlpRemoveFreeBlock(Heap
, FollowingEntry
, FALSE
, FALSE
);
2530 Heap
->TotalFreeSize
-= FollowingEntry
->Size
;
2532 /* And make up a new combined block */
2533 FreeSize
+= FollowingEntry
->Size
;
2534 UnusedEntry
->Flags
= RememberFlags
;
2536 /* Check where to put it */
2537 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2539 /* Fine for a dedicated list */
2540 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2542 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2543 ((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
)->PreviousSize
= (USHORT
)FreeSize
;
2545 /* Insert it back and update total size */
2546 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2547 Heap
->TotalFreeSize
+= FreeSize
;
2551 /* The block is very large, leave all the hassle to the insertion routine */
2552 RtlpInsertFreeBlock(Heap
, UnusedEntry
, FreeSize
);
2558 /* Copy user settable flags */
2559 InUseEntry
->Flags
&= ~HEAP_ENTRY_SETTABLE_FLAGS
;
2560 InUseEntry
->Flags
|= ((Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4);
2562 /* Properly "zero out" (and fill!) the space */
2563 if (Flags
& HEAP_ZERO_MEMORY
)
2565 RtlZeroMemory((PCHAR
)(InUseEntry
+ 1) + PrevSize
, Size
- PrevSize
);
2567 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2569 /* Calculate tail part which we need to fill */
2570 TailPart
= PrevSize
& (sizeof(ULONG
) - 1);
2572 /* "Invert" it as usual */
2573 if (TailPart
) TailPart
= 4 - TailPart
;
2575 if (Size
> (PrevSize
+ TailPart
))
2576 AddedSize
= (Size
- (PrevSize
+ TailPart
)) & ~(sizeof(ULONG
) - 1);
2580 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + PrevSize
+ TailPart
,
2582 ARENA_INUSE_FILLER
);
2586 /* Fill the new tail */
2587 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2589 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2594 /* Return success */
2598 PHEAP_ENTRY_EXTRA NTAPI
2599 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2601 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2603 /* Check if it's a big block */
2604 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2606 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2608 /* Return a pointer to the extra stuff*/
2609 return &VirtualEntry
->ExtraStuff
;
2613 /* This is a usual entry, which means extra stuff follows this block */
2614 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2619 /***********************************************************************
2622 * Heap [in] Handle of heap block
2623 * Flags [in] Heap reallocation flags
2624 * Ptr, [in] Address of memory to reallocate
2625 * Size [in] Number of bytes to reallocate
2628 * Pointer to reallocated memory block
2630 * 0x7d030f60--invalid flags in RtlHeapAllocate
2634 RtlReAllocateHeap(HANDLE HeapPtr
,
2639 PHEAP Heap
= (PHEAP
)HeapPtr
;
2640 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2641 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2642 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2643 BOOLEAN HeapLocked
= FALSE
;
2644 PVOID NewBaseAddress
;
2645 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2646 SIZE_T OldSize
, Index
, OldIndex
;
2650 SIZE_T RemainderBytes
, ExtraSize
;
2651 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2652 EXCEPTION_RECORD ExceptionRecord
;
2654 /* Return success in case of a null pointer */
2657 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS
);
2661 /* Force heap flags */
2662 Flags
|= Heap
->ForceFlags
;
2664 /* Call special heap */
2665 if (RtlpHeapIsSpecial(Flags
))
2666 return RtlDebugReAllocateHeap(Heap
, Flags
, Ptr
, Size
);
2668 /* Make sure size is valid */
2669 if (Size
>= 0x80000000)
2671 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2675 /* Calculate allocation size and index */
2677 AllocationSize
= Size
;
2680 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2682 /* Add up extra stuff, if it is present anywhere */
2683 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2684 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2685 Heap
->PseudoTagEntries
)
2687 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2690 /* Acquire the lock if necessary */
2691 if (!(Flags
& HEAP_NO_SERIALIZE
))
2693 RtlEnterHeapLock(Heap
->LockVariable
);
2695 Flags
&= ~HEAP_NO_SERIALIZE
;
2698 /* Get the pointer to the in-use entry */
2699 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2701 /* If that entry is not really in-use, we have a problem */
2702 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2704 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2706 /* Release the lock and return */
2708 RtlLeaveHeapLock(Heap
->LockVariable
);
2712 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2714 /* This is a virtually allocated block. Get its size */
2715 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2717 /* Convert it to an index */
2718 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2720 /* Calculate new allocation size and round it to the page size */
2721 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2722 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2727 OldIndex
= InUseEntry
->Size
;
2729 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2732 /* Calculate new index */
2733 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2735 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2736 if (Index
<= OldIndex
)
2738 /* Difference must be greater than 1, adjust if it's not so */
2739 if (Index
+ 1 == OldIndex
)
2742 AllocationSize
+= sizeof(HEAP_ENTRY
);
2745 /* Calculate new size */
2746 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2748 /* Simple in case of a virtual alloc - just an unused size */
2749 InUseEntry
->Size
= (USHORT
)(AllocationSize
- Size
);
2751 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2753 /* There is extra stuff, take it into account */
2754 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2755 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2756 *NewExtra
= *OldExtra
;
2758 // FIXME Tagging, TagIndex
2760 /* Update unused bytes count */
2761 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2765 // FIXME Tagging, SmallTagIndex
2766 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2769 /* If new size is bigger than the old size */
2772 /* Zero out that additional space if required */
2773 if (Flags
& HEAP_ZERO_MEMORY
)
2775 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2777 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2779 /* Fill it on free if required */
2780 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2783 RemainderBytes
= 4 - RemainderBytes
;
2785 if (Size
> (OldSize
+ RemainderBytes
))
2787 /* Calculate actual amount of extra bytes to fill */
2788 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2790 /* Fill them if there are any */
2793 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2795 ARENA_INUSE_FILLER
);
2801 /* Fill tail of the heap entry if required */
2802 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2804 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2809 /* Check if the difference is significant or not */
2810 if (Index
!= OldIndex
)
2813 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2815 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2817 /* This is a virtual block allocation */
2818 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2822 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2823 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2825 /* Release the memory */
2826 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2827 (PVOID
*)&DecommitBase
,
2831 if (!NT_SUCCESS(Status
))
2833 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2837 /* Otherwise reduce the commit size */
2838 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2843 /* Reduce size of the block and possibly split it */
2844 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2846 /* Initialize this entry */
2847 SplitBlock
->Flags
= FreeFlags
;
2848 SplitBlock
->PreviousSize
= (USHORT
)Index
;
2849 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2851 /* Remember free size */
2852 FreeSize
= InUseEntry
->Size
- Index
;
2855 InUseEntry
->Size
= (USHORT
)Index
;
2856 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2858 /* Is that the last entry */
2859 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2861 /* Set its size and insert it to the list */
2862 SplitBlock
->Size
= (USHORT
)FreeSize
;
2863 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2865 /* Update total free size */
2866 Heap
->TotalFreeSize
+= FreeSize
;
2870 /* Get the block after that one */
2871 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2873 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2875 /* It's in use, add it here*/
2876 SplitBlock
->Size
= (USHORT
)FreeSize
;
2878 /* Update previous size of the next entry */
2879 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2881 /* Insert it to the list */
2882 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2884 /* Update total size */
2885 Heap
->TotalFreeSize
+= FreeSize
;
2889 /* Next entry is free, so merge with it */
2890 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2892 /* Remove it, update total size */
2893 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2894 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2896 /* Calculate total free size */
2897 FreeSize
+= SplitBlock2
->Size
;
2899 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2901 SplitBlock
->Size
= (USHORT
)FreeSize
;
2903 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2905 /* Update previous size of the next entry */
2906 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2909 /* Insert the new one back and update total size */
2910 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2911 Heap
->TotalFreeSize
+= FreeSize
;
2916 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2925 /* We're growing the block */
2926 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
2927 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
2929 /* Growing in place failed, so growing out of place */
2930 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
2932 DPRINT1("Realloc in place failed, but it was the only option\n");
2937 /* Clear tag bits */
2938 Flags
&= ~HEAP_TAG_MASK
;
2940 /* Process extra stuff */
2941 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2943 /* Preserve user settable flags */
2944 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
2946 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
2948 /* Get pointer to the old extra data */
2949 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2951 /* Save tag index if it was set */
2952 if (OldExtra
->TagIndex
&&
2953 !(OldExtra
->TagIndex
& HEAP_PSEUDO_TAG_FLAG
))
2955 Flags
|= OldExtra
->TagIndex
<< HEAP_TAG_SHIFT
;
2958 else if (InUseEntry
->SmallTagIndex
)
2960 /* Take small tag index into account */
2961 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
2964 /* Allocate new block from the heap */
2965 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
2966 Flags
& ~HEAP_ZERO_MEMORY
,
2969 /* Proceed if it didn't fail */
2972 /* Get new entry pointer */
2973 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
2975 /* Process extra stuff if it exists */
2976 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2978 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
2980 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2982 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2983 NewExtra
->Settable
= OldExtra
->Settable
;
2987 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
2991 /* Copy actual user bits */
2993 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
2995 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
2997 /* Zero remaining part if required */
2998 if (Size
> OldSize
&&
2999 (Flags
& HEAP_ZERO_MEMORY
))
3001 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
3004 /* Free the old block */
3005 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
3008 Ptr
= NewBaseAddress
;
3013 /* Did resizing fail? */
3014 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
3016 /* Generate an exception if required */
3017 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
3018 ExceptionRecord
.ExceptionRecord
= NULL
;
3019 ExceptionRecord
.NumberParameters
= 1;
3020 ExceptionRecord
.ExceptionFlags
= 0;
3021 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
3023 RtlRaiseException(&ExceptionRecord
);
3026 /* Release the heap lock if it was acquired */
3028 RtlLeaveHeapLock(Heap
->LockVariable
);
3034 /***********************************************************************
3040 RtlCompactHeap(HANDLE Heap
,
3048 /***********************************************************************
3050 * Attempts to acquire the critical section object for a specified heap.
3053 * Heap [in] Handle of heap to lock for exclusive access
3062 RtlLockHeap(IN HANDLE HeapPtr
)
3064 PHEAP Heap
= (PHEAP
)HeapPtr
;
3066 // FIXME Check for special heap
3068 /* Check if it's really a heap */
3069 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3071 /* Lock if it's lockable */
3072 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3074 RtlEnterHeapLock(Heap
->LockVariable
);
3081 /***********************************************************************
3083 * Releases ownership of the critical section object.
3086 * Heap [in] Handle to the heap to unlock
3095 RtlUnlockHeap(HANDLE HeapPtr
)
3097 PHEAP Heap
= (PHEAP
)HeapPtr
;
3099 // FIXME Check for special heap
3101 /* Check if it's really a heap */
3102 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3104 /* Unlock if it's lockable */
3105 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3107 RtlLeaveHeapLock(Heap
->LockVariable
);
3114 /***********************************************************************
3117 * Heap [in] Handle of heap
3118 * Flags [in] Heap size control flags
3119 * Ptr [in] Address of memory to return size for
3122 * Size in bytes of allocated memory
3123 * 0xffffffff: Failure
3134 PHEAP Heap
= (PHEAP
)HeapPtr
;
3135 PHEAP_ENTRY HeapEntry
;
3138 // FIXME This is a hack around missing SEH support!
3141 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3146 Flags
|= Heap
->ForceFlags
;
3148 /* Call special heap */
3149 if (RtlpHeapIsSpecial(Flags
))
3150 return RtlDebugSizeHeap(Heap
, Flags
, Ptr
);
3152 /* Get the heap entry pointer */
3153 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3155 /* Return -1 if that entry is free */
3156 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3158 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3162 /* Get size of this block depending if it's a usual or a big one */
3163 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3165 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3170 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3173 /* Return calculated size */
3178 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry
)
3180 SIZE_T Size
, Result
;
3183 /* Calculate size */
3184 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3185 Size
= RtlpGetSizeOfBigBlock(HeapEntry
);
3187 Size
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3189 /* Calculate pointer to the tail part of the block */
3190 TailPart
= (PCHAR
)(HeapEntry
+ 1) + Size
;
3192 /* Compare tail pattern */
3193 Result
= RtlCompareMemory(TailPart
,
3197 if (Result
!= HEAP_ENTRY_SIZE
)
3199 DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size
, HeapEntry
, TailPart
+ Result
);
3208 RtlpValidateHeapHeaders(
3210 BOOLEAN Recalculate
)
3212 // We skip header validation for now
3217 RtlpValidateHeapEntry(
3219 PHEAP_ENTRY HeapEntry
)
3221 BOOLEAN BigAllocation
, EntryFound
= FALSE
;
3222 PHEAP_SEGMENT Segment
;
3223 ULONG SegmentOffset
;
3225 /* Perform various consistency checks of this entry */
3226 if (!HeapEntry
) goto invalid_entry
;
3227 if ((ULONG_PTR
)HeapEntry
& (HEAP_ENTRY_SIZE
- 1)) goto invalid_entry
;
3228 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
)) goto invalid_entry
;
3230 BigAllocation
= HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
;
3231 Segment
= Heap
->Segments
[HeapEntry
->SegmentOffset
];
3233 if (BigAllocation
&&
3234 (((ULONG_PTR
)HeapEntry
& (PAGE_SIZE
- 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
)))
3237 if (!BigAllocation
&& (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
||
3239 HeapEntry
< Segment
->FirstEntry
||
3240 HeapEntry
>= Segment
->LastValidEntry
))
3243 if ((HeapEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
) &&
3244 !RtlpCheckInUsePattern(HeapEntry
))
3247 /* Checks are done, if this is a virtual entry, that's all */
3248 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) return TRUE
;
3250 /* Go through segments and check if this entry fits into any of them */
3251 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3253 Segment
= Heap
->Segments
[SegmentOffset
];
3254 if (!Segment
) continue;
3256 if ((HeapEntry
>= Segment
->FirstEntry
) &&
3257 (HeapEntry
< Segment
->LastValidEntry
))
3265 /* Return our result of finding entry in the segments */
3269 DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry
, Heap
);
3274 RtlpValidateHeapSegment(
3276 PHEAP_SEGMENT Segment
,
3277 UCHAR SegmentOffset
,
3278 PULONG FreeEntriesCount
,
3279 PSIZE_T TotalFreeSize
,
3281 PSIZE_T PseudoTagEntries
)
3283 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
3284 PLIST_ENTRY UcrEntry
;
3285 SIZE_T ByteSize
, Size
, Result
;
3286 PHEAP_ENTRY CurrentEntry
;
3287 ULONG UnCommittedPages
;
3288 ULONG UnCommittedRanges
;
3291 UnCommittedPages
= 0;
3292 UnCommittedRanges
= 0;
3294 if (IsListEmpty(&Segment
->UCRSegmentList
))
3297 UcrDescriptor
= NULL
;
3301 UcrEntry
= Segment
->UCRSegmentList
.Flink
;
3302 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3305 if (Segment
->BaseAddress
== Heap
)
3306 CurrentEntry
= &Heap
->Entry
;
3308 CurrentEntry
= &Segment
->Entry
;
3310 while (CurrentEntry
< Segment
->LastValidEntry
)
3312 if (UcrDescriptor
&&
3313 ((PVOID
)CurrentEntry
>= UcrDescriptor
->Address
))
3315 DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
3316 CurrentEntry
, UcrDescriptor
->Address
,
3317 (PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3324 while (CurrentEntry
< Segment
->LastValidEntry
)
3326 if (PreviousSize
!= CurrentEntry
->PreviousSize
)
3328 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
3329 CurrentEntry
, CurrentEntry
->PreviousSize
, PreviousSize
);
3334 PreviousSize
= CurrentEntry
->Size
;
3335 Size
= CurrentEntry
->Size
<< HEAP_ENTRY_SHIFT
;
3337 if (CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
)
3344 /* Check fill pattern */
3345 if (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
)
3347 if (!RtlpCheckInUsePattern(CurrentEntry
))
3353 /* The entry is free, increase free entries count and total free size */
3354 *FreeEntriesCount
= *FreeEntriesCount
+ 1;
3355 *TotalFreeSize
+= CurrentEntry
->Size
;
3357 if ((Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
) &&
3358 (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
3360 ByteSize
= Size
- sizeof(HEAP_FREE_ENTRY
);
3362 if ((CurrentEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) &&
3363 (ByteSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
)))
3365 ByteSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
3368 Result
= RtlCompareMemoryUlong((PCHAR
)((PHEAP_FREE_ENTRY
)CurrentEntry
+ 1),
3372 if (Result
!= ByteSize
)
3374 DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
3376 (PCHAR
)(CurrentEntry
+ 1) + Result
);
3383 if (CurrentEntry
->SegmentOffset
!= SegmentOffset
)
3385 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry
, SegmentOffset
, CurrentEntry
->SegmentOffset
);
3389 /* Check if it's the last entry */
3390 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
3392 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3396 /* Check if it's not really the last one */
3397 if (CurrentEntry
!= Segment
->LastValidEntry
)
3399 DPRINT1("HEAP: Heap entry %p is not last block in segment (%x)\n", CurrentEntry
, Segment
->LastValidEntry
);
3403 else if (CurrentEntry
!= UcrDescriptor
->Address
)
3405 DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
3406 CurrentEntry
, UcrDescriptor
->Address
);
3412 UnCommittedPages
+= (UcrDescriptor
->Size
/ PAGE_SIZE
);
3413 UnCommittedRanges
++;
3415 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3417 /* Go to the next UCR descriptor */
3418 UcrEntry
= UcrEntry
->Flink
;
3419 if (UcrEntry
== &Segment
->UCRSegmentList
)
3422 UcrDescriptor
= NULL
;
3426 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3433 /* Advance to the next entry */
3434 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3438 /* Check total numbers of UCP and UCR */
3439 if (Segment
->NumberOfUnCommittedPages
!= UnCommittedPages
)
3441 DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
3442 Segment
, Segment
->NumberOfUnCommittedPages
, UnCommittedPages
);
3447 if (Segment
->NumberOfUnCommittedRanges
!= UnCommittedRanges
)
3449 DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
3450 Segment
, Segment
->NumberOfUnCommittedRanges
, UnCommittedRanges
);
3459 RtlpValidateHeap(PHEAP Heap
,
3460 BOOLEAN ForceValidation
)
3462 PHEAP_SEGMENT Segment
;
3464 UCHAR SegmentOffset
;
3465 SIZE_T Size
, TotalFreeSize
;
3467 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
3468 PLIST_ENTRY ListHead
, NextEntry
;
3469 PHEAP_FREE_ENTRY FreeEntry
;
3470 ULONG FreeBlocksCount
, FreeListEntriesCount
;
3473 if (!RtlpValidateHeapHeaders(Heap
, FALSE
))
3476 /* Skip validation if it's not needed */
3477 if (!ForceValidation
&& !(Heap
->Flags
& HEAP_VALIDATE_ALL_ENABLED
))
3480 /* Check free lists bitmaps */
3481 FreeListEntriesCount
= 0;
3482 ListHead
= &Heap
->FreeLists
[0];
3484 for (Size
= 0; Size
< HEAP_FREELISTS
; Size
++)
3488 /* This is a dedicated list. Check if it's empty */
3489 EmptyList
= IsListEmpty(ListHead
);
3491 if (Heap
->u
.FreeListsInUseBytes
[Size
>> 3] & (1 << (Size
& 7)))
3495 DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size
);
3503 DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size
);
3509 /* Now check this list entries */
3510 NextEntry
= ListHead
->Flink
;
3513 while (ListHead
!= NextEntry
)
3515 FreeEntry
= CONTAINING_RECORD(NextEntry
, HEAP_FREE_ENTRY
, FreeList
);
3516 NextEntry
= NextEntry
->Flink
;
3518 /* If there is an in-use entry in a free list - that's quite a big problem */
3519 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
)
3521 DPRINT1("HEAP: %x-dedicated list free element %x is marked in-use\n", Size
, FreeEntry
);
3525 /* Check sizes according to that specific list's size */
3526 if ((Size
== 0) && (FreeEntry
->Size
< HEAP_FREELISTS
))
3528 DPRINT1("HEAP: Non dedicated list free element %x has size %x which would fit a dedicated list\n", FreeEntry
, FreeEntry
->Size
);
3531 else if (Size
&& (FreeEntry
->Size
!= Size
))
3533 DPRINT1("HEAP: %x-dedicated list free element %x has incorrect size %x\n", Size
, FreeEntry
, FreeEntry
->Size
);
3536 else if ((Size
== 0) && (FreeEntry
->Size
< PreviousSize
))
3538 DPRINT1("HEAP: Non dedicated list free element %x is not put in order\n", FreeEntry
);
3542 /* Remember previous size*/
3543 PreviousSize
= FreeEntry
->Size
;
3545 /* Add up to the total amount of free entries */
3546 FreeListEntriesCount
++;
3549 /* Go to the head of the next free list */
3553 /* Check big allocations */
3554 ListHead
= &Heap
->VirtualAllocdBlocks
;
3555 NextEntry
= ListHead
->Flink
;
3557 while (ListHead
!= NextEntry
)
3559 VirtualAllocBlock
= CONTAINING_RECORD(NextEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
3561 /* We can only check the fill pattern */
3562 if (VirtualAllocBlock
->BusyBlock
.Flags
& HEAP_ENTRY_FILL_PATTERN
)
3564 if (!RtlpCheckInUsePattern(&VirtualAllocBlock
->BusyBlock
))
3568 NextEntry
= NextEntry
->Flink
;
3571 /* Check all segments */
3572 FreeBlocksCount
= 0;
3575 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3577 Segment
= Heap
->Segments
[SegmentOffset
];
3579 /* Go to the next one if there is no segment */
3580 if (!Segment
) continue;
3582 if (!RtlpValidateHeapSegment(Heap
,
3594 if (FreeListEntriesCount
!= FreeBlocksCount
)
3596 DPRINT1("HEAP: Free blocks count in arena (%d) does not match free blocks number in the free lists (%d)\n", FreeBlocksCount
, FreeListEntriesCount
);
3600 if (Heap
->TotalFreeSize
!= TotalFreeSize
)
3602 DPRINT1("HEAP: Total size of free blocks in arena (%d) does not equal to the one in heap header (%d)\n", TotalFreeSize
, Heap
->TotalFreeSize
);
3609 /***********************************************************************
3611 * Validates a specified heap.
3614 * Heap [in] Handle to the heap
3615 * Flags [in] Bit flags that control access during operation
3616 * Block [in] Optional pointer to memory block to validate
3627 BOOLEAN NTAPI
RtlValidateHeap(
3633 PHEAP Heap
= (PHEAP
)HeapPtr
;
3634 BOOLEAN HeapLocked
= FALSE
;
3637 /* Check for page heap */
3638 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
)
3639 return RtlpDebugPageHeapValidate(HeapPtr
, Flags
, Block
);
3641 /* Check signature */
3642 if (Heap
->Signature
!= HEAP_SIGNATURE
)
3644 DPRINT1("HEAP: Signature %x is invalid for heap %p\n", Heap
->Signature
, Heap
);
3649 Flags
= Heap
->ForceFlags
;
3651 /* Acquire the lock if necessary */
3652 if (!(Flags
& HEAP_NO_SERIALIZE
))
3654 RtlEnterHeapLock(Heap
->LockVariable
);
3658 /* Either validate whole heap or just one entry */
3660 HeapValid
= RtlpValidateHeap(Heap
, TRUE
);
3662 HeapValid
= RtlpValidateHeapEntry(Heap
, (PHEAP_ENTRY
)Block
- 1);
3664 /* Unlock if it's lockable */
3667 RtlLeaveHeapLock(Heap
->LockVariable
);
3674 RtlInitializeHeapManager(VOID
)
3679 Peb
= RtlGetCurrentPeb();
3681 /* Initialize heap-related fields of PEB */
3682 Peb
->NumberOfHeaps
= 0;
3684 /* Initialize the process heaps list protecting lock */
3685 RtlInitializeHeapLock(&RtlpProcessHeapsListLock
);
3693 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3697 return STATUS_NOT_IMPLEMENTED
;
3705 RtlGetProcessHeaps(ULONG count
,
3717 RtlValidateProcessHeaps(VOID
)
3729 IN PVOID HeapHandle
,
3742 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3744 IN PVOID BaseAddress
,
3747 PHEAP Heap
= (PHEAP
)HeapHandle
;
3748 PHEAP_ENTRY HeapEntry
;
3749 PHEAP_ENTRY_EXTRA Extra
;
3750 BOOLEAN HeapLocked
= FALSE
;
3753 Flags
|= Heap
->Flags
;
3755 /* Call special heap */
3756 if (RtlpHeapIsSpecial(Flags
))
3757 return RtlDebugSetUserValueHeap(Heap
, Flags
, BaseAddress
, UserValue
);
3759 /* Lock if it's lockable */
3760 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3762 RtlEnterHeapLock(Heap
->LockVariable
);
3766 /* Get a pointer to the entry */
3767 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3769 /* If it's a free entry - return error */
3770 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3772 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3774 /* Release the heap lock if it was acquired */
3776 RtlLeaveHeapLock(Heap
->LockVariable
);
3781 /* Check if this entry has an extra stuff associated with it */
3782 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3784 /* Use extra to store the value */
3785 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3786 Extra
->Settable
= (ULONG_PTR
)UserValue
;
3789 /* Release the heap lock if it was acquired */
3791 RtlLeaveHeapLock(Heap
->LockVariable
);
3801 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3803 IN PVOID BaseAddress
,
3804 IN ULONG UserFlagsReset
,
3805 IN ULONG UserFlagsSet
)
3807 PHEAP Heap
= (PHEAP
)HeapHandle
;
3808 PHEAP_ENTRY HeapEntry
;
3809 BOOLEAN HeapLocked
= FALSE
;
3812 Flags
|= Heap
->Flags
;
3814 /* Call special heap */
3815 if (RtlpHeapIsSpecial(Flags
))
3816 return RtlDebugSetUserFlagsHeap(Heap
, Flags
, BaseAddress
, UserFlagsReset
, UserFlagsSet
);
3818 /* Lock if it's lockable */
3819 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3821 RtlEnterHeapLock(Heap
->LockVariable
);
3825 /* Get a pointer to the entry */
3826 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3828 /* If it's a free entry - return error */
3829 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3831 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3833 /* Release the heap lock if it was acquired */
3835 RtlLeaveHeapLock(Heap
->LockVariable
);
3840 /* Set / reset flags */
3841 HeapEntry
->Flags
&= ~(UserFlagsReset
>> 4);
3842 HeapEntry
->Flags
|= (UserFlagsSet
>> 4);
3844 /* Release the heap lock if it was acquired */
3846 RtlLeaveHeapLock(Heap
->LockVariable
);
3856 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3858 IN PVOID BaseAddress
,
3859 OUT PVOID
*UserValue
,
3860 OUT PULONG UserFlags
)
3862 PHEAP Heap
= (PHEAP
)HeapHandle
;
3863 PHEAP_ENTRY HeapEntry
;
3864 PHEAP_ENTRY_EXTRA Extra
;
3865 BOOLEAN HeapLocked
= FALSE
;
3868 Flags
|= Heap
->Flags
;
3870 /* Call special heap */
3871 if (RtlpHeapIsSpecial(Flags
))
3872 return RtlDebugGetUserInfoHeap(Heap
, Flags
, BaseAddress
, UserValue
, UserFlags
);
3874 /* Lock if it's lockable */
3875 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3877 RtlEnterHeapLock(Heap
->LockVariable
);
3881 /* Get a pointer to the entry */
3882 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3884 /* If it's a free entry - return error */
3885 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3887 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3889 /* Release the heap lock if it was acquired */
3891 RtlLeaveHeapLock(Heap
->LockVariable
);
3896 /* Check if this entry has an extra stuff associated with it */
3897 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3899 /* Get pointer to extra data */
3900 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3902 /* Pass user value */
3904 *UserValue
= (PVOID
)Extra
->Settable
;
3906 /* Decode and return user flags */
3908 *UserFlags
= (HeapEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4;
3911 /* Release the heap lock if it was acquired */
3913 RtlLeaveHeapLock(Heap
->LockVariable
);
3923 RtlUsageHeap(IN HANDLE Heap
,
3925 OUT PRTL_HEAP_USAGE Usage
)
3929 return STATUS_NOT_IMPLEMENTED
;
3934 RtlQueryTagHeap(IN PVOID HeapHandle
,
3937 IN BOOLEAN ResetCounters
,
3938 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
3947 RtlExtendHeap(IN HANDLE Heap
,
3959 RtlCreateTagHeap(IN HANDLE HeapHandle
,
3962 IN PWSTR TagSubName
)
3971 RtlWalkHeap(IN HANDLE HeapHandle
,
3975 return STATUS_NOT_IMPLEMENTED
;
3980 RtlProtectHeap(IN PVOID HeapHandle
,
3981 IN BOOLEAN ReadOnly
)
3989 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
3990 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
3991 IN PVOID HeapInformation
,
3992 IN SIZE_T HeapInformationLength
)
3994 /* Setting heap information is not really supported except for enabling LFH */
3995 if (HeapInformationClass
== 0) return STATUS_SUCCESS
;
3997 /* Check buffer length */
3998 if (HeapInformationLength
< sizeof(ULONG
))
4000 /* The provided buffer is too small */
4001 return STATUS_BUFFER_TOO_SMALL
;
4004 /* Check for a special magic value for enabling LFH */
4005 if (*(PULONG
)HeapInformation
== 2)
4007 DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
4008 return STATUS_SUCCESS
;
4011 return STATUS_UNSUCCESSFUL
;
4016 RtlQueryHeapInformation(HANDLE HeapHandle
,
4017 HEAP_INFORMATION_CLASS HeapInformationClass
,
4018 PVOID HeapInformation OPTIONAL
,
4019 SIZE_T HeapInformationLength OPTIONAL
,
4020 PSIZE_T ReturnLength OPTIONAL
)
4022 PHEAP Heap
= (PHEAP
)HeapHandle
;
4024 /* Only HeapCompatibilityInformation is supported */
4025 if (HeapInformationClass
!= HeapCompatibilityInformation
)
4026 return STATUS_UNSUCCESSFUL
;
4028 /* Set result length */
4029 if (ReturnLength
) *ReturnLength
= sizeof(ULONG
);
4031 /* Check buffer length */
4032 if (HeapInformationLength
< sizeof(ULONG
))
4034 /* It's too small, return needed length */
4035 return STATUS_BUFFER_TOO_SMALL
;
4038 /* Return front end heap type */
4039 *(PULONG
)HeapInformation
= Heap
->FrontEndHeapType
;
4041 return STATUS_SUCCESS
;
4046 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
4058 RtlMultipleFreeHeap(IN PVOID HeapHandle
,