2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: RTL Heap backend allocator
6 * PROGRAMMERS: Copyright 2010 Aleksey Bragin
10 http://msdn.microsoft.com/en-us/library/ms810466.aspx
11 http://msdn.microsoft.com/en-us/library/ms810603.aspx
12 http://www.securitylab.ru/analytics/216376.php
13 http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
14 http://www.phreedom.org/research/exploits/asn1-bitstring/
15 http://illmatics.com/Understanding_the_LFH.pdf
16 http://www.alex-ionescu.com/?p=18
19 /* INCLUDES *****************************************************************/
29 /* How many least significant bits are clear */
30 UCHAR RtlpBitsClearLow
[] =
32 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
33 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
34 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
35 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
36 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
37 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
38 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
39 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
40 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
41 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
42 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
43 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
44 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
45 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
46 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
47 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(OUT PHEAP Heap
,
88 IN PHEAP_LOCK Lock OPTIONAL
,
89 IN PRTL_HEAP_PARAMETERS Parameters
)
95 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
99 ASSERT(Parameters
!= NULL
);
100 ASSERT(!(Flags
& HEAP_LOCK_USER_ALLOCATED
));
101 ASSERT(!(Flags
& HEAP_NO_SERIALIZE
) || (Lock
== NULL
)); /* HEAP_NO_SERIALIZE => no lock */
103 /* Start out with the size of a plain Heap header */
104 HeaderSize
= ROUND_UP(sizeof(HEAP
), sizeof(HEAP_ENTRY
));
106 /* Check if space needs to be added for the Heap Lock */
107 if (!(Flags
& HEAP_NO_SERIALIZE
))
110 /* The user manages the Heap Lock */
111 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
113 if (RtlpGetMode() == UserMode
)
115 /* In user mode, the Heap Lock trails the Heap header */
116 Lock
= (PHEAP_LOCK
) ((ULONG_PTR
) (Heap
) + HeaderSize
);
117 HeaderSize
+= ROUND_UP(sizeof(HEAP_LOCK
), sizeof(HEAP_ENTRY
));
121 /* Add space for the initial Heap UnCommitted Range Descriptor list */
122 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
) ((ULONG_PTR
) (Heap
) + HeaderSize
);
123 HeaderSize
+= ROUND_UP(NumUCRs
* sizeof(HEAP_UCR_DESCRIPTOR
), sizeof(HEAP_ENTRY
));
126 ASSERT(HeaderSize
<= PAGE_SIZE
);
128 /* Initialise the Heap Entry header containing the Heap header */
129 Heap
->Entry
.Size
= (USHORT
)(HeaderSize
>> HEAP_ENTRY_SHIFT
);
130 Heap
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
131 Heap
->Entry
.SmallTagIndex
= LOBYTE(Heap
->Entry
.Size
) ^ HIBYTE(Heap
->Entry
.Size
) ^ Heap
->Entry
.Flags
;
132 Heap
->Entry
.PreviousSize
= 0;
133 Heap
->Entry
.SegmentOffset
= 0;
134 Heap
->Entry
.UnusedBytes
= 0;
136 /* Initialise the Heap header */
137 Heap
->Signature
= HEAP_SIGNATURE
;
139 Heap
->ForceFlags
= (Flags
& (HEAP_NO_SERIALIZE
|
140 HEAP_GENERATE_EXCEPTIONS
|
142 HEAP_REALLOC_IN_PLACE_ONLY
|
143 HEAP_VALIDATE_PARAMETERS_ENABLED
|
144 HEAP_VALIDATE_ALL_ENABLED
|
145 HEAP_TAIL_CHECKING_ENABLED
|
146 HEAP_CREATE_ALIGN_16
|
147 HEAP_FREE_CHECKING_ENABLED
));
149 /* Initialise the Heap parameters */
150 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, sizeof(HEAP_ENTRY
)) >> HEAP_ENTRY_SHIFT
;
151 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
152 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
153 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
154 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
155 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
156 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
158 /* Initialise the Heap validation info */
159 Heap
->HeaderValidateCopy
= NULL
;
160 Heap
->HeaderValidateLength
= (USHORT
)HeaderSize
;
162 /* Initialise the Heap Lock */
163 if (!(Flags
& HEAP_NO_SERIALIZE
) && !(Flags
& HEAP_LOCK_USER_ALLOCATED
))
165 Status
= RtlInitializeHeapLock(&Lock
);
166 if (!NT_SUCCESS(Status
))
169 Heap
->LockVariable
= Lock
;
171 /* Initialise the Heap alignment info */
172 if (Flags
& HEAP_CREATE_ALIGN_16
)
174 Heap
->AlignMask
= (ULONG
) ~15;
175 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
179 Heap
->AlignMask
= (ULONG
) ~(sizeof(HEAP_ENTRY
) - 1);
180 Heap
->AlignRound
= 2 * sizeof(HEAP_ENTRY
) - 1;
183 if (Flags
& HEAP_TAIL_CHECKING_ENABLED
)
184 Heap
->AlignRound
+= sizeof(HEAP_ENTRY
);
186 /* Initialise the Heap Segment list */
187 for (Index
= 0; Index
< HEAP_SEGMENTS
; ++Index
)
188 Heap
->Segments
[Index
] = NULL
;
190 /* Initialise the Heap Free Heap Entry lists */
191 for (Index
= 0; Index
< HEAP_FREELISTS
; ++Index
)
192 InitializeListHead(&Heap
->FreeLists
[Index
]);
194 /* Initialise the Heap Virtual Allocated Blocks list */
195 InitializeListHead(&Heap
->VirtualAllocdBlocks
);
197 /* Initialise the Heap UnCommitted Region lists */
198 InitializeListHead(&Heap
->UCRSegments
);
199 InitializeListHead(&Heap
->UCRList
);
201 /* Register the initial Heap UnCommitted Region Descriptors */
202 for (Index
= 0; Index
< NumUCRs
; ++Index
)
203 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
[Index
].ListEntry
);
205 return STATUS_SUCCESS
;
210 RtlpSetFreeListsBit(PHEAP Heap
,
211 PHEAP_FREE_ENTRY FreeEntry
)
215 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
217 /* Calculate offset in the free list bitmap */
218 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
219 Bit
= 1 << (FreeEntry
->Size
& 7);
221 /* Assure it's not already set */
222 ASSERT((Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
) == 0);
225 Heap
->u
.FreeListsInUseBytes
[Index
] |= Bit
;
230 RtlpClearFreeListsBit(PHEAP Heap
,
231 PHEAP_FREE_ENTRY FreeEntry
)
235 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
237 /* Calculate offset in the free list bitmap */
238 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
239 Bit
= 1 << (FreeEntry
->Size
& 7);
241 /* Assure it was set and the corresponding free list is empty */
242 ASSERT(Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
);
243 ASSERT(IsListEmpty(&Heap
->FreeLists
[FreeEntry
->Size
]));
246 Heap
->u
.FreeListsInUseBytes
[Index
] ^= Bit
;
250 RtlpInsertFreeBlockHelper(PHEAP Heap
,
251 PHEAP_FREE_ENTRY FreeEntry
,
255 PLIST_ENTRY FreeListHead
, Current
;
256 PHEAP_FREE_ENTRY CurrentEntry
;
258 ASSERT(FreeEntry
->Size
== BlockSize
);
260 /* Fill if it's not denied */
263 FreeEntry
->Flags
&= ~(HEAP_ENTRY_FILL_PATTERN
|
264 HEAP_ENTRY_EXTRA_PRESENT
|
267 if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
269 RtlFillMemoryUlong((PCHAR
)(FreeEntry
+ 1),
270 (BlockSize
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
),
273 FreeEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
278 /* Clear out all flags except the last entry one */
279 FreeEntry
->Flags
&= HEAP_ENTRY_LAST_ENTRY
;
282 /* Insert it either into dedicated or non-dedicated list */
283 if (BlockSize
< HEAP_FREELISTS
)
286 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
288 if (IsListEmpty(FreeListHead
))
290 RtlpSetFreeListsBit(Heap
, FreeEntry
);
295 /* Non-dedicated one */
296 FreeListHead
= &Heap
->FreeLists
[0];
297 Current
= FreeListHead
->Flink
;
299 /* Find a position where to insert it to (the list must be sorted) */
300 while (FreeListHead
!= Current
)
302 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
304 if (BlockSize
<= CurrentEntry
->Size
)
307 Current
= Current
->Flink
;
310 FreeListHead
= Current
;
313 /* Actually insert it into the list */
314 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
318 RtlpInsertFreeBlock(PHEAP Heap
,
319 PHEAP_FREE_ENTRY FreeEntry
,
322 USHORT Size
, PreviousSize
;
323 UCHAR SegmentOffset
, Flags
;
324 PHEAP_SEGMENT Segment
;
326 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
328 /* Increase the free size counter */
329 Heap
->TotalFreeSize
+= BlockSize
;
331 /* Remember certain values */
332 Flags
= FreeEntry
->Flags
;
333 PreviousSize
= FreeEntry
->PreviousSize
;
334 SegmentOffset
= FreeEntry
->SegmentOffset
;
335 Segment
= Heap
->Segments
[SegmentOffset
];
340 /* Check for the max size */
341 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
343 Size
= HEAP_MAX_BLOCK_SIZE
;
345 /* Special compensation if it goes above limit just by 1 */
346 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
349 FreeEntry
->Flags
= 0;
353 Size
= (USHORT
)BlockSize
;
354 FreeEntry
->Flags
= Flags
;
357 /* Change its size and insert it into a free list */
358 FreeEntry
->Size
= Size
;
359 FreeEntry
->PreviousSize
= PreviousSize
;
360 FreeEntry
->SegmentOffset
= SegmentOffset
;
362 /* Call a helper to actually insert the block */
363 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
369 /* Go to the next entry */
370 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
372 /* Check if that's all */
373 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
376 /* Update previous size if needed */
377 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
378 FreeEntry
->PreviousSize
= PreviousSize
;
382 RtlpRemoveFreeBlock(PHEAP Heap
,
383 PHEAP_FREE_ENTRY FreeEntry
,
387 SIZE_T Result
, RealSize
;
389 /* Remove the free block and update the freelists bitmap */
390 if (RemoveEntryList(&FreeEntry
->FreeList
) &&
391 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
393 RtlpClearFreeListsBit(Heap
, FreeEntry
);
396 /* Fill with pattern if necessary */
398 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
400 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
402 /* Deduct extra stuff from block's real size */
403 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
404 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
406 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
409 /* Check if the free filler is intact */
410 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
414 if (Result
!= RealSize
)
416 DPRINT1("Free heap block %p modified at %p after it was freed\n",
418 (PCHAR
)(FreeEntry
+ 1) + Result
);
424 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
426 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
428 /* Get pointer to the containing record */
429 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
430 ASSERT(VirtualEntry
->BusyBlock
.Size
>= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
));
432 /* Restore the real size */
433 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
436 PHEAP_UCR_DESCRIPTOR NTAPI
437 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
440 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
441 PHEAP_UCR_SEGMENT UcrSegment
;
442 PHEAP Heap
= Segment
->Heap
;
443 SIZE_T ReserveSize
= 16 * PAGE_SIZE
;
444 SIZE_T CommitSize
= 1 * PAGE_SIZE
;
447 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
449 /* Check if we have unused UCRs */
450 if (IsListEmpty(&Heap
->UCRList
))
452 /* Get a pointer to the first UCR segment */
453 UcrSegment
= CONTAINING_RECORD(Heap
->UCRSegments
.Flink
, HEAP_UCR_SEGMENT
, ListEntry
);
455 /* Check the list of UCR segments */
456 if (IsListEmpty(&Heap
->UCRSegments
) ||
457 UcrSegment
->ReservedSize
== UcrSegment
->CommittedSize
)
459 /* We need to create a new one. Reserve 16 pages for it */
461 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
462 (PVOID
*)&UcrSegment
,
468 if (!NT_SUCCESS(Status
)) return NULL
;
470 /* Commit one page */
471 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
472 (PVOID
*)&UcrSegment
,
478 if (!NT_SUCCESS(Status
))
480 /* Release reserved memory */
481 ZwFreeVirtualMemory(NtCurrentProcess(),
482 (PVOID
*)&UcrSegment
,
489 UcrSegment
->ReservedSize
= ReserveSize
;
490 UcrSegment
->CommittedSize
= CommitSize
;
492 /* Add it to the head of the list */
493 InsertHeadList(&Heap
->UCRSegments
, &UcrSegment
->ListEntry
);
495 /* Get a pointer to the first available UCR descriptor */
496 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)(UcrSegment
+ 1);
500 /* It's possible to use existing UCR segment. Commit one more page */
501 UcrDescriptor
= (PHEAP_UCR_DESCRIPTOR
)((PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
);
502 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
503 (PVOID
*)&UcrDescriptor
,
509 if (!NT_SUCCESS(Status
)) return NULL
;
512 UcrSegment
->CommittedSize
+= CommitSize
;
515 /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
516 while ((PCHAR
)(UcrDescriptor
+ 1) <= (PCHAR
)UcrSegment
+ UcrSegment
->CommittedSize
)
518 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
523 /* There are unused UCRs, just get the first one */
524 Entry
= RemoveHeadList(&Heap
->UCRList
);
525 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
526 return UcrDescriptor
;
530 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
531 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
534 UcrDescriptor
->Address
= NULL
;
535 UcrDescriptor
->Size
= 0;
537 /* Put it into the heap's list of unused UCRs */
538 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
542 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
547 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
549 DPRINT("RtlpInsertUnCommittedPages(%p %08Ix %Ix)\n", Segment
, Address
, Size
);
551 /* Go through the list of UCR descriptors, they are sorted from lowest address
553 Current
= Segment
->UCRSegmentList
.Flink
;
554 while (Current
!= &Segment
->UCRSegmentList
)
556 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
558 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
560 /* Check for a really lucky case */
561 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
564 UcrDescriptor
->Address
= (PVOID
)Address
;
565 UcrDescriptor
->Size
+= Size
;
569 /* We found the block before which the new one should go */
572 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
574 /* Modify this entry */
575 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
576 Size
+= UcrDescriptor
->Size
;
578 /* Advance to the next descriptor */
579 Current
= Current
->Flink
;
581 /* Remove the current descriptor from the list and destroy it */
582 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
583 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
585 Segment
->NumberOfUnCommittedRanges
--;
589 /* Advance to the next descriptor */
590 Current
= Current
->Flink
;
594 /* Create a new UCR descriptor */
595 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
596 if (!UcrDescriptor
) return;
598 UcrDescriptor
->Address
= (PVOID
)Address
;
599 UcrDescriptor
->Size
= Size
;
601 /* "Current" is the descriptor before which our one should go */
602 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
604 DPRINT("Added segment UCR with base %08Ix, size 0x%x\n", Address
, Size
);
606 /* Increase counters */
607 Segment
->NumberOfUnCommittedRanges
++;
610 PHEAP_FREE_ENTRY NTAPI
611 RtlpFindAndCommitPages(PHEAP Heap
,
612 PHEAP_SEGMENT Segment
,
614 PVOID AddressRequested
)
617 ULONG_PTR Address
= 0;
618 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
619 PHEAP_ENTRY FirstEntry
, LastEntry
;
622 DPRINT("RtlpFindAndCommitPages(%p %p %Ix %08Ix)\n", Heap
, Segment
, *Size
, Address
);
624 /* Go through UCRs in a segment */
625 Current
= Segment
->UCRSegmentList
.Flink
;
626 while (Current
!= &Segment
->UCRSegmentList
)
628 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
630 /* Check if we can use that one right away */
631 if (UcrDescriptor
->Size
>= *Size
&&
632 (UcrDescriptor
->Address
== AddressRequested
|| !AddressRequested
))
634 /* Get the address */
635 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
638 if (Heap
->CommitRoutine
)
640 Status
= Heap
->CommitRoutine(Heap
, (PVOID
*)&Address
, Size
);
644 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
652 DPRINT("Committed %Iu bytes at base %08Ix, UCR size is %lu\n", *Size
, Address
, UcrDescriptor
->Size
);
654 /* Fail in unsuccessful case */
655 if (!NT_SUCCESS(Status
))
657 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
661 /* Update tracking numbers */
662 Segment
->NumberOfUnCommittedPages
-= (ULONG
)(*Size
/ PAGE_SIZE
);
664 /* Calculate first and last entries */
665 FirstEntry
= (PHEAP_ENTRY
)Address
;
667 /* Go through the entries to find the last one */
669 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
671 LastEntry
= &Segment
->Entry
;
673 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
675 ASSERT(LastEntry
->Size
!= 0);
676 LastEntry
+= LastEntry
->Size
;
678 ASSERT((LastEntry
+ LastEntry
->Size
) == FirstEntry
);
680 /* Unmark it as a last entry */
681 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
683 /* Update UCR descriptor */
684 UcrDescriptor
->Address
= (PVOID
)((ULONG_PTR
)UcrDescriptor
->Address
+ *Size
);
685 UcrDescriptor
->Size
-= *Size
;
687 DPRINT("Updating UcrDescriptor %p, new Address %p, size %lu\n",
688 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
690 /* Set various first entry fields */
691 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
692 FirstEntry
->Size
= (USHORT
)(*Size
>> HEAP_ENTRY_SHIFT
);
693 FirstEntry
->PreviousSize
= LastEntry
->Size
;
695 /* Check if anything left in this UCR */
696 if (UcrDescriptor
->Size
== 0)
698 /* It's fully exhausted */
700 /* Check if this is the end of the segment */
701 if(UcrDescriptor
->Address
== Segment
->LastValidEntry
)
703 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
707 FirstEntry
->Flags
= 0;
708 /* Update field of next entry */
709 ASSERT((FirstEntry
+ FirstEntry
->Size
)->PreviousSize
== 0);
710 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
713 /* This UCR needs to be removed because it became useless */
714 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
716 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
717 Segment
->NumberOfUnCommittedRanges
--;
721 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
725 return (PHEAP_FREE_ENTRY
)FirstEntry
;
728 /* Advance to the next descriptor */
729 PreviousUcr
= UcrDescriptor
;
730 Current
= Current
->Flink
;
737 RtlpDeCommitFreeBlock(PHEAP Heap
,
738 PHEAP_FREE_ENTRY FreeEntry
,
741 PHEAP_SEGMENT Segment
;
742 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
743 PHEAP_FREE_ENTRY NextFreeEntry
;
744 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
745 SIZE_T PrecedingSize
, NextSize
, DecommitSize
;
746 ULONG_PTR DecommitBase
;
749 DPRINT("Decommitting %p %p %x\n", Heap
, FreeEntry
, Size
);
751 /* We can't decommit if there is a commit routine! */
752 if (Heap
->CommitRoutine
)
754 /* Just add it back the usual way */
755 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
759 /* Get the segment */
760 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
762 /* Get the preceding entry */
763 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
764 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
766 if (PrecedingSize
== 1)
768 /* Just 1 heap entry, increase the base/size */
769 DecommitBase
+= PAGE_SIZE
;
770 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
772 else if (FreeEntry
->PreviousSize
&&
773 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
775 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
778 /* Get the next entry */
779 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
780 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
781 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
785 /* Just 1 heap entry, increase the size */
786 DecommitSize
-= PAGE_SIZE
;
787 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
789 else if (NextSize
== 0 &&
790 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
792 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
795 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
797 /* Calculate real decommit size */
798 if (DecommitSize
> DecommitBase
)
800 DecommitSize
-= DecommitBase
;
804 /* Nothing to decommit */
805 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
809 /* A decommit is necessary. Create a UCR descriptor */
810 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
813 DPRINT1("HEAP: Failed to create UCR descriptor\n");
814 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
818 /* Decommit the memory */
819 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
820 (PVOID
*)&DecommitBase
,
824 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
825 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
827 if (!NT_SUCCESS(Status
))
829 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
833 /* Insert uncommitted pages */
834 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
835 Segment
->NumberOfUnCommittedPages
+= (ULONG
)(DecommitSize
/ PAGE_SIZE
);
839 /* Adjust size of this free entry and insert it */
840 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
841 FreeEntry
->Size
= (USHORT
)PrecedingSize
;
842 Heap
->TotalFreeSize
+= PrecedingSize
;
844 /* Insert it into the free list */
845 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
, FALSE
);
847 else if (PrecedingInUseEntry
)
849 /* Adjust preceding in use entry */
850 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
853 /* Now the next one */
856 /* Adjust size of this free entry and insert it */
857 NextFreeEntry
->Flags
= 0;
858 NextFreeEntry
->PreviousSize
= 0;
859 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
860 NextFreeEntry
->Size
= (USHORT
)NextSize
;
862 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= (USHORT
)NextSize
;
864 Heap
->TotalFreeSize
+= NextSize
;
865 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
, FALSE
);
867 else if (NextInUseEntry
)
869 NextInUseEntry
->PreviousSize
= 0;
875 RtlpInitializeHeapSegment(IN OUT PHEAP Heap
,
876 OUT PHEAP_SEGMENT Segment
,
877 IN UCHAR SegmentIndex
,
878 IN ULONG SegmentFlags
,
879 IN SIZE_T SegmentReserve
,
880 IN SIZE_T SegmentCommit
)
882 PHEAP_ENTRY HeapEntry
;
885 ASSERT(Heap
!= NULL
);
886 ASSERT(Segment
!= NULL
);
887 ASSERT(SegmentCommit
>= PAGE_SIZE
);
888 ASSERT(ROUND_DOWN(SegmentCommit
, PAGE_SIZE
) == SegmentCommit
);
889 ASSERT(SegmentReserve
>= SegmentCommit
);
890 ASSERT(ROUND_DOWN(SegmentReserve
, PAGE_SIZE
) == SegmentReserve
);
892 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap
, Segment
, SegmentIndex
, SegmentFlags
, SegmentReserve
, SegmentCommit
);
894 /* Initialise the Heap Entry header if this is not the first Heap Segment */
895 if ((PHEAP_SEGMENT
) (Heap
) != Segment
)
897 Segment
->Entry
.Size
= ROUND_UP(sizeof(HEAP_SEGMENT
), sizeof(HEAP_ENTRY
)) >> HEAP_ENTRY_SHIFT
;
898 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
899 Segment
->Entry
.SmallTagIndex
= LOBYTE(Segment
->Entry
.Size
) ^ HIBYTE(Segment
->Entry
.Size
) ^ Segment
->Entry
.Flags
;
900 Segment
->Entry
.PreviousSize
= 0;
901 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
902 Segment
->Entry
.UnusedBytes
= 0;
906 ASSERT((Segment
->Entry
.Size
<< HEAP_ENTRY_SHIFT
) <= PAGE_SIZE
);
908 /* Initialise the Heap Segment header */
909 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
910 Segment
->SegmentFlags
= SegmentFlags
;
911 Segment
->Heap
= Heap
;
912 Heap
->Segments
[SegmentIndex
] = Segment
;
914 /* Initialise the Heap Segment location information */
915 Segment
->BaseAddress
= Segment
;
916 Segment
->NumberOfPages
= (ULONG
)(SegmentReserve
>> PAGE_SHIFT
);
918 /* Initialise the Heap Entries contained within the Heap Segment */
919 Segment
->FirstEntry
= &Segment
->Entry
+ Segment
->Entry
.Size
;
920 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)Segment
+ SegmentReserve
);
922 if (((SIZE_T
)Segment
->Entry
.Size
<< HEAP_ENTRY_SHIFT
) < SegmentCommit
)
924 HeapEntry
= Segment
->FirstEntry
;
926 /* Prepare a Free Heap Entry header */
927 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
928 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
929 HeapEntry
->SegmentOffset
= SegmentIndex
;
931 /* Register the Free Heap Entry */
932 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
) HeapEntry
, (SegmentCommit
>> HEAP_ENTRY_SHIFT
) - Segment
->Entry
.Size
);
935 /* Initialise the Heap Segment UnCommitted Range information */
936 Segment
->NumberOfUnCommittedPages
= (ULONG
)((SegmentReserve
- SegmentCommit
) >> PAGE_SHIFT
);
937 Segment
->NumberOfUnCommittedRanges
= 0;
938 InitializeListHead(&Segment
->UCRSegmentList
);
940 /* Register the UnCommitted Range of the Heap Segment */
941 if (Segment
->NumberOfUnCommittedPages
!= 0)
942 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
) (Segment
) + SegmentCommit
, SegmentReserve
- SegmentCommit
);
944 return STATUS_SUCCESS
;
948 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
954 /* Make sure it's not user allocated */
955 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
957 BaseAddress
= Segment
->BaseAddress
;
958 DPRINT("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
960 /* Release virtual memory */
961 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
966 if (!NT_SUCCESS(Status
))
968 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
972 PHEAP_FREE_ENTRY NTAPI
973 RtlpCoalesceHeap(PHEAP Heap
)
979 PHEAP_FREE_ENTRY NTAPI
980 RtlpCoalesceFreeBlocks (PHEAP Heap
,
981 PHEAP_FREE_ENTRY FreeEntry
,
985 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
987 /* Get the previous entry */
988 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
991 if (CurrentEntry
!= FreeEntry
&&
992 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
993 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
995 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
997 /* Remove it if asked for */
1000 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1001 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1003 /* Remove it only once! */
1007 /* Remove previous entry too */
1008 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1011 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1013 /* Advance FreeEntry and update sizes */
1014 FreeEntry
= CurrentEntry
;
1015 *FreeSize
= *FreeSize
+ CurrentEntry
->Size
;
1016 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1017 FreeEntry
->Size
= (USHORT
)(*FreeSize
);
1019 /* Also update previous size if needed */
1020 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1022 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= (USHORT
)(*FreeSize
);
1026 /* Check the next block if it exists */
1027 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1029 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1031 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1032 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1034 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1036 /* Remove it if asked for */
1039 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1040 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1044 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1046 /* Remove next entry now */
1047 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1050 *FreeSize
= *FreeSize
+ NextEntry
->Size
;
1051 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1052 FreeEntry
->Size
= (USHORT
)(*FreeSize
);
1054 /* Also update previous size if needed */
1055 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1057 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= (USHORT
)(*FreeSize
);
1064 PHEAP_FREE_ENTRY NTAPI
1065 RtlpExtendHeap(PHEAP Heap
,
1069 UCHAR Index
, EmptyIndex
;
1070 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1071 PHEAP_SEGMENT Segment
;
1072 PHEAP_FREE_ENTRY FreeEntry
;
1075 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1077 /* Calculate amount in pages */
1078 Pages
= (ULONG
)((Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
);
1079 FreeSize
= Pages
* PAGE_SIZE
;
1080 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1082 /* Find an empty segment */
1083 EmptyIndex
= HEAP_SEGMENTS
;
1084 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1086 Segment
= Heap
->Segments
[Index
];
1088 if (Segment
) DPRINT("Segment[%u] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1090 /* Check if its size suits us */
1092 Pages
<= Segment
->NumberOfUnCommittedPages
)
1094 DPRINT("This segment is suitable\n");
1096 /* Commit needed amount */
1097 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1099 /* Coalesce it with adjacent entries */
1102 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1103 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1104 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1108 else if (!Segment
&&
1109 EmptyIndex
== HEAP_SEGMENTS
)
1111 /* Remember the first unused segment index */
1116 /* No luck, need to grow the heap */
1117 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1118 (EmptyIndex
!= HEAP_SEGMENTS
))
1122 /* Reserve the memory */
1123 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1124 ReserveSize
= Heap
->SegmentReserve
;
1126 ReserveSize
= Size
+ PAGE_SIZE
;
1128 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1135 /* If it failed, retry again with a half division algorithm */
1136 while (!NT_SUCCESS(Status
) &&
1137 ReserveSize
!= Size
+ PAGE_SIZE
)
1141 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1142 ReserveSize
= Size
+ PAGE_SIZE
;
1144 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1152 /* Proceed only if it's success */
1153 if (NT_SUCCESS(Status
))
1155 Heap
->SegmentReserve
+= ReserveSize
;
1157 /* Now commit the memory */
1158 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1159 CommitSize
= Heap
->SegmentCommit
;
1161 CommitSize
= Size
+ PAGE_SIZE
;
1163 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1170 DPRINT("Committed %lu bytes at base %p\n", CommitSize
, Segment
);
1172 /* Initialize heap segment if commit was successful */
1173 if (NT_SUCCESS(Status
))
1174 Status
= RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, ReserveSize
, CommitSize
);
1176 /* If everything worked - cool */
1177 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1179 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1181 /* Nope, we failed. Free memory */
1182 ZwFreeVirtualMemory(NtCurrentProcess(),
1189 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1193 if (RtlpGetMode() == UserMode
)
1195 /* If coalescing on free is disabled in usermode, then do it here */
1196 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1198 FreeEntry
= RtlpCoalesceHeap(Heap
);
1200 /* If it's a suitable one - return it */
1202 FreeEntry
->Size
>= Size
)
1212 /***********************************************************************
1215 * Handle of heap: Success
1221 RtlCreateHeap(ULONG Flags
,
1226 PRTL_HEAP_PARAMETERS Parameters
)
1228 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1230 RTL_HEAP_PARAMETERS SafeParams
= {0};
1231 ULONG_PTR MaximumUserModeAddress
;
1232 SYSTEM_BASIC_INFORMATION SystemInformation
;
1233 MEMORY_BASIC_INFORMATION MemoryInfo
;
1234 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1235 ULONG HeapSegmentFlags
= 0;
1239 /* Check for a special heap */
1240 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1242 Heap
= RtlpPageHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1243 if (Heap
) return Heap
;
1245 /* Reset a special Parameters == -1 hack */
1246 if ((ULONG_PTR
)Parameters
== (ULONG_PTR
)-1)
1249 DPRINT1("Enabling page heap failed\n");
1252 /* Check validation flags */
1253 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1255 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1256 Flags
&= HEAP_CREATE_VALID_MASK
;
1259 /* Capture parameters */
1264 /* If size of structure correct, then copy it */
1265 if (Parameters
->Length
== sizeof(RTL_HEAP_PARAMETERS
))
1266 RtlCopyMemory(&SafeParams
, Parameters
, sizeof(RTL_HEAP_PARAMETERS
));
1268 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1270 _SEH2_YIELD(return NULL
);
1275 Parameters
= &SafeParams
;
1277 /* Check global flags */
1278 if (NtGlobalFlags
& FLG_HEAP_DISABLE_COALESCING
)
1279 Flags
|= HEAP_DISABLE_COALESCE_ON_FREE
;
1281 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1282 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1284 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1285 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1287 if (RtlpGetMode() == UserMode
)
1289 /* Also check these flags if in usermode */
1290 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1291 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1293 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1294 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1296 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1297 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1300 /* Set tunable parameters */
1301 RtlpSetHeapParameters(Parameters
);
1303 /* Get the max um address */
1304 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1306 sizeof(SystemInformation
),
1309 if (!NT_SUCCESS(Status
))
1311 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1315 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1317 /* Calculate max alloc size */
1318 if (!Parameters
->MaximumAllocationSize
)
1319 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1321 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1323 if (!Parameters
->VirtualMemoryThreshold
||
1324 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1326 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1329 /* Check reserve/commit sizes and set default values */
1332 CommitSize
= PAGE_SIZE
;
1334 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1336 TotalSize
= 64 * PAGE_SIZE
;
1340 /* Round up the commit size to be at least the page size */
1341 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1344 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1346 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1349 /* Call special heap */
1350 if (RtlpHeapIsSpecial(Flags
))
1351 return RtlDebugCreateHeap(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1353 /* Without serialization, a lock makes no sense */
1354 if ((Flags
& HEAP_NO_SERIALIZE
) && (Lock
!= NULL
))
1357 /* See if we are already provided with an address for the heap */
1360 if (Parameters
->CommitRoutine
)
1362 /* There is a commit routine, so no problem here, check params */
1363 if ((Flags
& HEAP_GROWABLE
) ||
1364 !Parameters
->InitialCommit
||
1365 !Parameters
->InitialReserve
||
1366 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1372 /* Calculate committed and uncommitted addresses */
1373 CommittedAddress
= Addr
;
1374 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1375 TotalSize
= Parameters
->InitialReserve
;
1377 /* Zero the initial page ourselves */
1378 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1382 /* Commit routine is absent, so query how much memory caller reserved */
1383 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1385 MemoryBasicInformation
,
1390 if (!NT_SUCCESS(Status
))
1392 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1397 if (MemoryInfo
.BaseAddress
!= Addr
||
1398 MemoryInfo
.State
== MEM_FREE
)
1403 /* Validation checks passed, set committed/uncommitted addresses */
1404 CommittedAddress
= Addr
;
1406 /* Check if it's committed or not */
1407 if (MemoryInfo
.State
== MEM_COMMIT
)
1409 /* Zero it out because it's already committed */
1410 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1412 /* Calculate uncommitted address value */
1413 CommitSize
= MemoryInfo
.RegionSize
;
1414 TotalSize
= CommitSize
;
1415 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1417 /* Check if uncommitted address is reserved */
1418 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1420 MemoryBasicInformation
,
1425 if (NT_SUCCESS(Status
) &&
1426 MemoryInfo
.State
== MEM_RESERVE
)
1428 /* It is, so add it up to the reserve size */
1429 TotalSize
+= MemoryInfo
.RegionSize
;
1434 /* It's not committed, inform following code that a commit is necessary */
1435 CommitSize
= PAGE_SIZE
;
1436 UncommittedAddress
= Addr
;
1440 /* Mark this as a user-committed mem */
1441 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1446 /* Check commit routine */
1447 if (Parameters
->CommitRoutine
) return NULL
;
1449 /* Reserve memory */
1450 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1457 if (!NT_SUCCESS(Status
))
1459 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1463 /* Set base addresses */
1464 CommittedAddress
= Heap
;
1465 UncommittedAddress
= Heap
;
1468 /* Check if we need to commit something */
1469 if (CommittedAddress
== UncommittedAddress
)
1471 /* Commit the required size */
1472 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1479 DPRINT("Committed %Iu bytes at base %p\n", CommitSize
, CommittedAddress
);
1481 if (!NT_SUCCESS(Status
))
1483 DPRINT1("Failure, Status 0x%08X\n", Status
);
1485 /* Release memory if it was reserved */
1486 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1494 /* Calculate new uncommitted address */
1495 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1498 /* Initialize the heap */
1499 Status
= RtlpInitializeHeap(Heap
, Flags
, Lock
, Parameters
);
1500 if (!NT_SUCCESS(Status
))
1502 DPRINT1("Failed to initialize heap (%x)\n", Status
);
1506 /* Initialize heap's first segment */
1507 Status
= RtlpInitializeHeapSegment(Heap
, (PHEAP_SEGMENT
) (Heap
), 0, HeapSegmentFlags
, TotalSize
, CommitSize
);
1508 if (!NT_SUCCESS(Status
))
1510 DPRINT1("Failed to initialize heap segment (%x)\n", Status
);
1514 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1516 /* Add heap to process list in case of usermode heap */
1517 if (RtlpGetMode() == UserMode
)
1519 RtlpAddHeapToProcessList(Heap
);
1521 // FIXME: What about lookasides?
1527 /***********************************************************************
1536 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1537 * Failure: The Heap handle, if heap is the process heap.
1540 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1542 PHEAP Heap
= (PHEAP
)HeapPtr
;
1543 PLIST_ENTRY Current
;
1544 PHEAP_UCR_SEGMENT UcrSegment
;
1545 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1549 PHEAP_SEGMENT Segment
;
1551 if (!HeapPtr
) return NULL
;
1553 /* Call page heap routine if required */
1554 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
) return RtlpPageHeapDestroy(HeapPtr
);
1556 /* Call special heap */
1557 if (RtlpHeapIsSpecial(Heap
->Flags
))
1559 if (!RtlDebugDestroyHeap(Heap
)) return HeapPtr
;
1562 /* Check for a process heap */
1563 if (RtlpGetMode() == UserMode
&&
1564 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1566 /* Free up all big allocations */
1567 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1568 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1570 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1571 BaseAddress
= (PVOID
)VirtualEntry
;
1572 Current
= Current
->Flink
;
1574 ZwFreeVirtualMemory(NtCurrentProcess(),
1580 /* Delete tags and remove heap from the process heaps list in user mode */
1581 if (RtlpGetMode() == UserMode
)
1583 // FIXME DestroyTags
1584 RtlpRemoveHeapFromProcessList(Heap
);
1587 /* Delete the heap lock */
1588 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1590 /* Delete it if it wasn't user allocated */
1591 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1592 RtlDeleteHeapLock(Heap
->LockVariable
);
1594 /* Clear out the lock variable */
1595 Heap
->LockVariable
= NULL
;
1598 /* Free UCR segments if any were created */
1599 Current
= Heap
->UCRSegments
.Flink
;
1600 while (Current
!= &Heap
->UCRSegments
)
1602 UcrSegment
= CONTAINING_RECORD(Current
, HEAP_UCR_SEGMENT
, ListEntry
);
1604 /* Advance to the next descriptor */
1605 Current
= Current
->Flink
;
1607 BaseAddress
= (PVOID
)UcrSegment
;
1610 /* Release that memory */
1611 ZwFreeVirtualMemory(NtCurrentProcess(),
1617 /* Go through segments and destroy them */
1618 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1620 Segment
= Heap
->Segments
[i
];
1621 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1628 RtlpSplitEntry(PHEAP Heap
,
1630 PHEAP_FREE_ENTRY FreeBlock
,
1631 SIZE_T AllocationSize
,
1635 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1636 UCHAR FreeFlags
, EntryFlags
= HEAP_ENTRY_BUSY
;
1637 PHEAP_ENTRY InUseEntry
;
1640 /* Add extra flags in case of settable user value feature is requested,
1641 or there is a tag (small or normal) or there is a request to
1642 capture stack backtraces */
1643 if ((Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
1644 Heap
->PseudoTagEntries
)
1646 /* Add flag which means that the entry will have extra stuff attached */
1647 EntryFlags
|= HEAP_ENTRY_EXTRA_PRESENT
;
1649 /* NB! AllocationSize is already adjusted by RtlAllocateHeap */
1652 /* Add settable user flags, if any */
1653 EntryFlags
|= (Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4;
1655 /* Save flags, update total free size */
1656 FreeFlags
= FreeBlock
->Flags
;
1657 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1659 /* Make this block an in-use one */
1660 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1661 InUseEntry
->Flags
= EntryFlags
;
1662 InUseEntry
->SmallTagIndex
= 0;
1664 /* Calculate the extra amount */
1665 FreeSize
= InUseEntry
->Size
- Index
;
1667 /* Update it's size fields (we don't need their data anymore) */
1668 InUseEntry
->Size
= (USHORT
)Index
;
1669 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
1671 /* If there is something to split - do the split */
1674 /* Don't split if resulting entry can't contain any payload data
1675 (i.e. being just HEAP_ENTRY_SIZE) */
1678 /* Increase sizes of the in-use entry */
1680 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
1684 /* Calculate a pointer to the new entry */
1685 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
1688 SplitBlock
->Flags
= FreeFlags
;
1689 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
1690 SplitBlock
->Size
= (USHORT
)FreeSize
;
1691 SplitBlock
->PreviousSize
= (USHORT
)Index
;
1693 /* Check if it's the last entry */
1694 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1696 /* Insert it to the free list if it's the last entry */
1697 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1698 Heap
->TotalFreeSize
+= FreeSize
;
1702 /* Not so easy - need to update next's previous size too */
1703 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
1705 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
1707 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
1708 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
1709 Heap
->TotalFreeSize
+= FreeSize
;
1713 /* Even more complex - the next entry is free, so we can merge them into one! */
1714 SplitBlock
->Flags
= SplitBlock2
->Flags
;
1716 /* Remove that next entry */
1717 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
1720 FreeSize
+= SplitBlock2
->Size
;
1721 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
1723 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1725 /* Insert it back */
1726 SplitBlock
->Size
= (USHORT
)FreeSize
;
1728 /* Don't forget to update previous size of the next entry! */
1729 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1731 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
1734 /* Actually insert it */
1735 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, (USHORT
)FreeSize
, FALSE
);
1737 /* Update total size */
1738 Heap
->TotalFreeSize
+= FreeSize
;
1742 /* Resulting block is quite big */
1743 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
1748 /* Reset flags of the free entry */
1753 /* Set last entry flag */
1754 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
1755 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1761 RtlpAllocateNonDedicated(PHEAP Heap
,
1764 SIZE_T AllocationSize
,
1768 PLIST_ENTRY FreeListHead
, Next
;
1769 PHEAP_FREE_ENTRY FreeBlock
;
1770 PHEAP_ENTRY InUseEntry
;
1771 PHEAP_ENTRY_EXTRA Extra
;
1772 EXCEPTION_RECORD ExceptionRecord
;
1774 /* Go through the zero list to find a place where to insert the new entry */
1775 FreeListHead
= &Heap
->FreeLists
[0];
1777 /* Start from the largest block to reduce time */
1778 Next
= FreeListHead
->Blink
;
1779 if (FreeListHead
!= Next
)
1781 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1783 if (FreeBlock
->Size
>= Index
)
1785 /* Our request is smaller than the largest entry in the zero list */
1787 /* Go through the list to find insertion place */
1788 Next
= FreeListHead
->Flink
;
1789 while (FreeListHead
!= Next
)
1791 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
1793 if (FreeBlock
->Size
>= Index
)
1795 /* Found minimally fitting entry. Proceed to either using it as it is
1796 or splitting it to two entries */
1797 RemoveEntryList(&FreeBlock
->FreeList
);
1800 InUseEntry
= RtlpSplitEntry(Heap
, Flags
, FreeBlock
, AllocationSize
, Index
, Size
);
1802 /* Release the lock */
1803 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1805 /* Zero memory if that was requested */
1806 if (Flags
& HEAP_ZERO_MEMORY
)
1807 RtlZeroMemory(InUseEntry
+ 1, Size
);
1808 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1810 /* Fill this block with a special pattern */
1811 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1814 /* Fill tail of the block with a special pattern too if requested */
1815 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1817 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1818 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1821 /* Prepare extra if it's present */
1822 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1824 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1825 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1830 /* Return pointer to the */
1831 return InUseEntry
+ 1;
1834 /* Advance to the next entry */
1840 /* Extend the heap, 0 list didn't have anything suitable */
1841 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
1843 /* Use the new biggest entry we've got */
1846 RemoveEntryList(&FreeBlock
->FreeList
);
1849 InUseEntry
= RtlpSplitEntry(Heap
, Flags
, FreeBlock
, AllocationSize
, Index
, Size
);
1851 /* Release the lock */
1852 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1854 /* Zero memory if that was requested */
1855 if (Flags
& HEAP_ZERO_MEMORY
)
1856 RtlZeroMemory(InUseEntry
+ 1, Size
);
1857 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
1859 /* Fill this block with a special pattern */
1860 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
1863 /* Fill tail of the block with a special pattern too if requested */
1864 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1866 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
1867 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
1870 /* Prepare extra if it's present */
1871 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
1873 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
1874 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
1879 /* Return pointer to the */
1880 return InUseEntry
+ 1;
1883 /* Really unfortunate, out of memory condition */
1884 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
1886 /* Generate an exception */
1887 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
1889 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
1890 ExceptionRecord
.ExceptionRecord
= NULL
;
1891 ExceptionRecord
.NumberParameters
= 1;
1892 ExceptionRecord
.ExceptionFlags
= 0;
1893 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
1895 RtlRaiseException(&ExceptionRecord
);
1898 /* Release the lock */
1899 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
1900 DPRINT1("HEAP: Allocation failed!\n");
1901 DPRINT1("Flags %x\n", Heap
->Flags
);
1905 /***********************************************************************
1906 * HeapAlloc (KERNEL32.334)
1908 * Pointer to allocated memory block
1910 * 0x7d030f60--invalid flags in RtlHeapAllocate
1914 RtlAllocateHeap(IN PVOID HeapPtr
,
1918 PHEAP Heap
= (PHEAP
)HeapPtr
;
1919 PULONG FreeListsInUse
;
1920 ULONG FreeListsInUseUlong
;
1921 SIZE_T AllocationSize
;
1922 SIZE_T Index
, InUseIndex
, i
;
1923 PLIST_ENTRY FreeListHead
;
1924 PHEAP_ENTRY InUseEntry
;
1925 PHEAP_FREE_ENTRY FreeBlock
;
1926 UCHAR FreeFlags
, EntryFlags
= HEAP_ENTRY_BUSY
;
1927 EXCEPTION_RECORD ExceptionRecord
;
1928 BOOLEAN HeapLocked
= FALSE
;
1929 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
1930 PHEAP_ENTRY_EXTRA Extra
;
1934 Flags
|= Heap
->ForceFlags
;
1936 /* Call special heap */
1937 if (RtlpHeapIsSpecial(Flags
))
1938 return RtlDebugAllocateHeap(Heap
, Flags
, Size
);
1940 /* Check for the maximum size */
1941 if (Size
>= 0x80000000)
1943 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
1944 DPRINT1("HEAP: Allocation failed!\n");
1948 if (Flags
& (HEAP_CREATE_ENABLE_TRACING
))
1950 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
1953 //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
1955 /* Calculate allocation size and index */
1957 AllocationSize
= Size
;
1960 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
1962 /* Add extra flags in case of settable user value feature is requested,
1963 or there is a tag (small or normal) or there is a request to
1964 capture stack backtraces */
1965 if ((Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
1966 Heap
->PseudoTagEntries
)
1968 /* Add flag which means that the entry will have extra stuff attached */
1969 EntryFlags
|= HEAP_ENTRY_EXTRA_PRESENT
;
1971 /* Account for extra stuff size */
1972 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
1975 /* Add settable user flags, if any */
1976 EntryFlags
|= (Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4;
1978 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
1980 /* Acquire the lock if necessary */
1981 if (!(Flags
& HEAP_NO_SERIALIZE
))
1983 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
1987 /* Depending on the size, the allocation is going to be done from dedicated,
1988 non-dedicated lists or a virtual block of memory */
1989 if (Index
< HEAP_FREELISTS
)
1991 FreeListHead
= &Heap
->FreeLists
[Index
];
1993 if (!IsListEmpty(FreeListHead
))
1995 /* There is a free entry in this list */
1996 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2000 /* Save flags and remove the free entry */
2001 FreeFlags
= FreeBlock
->Flags
;
2002 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2004 /* Update the total free size of the heap */
2005 Heap
->TotalFreeSize
-= Index
;
2007 /* Initialize this block */
2008 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2009 InUseEntry
->Flags
= EntryFlags
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2010 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2011 InUseEntry
->SmallTagIndex
= 0;
2015 /* Find smallest free block which this request could fit in */
2016 InUseIndex
= Index
>> 5;
2017 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2019 /* This bit magic disables all sizes which are less than the requested allocation size */
2020 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2022 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2024 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2026 /* Go through the list */
2027 for (i
= InUseIndex
; i
< 4; i
++)
2029 if (FreeListsInUseUlong
)
2031 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2035 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2038 /* Nothing found, search in the non-dedicated list */
2040 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2042 /* That list is found, now calculate exact block */
2043 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2045 /* Take this entry and remove it from the list of free blocks */
2046 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2049 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2052 InUseEntry
= RtlpSplitEntry(Heap
, Flags
, FreeBlock
, AllocationSize
, Index
, Size
);
2055 /* Release the lock */
2056 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2058 /* Zero memory if that was requested */
2059 if (Flags
& HEAP_ZERO_MEMORY
)
2060 RtlZeroMemory(InUseEntry
+ 1, Size
);
2061 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2063 /* Fill this block with a special pattern */
2064 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2067 /* Fill tail of the block with a special pattern too if requested */
2068 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2070 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2071 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2074 /* Prepare extra if it's present */
2075 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2077 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2078 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2083 /* User data starts right after the entry's header */
2084 return InUseEntry
+ 1;
2086 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2088 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2089 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2091 else if (Heap
->Flags
& HEAP_GROWABLE
)
2093 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2094 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2096 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2097 (PVOID
*)&VirtualBlock
,
2103 if (!NT_SUCCESS(Status
))
2106 /* Release the lock */
2107 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2108 DPRINT1("HEAP: Allocation failed!\n");
2112 /* Initialize the newly allocated block */
2113 VirtualBlock
->BusyBlock
.Size
= (USHORT
)(AllocationSize
- Size
);
2114 ASSERT(VirtualBlock
->BusyBlock
.Size
>= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
));
2115 VirtualBlock
->BusyBlock
.Flags
= EntryFlags
| HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
;
2116 VirtualBlock
->CommitSize
= AllocationSize
;
2117 VirtualBlock
->ReserveSize
= AllocationSize
;
2119 /* Insert it into the list of virtual allocations */
2120 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2122 /* Release the lock */
2123 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2125 /* Return pointer to user data */
2126 return VirtualBlock
+ 1;
2129 /* Generate an exception */
2130 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2132 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2133 ExceptionRecord
.ExceptionRecord
= NULL
;
2134 ExceptionRecord
.NumberParameters
= 1;
2135 ExceptionRecord
.ExceptionFlags
= 0;
2136 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2138 RtlRaiseException(&ExceptionRecord
);
2141 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL
);
2143 /* Release the lock */
2144 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2145 DPRINT1("HEAP: Allocation failed!\n");
2150 /***********************************************************************
2151 * HeapFree (KERNEL32.338)
2158 BOOLEAN NTAPI
RtlFreeHeap(
2159 HANDLE HeapPtr
, /* [in] Handle of heap */
2160 ULONG Flags
, /* [in] Heap freeing flags */
2161 PVOID Ptr
/* [in] Address of memory to free */
2165 PHEAP_ENTRY HeapEntry
;
2166 USHORT TagIndex
= 0;
2168 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2169 BOOLEAN Locked
= FALSE
;
2172 /* Freeing NULL pointer is a legal operation */
2173 if (!Ptr
) return TRUE
;
2175 /* Get pointer to the heap and force flags */
2176 Heap
= (PHEAP
)HeapPtr
;
2177 Flags
|= Heap
->ForceFlags
;
2179 /* Call special heap */
2180 if (RtlpHeapIsSpecial(Flags
))
2181 return RtlDebugFreeHeap(Heap
, Flags
, Ptr
);
2183 /* Lock if necessary */
2184 if (!(Flags
& HEAP_NO_SERIALIZE
))
2186 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
2190 /* Get pointer to the heap entry */
2191 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2193 /* Check this entry, fail if it's invalid */
2194 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2195 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2196 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2198 /* This is an invalid block */
2199 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2200 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2202 /* Release the heap lock */
2203 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2207 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2209 /* Big allocation */
2210 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2212 /* Remove it from the list */
2213 RemoveEntryList(&VirtualEntry
->Entry
);
2218 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2219 (PVOID
*)&VirtualEntry
,
2223 if (!NT_SUCCESS(Status
))
2225 DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
2226 Status
, Heap
, Ptr
, VirtualEntry
);
2227 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status
);
2232 /* Normal allocation */
2233 BlockSize
= HeapEntry
->Size
;
2237 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2238 if (RtlpGetMode() == KernelMode
||
2239 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2241 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2242 (PHEAP_FREE_ENTRY
)HeapEntry
,
2247 /* If there is no need to decommit the block - put it into a free list */
2248 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2249 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2251 /* Check if it needs to go to a 0 list */
2252 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2254 /* General-purpose 0 list */
2255 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2259 /* Usual free list */
2260 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2262 /* Assert sizes are consistent */
2263 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2265 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2268 /* Increase the free size */
2269 Heap
->TotalFreeSize
+= BlockSize
;
2273 if (RtlpGetMode() == UserMode
&&
2282 /* Decommit this block */
2283 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2287 /* Release the heap lock */
2288 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2294 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2296 IN PHEAP_ENTRY InUseEntry
,
2300 UCHAR EntryFlags
, RememberFlags
;
2301 PHEAP_FREE_ENTRY FreeEntry
, UnusedEntry
, FollowingEntry
;
2302 SIZE_T FreeSize
, PrevSize
, TailPart
, AddedSize
= 0;
2303 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2305 /* We can't grow beyond specified threshold */
2306 if (Index
> Heap
->VirtualMemoryThreshold
)
2309 /* Get entry flags */
2310 EntryFlags
= InUseEntry
->Flags
;
2312 /* Get the next free entry */
2313 FreeEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ InUseEntry
->Size
);
2315 if (EntryFlags
& HEAP_ENTRY_LAST_ENTRY
)
2317 /* There is no next block, just uncommitted space. Calculate how much is needed */
2318 FreeSize
= (Index
- InUseEntry
->Size
) << HEAP_ENTRY_SHIFT
;
2319 FreeSize
= ROUND_UP(FreeSize
, PAGE_SIZE
);
2321 /* Find and commit those pages */
2322 FreeEntry
= RtlpFindAndCommitPages(Heap
,
2323 Heap
->Segments
[InUseEntry
->SegmentOffset
],
2327 /* Fail if it failed... */
2328 if (!FreeEntry
) return FALSE
;
2330 /* It was successful, perform coalescing */
2331 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
2332 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
2334 /* Check if it's enough */
2335 if (FreeSize
+ InUseEntry
->Size
< Index
)
2337 /* Still not enough */
2338 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
2339 Heap
->TotalFreeSize
+= FreeSize
;
2343 /* Remember flags of this free entry */
2344 RememberFlags
= FreeEntry
->Flags
;
2347 FreeSize
+= InUseEntry
->Size
;
2351 /* The next block indeed exists. Check if it's free or in use */
2352 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
) return FALSE
;
2354 /* Next entry is free, check if it can fit the block we need */
2355 FreeSize
= InUseEntry
->Size
+ FreeEntry
->Size
;
2356 if (FreeSize
< Index
) return FALSE
;
2358 /* Remember flags of this free entry */
2359 RememberFlags
= FreeEntry
->Flags
;
2361 /* Remove this block from the free list */
2362 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
2363 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
2366 PrevSize
= (InUseEntry
->Size
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2369 /* Don't produce too small blocks */
2376 /* Process extra stuff */
2377 if (EntryFlags
& HEAP_ENTRY_EXTRA_PRESENT
)
2379 /* Calculate pointers */
2380 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2381 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2384 *NewExtra
= *OldExtra
;
2390 InUseEntry
->Size
= (USHORT
)Index
;
2391 InUseEntry
->UnusedBytes
= (UCHAR
)((Index
<< HEAP_ENTRY_SHIFT
) - Size
);
2393 /* Check if there is a free space remaining after merging those blocks */
2396 /* Update flags and sizes */
2397 InUseEntry
->Flags
|= RememberFlags
& HEAP_ENTRY_LAST_ENTRY
;
2399 /* Either update previous size of the next entry or mark it as a last
2400 entry in the segment*/
2401 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2402 (InUseEntry
+ InUseEntry
->Size
)->PreviousSize
= InUseEntry
->Size
;
2406 /* Complex case, we need to split the block to give unused free space
2408 UnusedEntry
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2409 UnusedEntry
->PreviousSize
= (USHORT
)Index
;
2410 UnusedEntry
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2412 /* Update the following block or set the last entry in the segment */
2413 if (RememberFlags
& HEAP_ENTRY_LAST_ENTRY
)
2415 /* Set flags and size */
2416 UnusedEntry
->Flags
= RememberFlags
;
2417 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2419 /* Insert it to the heap and update total size */
2420 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2421 Heap
->TotalFreeSize
+= FreeSize
;
2425 /* There is a block after this one */
2426 FollowingEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
);
2428 if (FollowingEntry
->Flags
& HEAP_ENTRY_BUSY
)
2430 /* Update flags and set size of the unused space entry */
2431 UnusedEntry
->Flags
= RememberFlags
& (~HEAP_ENTRY_LAST_ENTRY
);
2432 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2434 /* Update previous size of the following entry */
2435 FollowingEntry
->PreviousSize
= (USHORT
)FreeSize
;
2437 /* Insert it to the heap and update total free size */
2438 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2439 Heap
->TotalFreeSize
+= FreeSize
;
2443 /* That following entry is also free, what a fortune! */
2444 RememberFlags
= FollowingEntry
->Flags
;
2447 RtlpRemoveFreeBlock(Heap
, FollowingEntry
, FALSE
, FALSE
);
2448 Heap
->TotalFreeSize
-= FollowingEntry
->Size
;
2450 /* And make up a new combined block */
2451 FreeSize
+= FollowingEntry
->Size
;
2452 UnusedEntry
->Flags
= RememberFlags
;
2454 /* Check where to put it */
2455 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2457 /* Fine for a dedicated list */
2458 UnusedEntry
->Size
= (USHORT
)FreeSize
;
2460 if (!(RememberFlags
& HEAP_ENTRY_LAST_ENTRY
))
2461 ((PHEAP_ENTRY
)UnusedEntry
+ FreeSize
)->PreviousSize
= (USHORT
)FreeSize
;
2463 /* Insert it back and update total size */
2464 RtlpInsertFreeBlockHelper(Heap
, UnusedEntry
, FreeSize
, FALSE
);
2465 Heap
->TotalFreeSize
+= FreeSize
;
2469 /* The block is very large, leave all the hassle to the insertion routine */
2470 RtlpInsertFreeBlock(Heap
, UnusedEntry
, FreeSize
);
2476 /* Properly "zero out" (and fill!) the space */
2477 if (Flags
& HEAP_ZERO_MEMORY
)
2479 RtlZeroMemory((PCHAR
)(InUseEntry
+ 1) + PrevSize
, Size
- PrevSize
);
2481 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2483 /* Calculate tail part which we need to fill */
2484 TailPart
= PrevSize
& (sizeof(ULONG
) - 1);
2486 /* "Invert" it as usual */
2487 if (TailPart
) TailPart
= 4 - TailPart
;
2489 if (Size
> (PrevSize
+ TailPart
))
2490 AddedSize
= (Size
- (PrevSize
+ TailPart
)) & ~(sizeof(ULONG
) - 1);
2494 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + PrevSize
+ TailPart
,
2496 ARENA_INUSE_FILLER
);
2500 /* Fill the new tail */
2501 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2503 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2508 /* Copy user settable flags */
2509 InUseEntry
->Flags
&= ~HEAP_ENTRY_SETTABLE_FLAGS
;
2510 InUseEntry
->Flags
|= ((Flags
& HEAP_SETTABLE_USER_FLAGS
) >> 4);
2512 /* Return success */
2516 PHEAP_ENTRY_EXTRA NTAPI
2517 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2519 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2521 /* Check if it's a big block */
2522 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2524 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2526 /* Return a pointer to the extra stuff*/
2527 return &VirtualEntry
->ExtraStuff
;
2531 /* This is a usual entry, which means extra stuff follows this block */
2532 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2537 /***********************************************************************
2540 * Heap [in] Handle of heap block
2541 * Flags [in] Heap reallocation flags
2542 * Ptr, [in] Address of memory to reallocate
2543 * Size [in] Number of bytes to reallocate
2546 * Pointer to reallocated memory block
2548 * 0x7d030f60--invalid flags in RtlHeapAllocate
2552 RtlReAllocateHeap(HANDLE HeapPtr
,
2557 PHEAP Heap
= (PHEAP
)HeapPtr
;
2558 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2559 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2560 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2561 BOOLEAN HeapLocked
= FALSE
;
2562 PVOID NewBaseAddress
;
2563 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2564 SIZE_T OldSize
, Index
, OldIndex
;
2568 SIZE_T RemainderBytes
, ExtraSize
;
2569 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2570 EXCEPTION_RECORD ExceptionRecord
;
2572 /* Return success in case of a null pointer */
2575 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS
);
2579 /* Force heap flags */
2580 Flags
|= Heap
->ForceFlags
;
2582 /* Call special heap */
2583 if (RtlpHeapIsSpecial(Flags
))
2584 return RtlDebugReAllocateHeap(Heap
, Flags
, Ptr
, Size
);
2586 /* Make sure size is valid */
2587 if (Size
>= 0x80000000)
2589 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2593 /* Calculate allocation size and index */
2595 AllocationSize
= Size
;
2598 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2600 /* Add up extra stuff, if it is present anywhere */
2601 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2602 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2603 Heap
->PseudoTagEntries
)
2605 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2608 /* Acquire the lock if necessary */
2609 if (!(Flags
& HEAP_NO_SERIALIZE
))
2611 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
2613 Flags
&= ~HEAP_NO_SERIALIZE
;
2616 /* Get the pointer to the in-use entry */
2617 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2619 /* If that entry is not really in-use, we have a problem */
2620 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2622 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2624 /* Release the lock and return */
2626 RtlLeaveHeapLock(Heap
->LockVariable
);
2630 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2632 /* This is a virtually allocated block. Get its size */
2633 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2635 /* Convert it to an index */
2636 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2638 /* Calculate new allocation size and round it to the page size */
2639 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2640 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2645 OldIndex
= InUseEntry
->Size
;
2647 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2650 /* Calculate new index */
2651 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2653 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2654 if (Index
<= OldIndex
)
2656 /* Difference must be greater than 1, adjust if it's not so */
2657 if (Index
+ 1 == OldIndex
)
2660 AllocationSize
+= sizeof(HEAP_ENTRY
);
2663 /* Calculate new size */
2664 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2666 /* Simple in case of a virtual alloc - just an unused size */
2667 InUseEntry
->Size
= (USHORT
)(AllocationSize
- Size
);
2668 ASSERT(InUseEntry
->Size
>= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
));
2670 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2672 /* There is extra stuff, take it into account */
2673 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2674 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2675 *NewExtra
= *OldExtra
;
2677 // FIXME Tagging, TagIndex
2679 /* Update unused bytes count */
2680 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2684 // FIXME Tagging, SmallTagIndex
2685 InUseEntry
->UnusedBytes
= (UCHAR
)(AllocationSize
- Size
);
2688 /* If new size is bigger than the old size */
2691 /* Zero out that additional space if required */
2692 if (Flags
& HEAP_ZERO_MEMORY
)
2694 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2696 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2698 /* Fill it on free if required */
2699 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2702 RemainderBytes
= 4 - RemainderBytes
;
2704 if (Size
> (OldSize
+ RemainderBytes
))
2706 /* Calculate actual amount of extra bytes to fill */
2707 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2709 /* Fill them if there are any */
2712 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2714 ARENA_INUSE_FILLER
);
2720 /* Fill tail of the heap entry if required */
2721 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2723 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2728 /* Check if the difference is significant or not */
2729 if (Index
!= OldIndex
)
2732 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2734 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2736 /* This is a virtual block allocation */
2737 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2741 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2742 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2744 /* Release the memory */
2745 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2746 (PVOID
*)&DecommitBase
,
2750 if (!NT_SUCCESS(Status
))
2752 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2756 /* Otherwise reduce the commit size */
2757 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2762 /* Reduce size of the block and possibly split it */
2763 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2765 /* Initialize this entry */
2766 SplitBlock
->Flags
= FreeFlags
;
2767 SplitBlock
->PreviousSize
= (USHORT
)Index
;
2768 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2770 /* Remember free size */
2771 FreeSize
= InUseEntry
->Size
- Index
;
2774 InUseEntry
->Size
= (USHORT
)Index
;
2775 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2777 /* Is that the last entry */
2778 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2780 /* Set its size and insert it to the list */
2781 SplitBlock
->Size
= (USHORT
)FreeSize
;
2782 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2784 /* Update total free size */
2785 Heap
->TotalFreeSize
+= FreeSize
;
2789 /* Get the block after that one */
2790 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2792 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2794 /* It's in use, add it here*/
2795 SplitBlock
->Size
= (USHORT
)FreeSize
;
2797 /* Update previous size of the next entry */
2798 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2800 /* Insert it to the list */
2801 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2803 /* Update total size */
2804 Heap
->TotalFreeSize
+= FreeSize
;
2808 /* Next entry is free, so merge with it */
2809 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2811 /* Remove it, update total size */
2812 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2813 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2815 /* Calculate total free size */
2816 FreeSize
+= SplitBlock2
->Size
;
2818 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2820 SplitBlock
->Size
= (USHORT
)FreeSize
;
2822 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2824 /* Update previous size of the next entry */
2825 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2828 /* Insert the new one back and update total size */
2829 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2830 Heap
->TotalFreeSize
+= FreeSize
;
2835 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2844 /* We're growing the block */
2845 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
2846 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
2848 /* Growing in place failed, so growing out of place */
2849 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
2851 DPRINT1("Realloc in place failed, but it was the only option\n");
2856 /* Clear tag bits */
2857 Flags
&= ~HEAP_TAG_MASK
;
2859 /* Process extra stuff */
2860 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2862 /* Preserve user settable flags */
2863 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
2865 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
2867 /* Get pointer to the old extra data */
2868 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2870 /* Save tag index if it was set */
2871 if (OldExtra
->TagIndex
&&
2872 !(OldExtra
->TagIndex
& HEAP_PSEUDO_TAG_FLAG
))
2874 Flags
|= OldExtra
->TagIndex
<< HEAP_TAG_SHIFT
;
2877 else if (InUseEntry
->SmallTagIndex
)
2879 /* Take small tag index into account */
2880 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
2883 /* Allocate new block from the heap */
2884 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
2885 Flags
& ~HEAP_ZERO_MEMORY
,
2888 /* Proceed if it didn't fail */
2891 /* Get new entry pointer */
2892 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
2894 /* Process extra stuff if it exists */
2895 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2897 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
2899 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2901 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2902 NewExtra
->Settable
= OldExtra
->Settable
;
2906 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
2910 /* Copy actual user bits */
2912 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
2914 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
2916 /* Zero remaining part if required */
2917 if (Size
> OldSize
&&
2918 (Flags
& HEAP_ZERO_MEMORY
))
2920 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
2923 /* Free the old block */
2924 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
2927 Ptr
= NewBaseAddress
;
2932 /* Did resizing fail? */
2933 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
2935 /* Generate an exception if required */
2936 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2937 ExceptionRecord
.ExceptionRecord
= NULL
;
2938 ExceptionRecord
.NumberParameters
= 1;
2939 ExceptionRecord
.ExceptionFlags
= 0;
2940 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2942 RtlRaiseException(&ExceptionRecord
);
2945 /* Release the heap lock if it was acquired */
2947 RtlLeaveHeapLock(Heap
->LockVariable
);
2953 /***********************************************************************
2959 RtlCompactHeap(HANDLE Heap
,
2967 /***********************************************************************
2969 * Attempts to acquire the critical section object for a specified heap.
2972 * Heap [in] Handle of heap to lock for exclusive access
2981 RtlLockHeap(IN HANDLE HeapPtr
)
2983 PHEAP Heap
= (PHEAP
)HeapPtr
;
2985 // FIXME Check for special heap
2987 /* Check if it's really a heap */
2988 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
2990 /* Lock if it's lockable */
2991 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
2993 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
3000 /***********************************************************************
3002 * Releases ownership of the critical section object.
3005 * Heap [in] Handle to the heap to unlock
3014 RtlUnlockHeap(HANDLE HeapPtr
)
3016 PHEAP Heap
= (PHEAP
)HeapPtr
;
3018 // FIXME Check for special heap
3020 /* Check if it's really a heap */
3021 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3023 /* Unlock if it's lockable */
3024 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3026 RtlLeaveHeapLock(Heap
->LockVariable
);
3033 /***********************************************************************
3036 * Heap [in] Handle of heap
3037 * Flags [in] Heap size control flags
3038 * Ptr [in] Address of memory to return size for
3041 * Size in bytes of allocated memory
3042 * 0xffffffff: Failure
3053 PHEAP Heap
= (PHEAP
)HeapPtr
;
3054 PHEAP_ENTRY HeapEntry
;
3057 // FIXME This is a hack around missing SEH support!
3060 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3065 Flags
|= Heap
->ForceFlags
;
3067 /* Call special heap */
3068 if (RtlpHeapIsSpecial(Flags
))
3069 return RtlDebugSizeHeap(Heap
, Flags
, Ptr
);
3071 /* Get the heap entry pointer */
3072 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3074 /* Return -1 if that entry is free */
3075 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3077 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3081 /* Get size of this block depending if it's a usual or a big one */
3082 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3084 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3089 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3092 /* Return calculated size */
3097 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry
)
3099 SIZE_T Size
, Result
;
3102 /* Calculate size */
3103 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3104 Size
= RtlpGetSizeOfBigBlock(HeapEntry
);
3106 Size
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3108 /* Calculate pointer to the tail part of the block */
3109 TailPart
= (PCHAR
)(HeapEntry
+ 1) + Size
;
3111 /* Compare tail pattern */
3112 Result
= RtlCompareMemory(TailPart
,
3116 if (Result
!= HEAP_ENTRY_SIZE
)
3118 DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size
, HeapEntry
, TailPart
+ Result
);
3127 RtlpValidateHeapHeaders(
3129 BOOLEAN Recalculate
)
3131 // We skip header validation for now
3136 RtlpValidateHeapEntry(
3138 PHEAP_ENTRY HeapEntry
)
3140 BOOLEAN BigAllocation
, EntryFound
= FALSE
;
3141 PHEAP_SEGMENT Segment
;
3142 ULONG SegmentOffset
;
3144 /* Perform various consistency checks of this entry */
3145 if (!HeapEntry
) goto invalid_entry
;
3146 if ((ULONG_PTR
)HeapEntry
& (HEAP_ENTRY_SIZE
- 1)) goto invalid_entry
;
3147 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
)) goto invalid_entry
;
3149 BigAllocation
= HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
;
3150 Segment
= Heap
->Segments
[HeapEntry
->SegmentOffset
];
3152 if (BigAllocation
&&
3153 (((ULONG_PTR
)HeapEntry
& (PAGE_SIZE
- 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
)))
3156 if (!BigAllocation
&& (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
||
3158 HeapEntry
< Segment
->FirstEntry
||
3159 HeapEntry
>= Segment
->LastValidEntry
))
3162 if ((HeapEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
) &&
3163 !RtlpCheckInUsePattern(HeapEntry
))
3166 /* Checks are done, if this is a virtual entry, that's all */
3167 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) return TRUE
;
3169 /* Go through segments and check if this entry fits into any of them */
3170 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3172 Segment
= Heap
->Segments
[SegmentOffset
];
3173 if (!Segment
) continue;
3175 if ((HeapEntry
>= Segment
->FirstEntry
) &&
3176 (HeapEntry
< Segment
->LastValidEntry
))
3184 /* Return our result of finding entry in the segments */
3188 DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry
, Heap
);
3193 RtlpValidateHeapSegment(
3195 PHEAP_SEGMENT Segment
,
3196 UCHAR SegmentOffset
,
3197 PULONG FreeEntriesCount
,
3198 PSIZE_T TotalFreeSize
,
3200 PSIZE_T PseudoTagEntries
)
3202 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
3203 PLIST_ENTRY UcrEntry
;
3204 SIZE_T ByteSize
, Size
, Result
;
3205 PHEAP_ENTRY CurrentEntry
;
3206 ULONG UnCommittedPages
;
3207 ULONG UnCommittedRanges
;
3210 UnCommittedPages
= 0;
3211 UnCommittedRanges
= 0;
3213 if (IsListEmpty(&Segment
->UCRSegmentList
))
3216 UcrDescriptor
= NULL
;
3220 UcrEntry
= Segment
->UCRSegmentList
.Flink
;
3221 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3224 if (Segment
->BaseAddress
== Heap
)
3225 CurrentEntry
= &Heap
->Entry
;
3227 CurrentEntry
= &Segment
->Entry
;
3229 while (CurrentEntry
< Segment
->LastValidEntry
)
3231 if (UcrDescriptor
&&
3232 ((PVOID
)CurrentEntry
>= UcrDescriptor
->Address
))
3234 DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
3235 CurrentEntry
, UcrDescriptor
->Address
,
3236 (PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3243 while (CurrentEntry
< Segment
->LastValidEntry
)
3245 if (PreviousSize
!= CurrentEntry
->PreviousSize
)
3247 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
3248 CurrentEntry
, CurrentEntry
->PreviousSize
, PreviousSize
);
3253 PreviousSize
= CurrentEntry
->Size
;
3254 Size
= CurrentEntry
->Size
<< HEAP_ENTRY_SHIFT
;
3256 if (CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
)
3263 /* Check fill pattern */
3264 if (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
)
3266 if (!RtlpCheckInUsePattern(CurrentEntry
))
3272 /* The entry is free, increase free entries count and total free size */
3273 *FreeEntriesCount
= *FreeEntriesCount
+ 1;
3274 *TotalFreeSize
+= CurrentEntry
->Size
;
3276 if ((Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
) &&
3277 (CurrentEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
3279 ByteSize
= Size
- sizeof(HEAP_FREE_ENTRY
);
3281 if ((CurrentEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) &&
3282 (ByteSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
)))
3284 ByteSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
3287 Result
= RtlCompareMemoryUlong((PCHAR
)((PHEAP_FREE_ENTRY
)CurrentEntry
+ 1),
3291 if (Result
!= ByteSize
)
3293 DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
3295 (PCHAR
)(CurrentEntry
+ 1) + Result
);
3302 if (CurrentEntry
->SegmentOffset
!= SegmentOffset
)
3304 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n",
3305 CurrentEntry
, SegmentOffset
, CurrentEntry
->SegmentOffset
);
3309 /* Check if it's the last entry */
3310 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
3312 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3316 /* Check if it's not really the last one */
3317 if (CurrentEntry
!= Segment
->LastValidEntry
)
3319 DPRINT1("HEAP: Heap entry %p is not last block in segment (%p)\n",
3320 CurrentEntry
, Segment
->LastValidEntry
);
3324 else if (CurrentEntry
!= UcrDescriptor
->Address
)
3326 DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
3327 CurrentEntry
, UcrDescriptor
->Address
);
3333 UnCommittedPages
+= (ULONG
)(UcrDescriptor
->Size
/ PAGE_SIZE
);
3334 UnCommittedRanges
++;
3336 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
);
3338 /* Go to the next UCR descriptor */
3339 UcrEntry
= UcrEntry
->Flink
;
3340 if (UcrEntry
== &Segment
->UCRSegmentList
)
3343 UcrDescriptor
= NULL
;
3347 UcrDescriptor
= CONTAINING_RECORD(UcrEntry
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
3354 /* Advance to the next entry */
3355 CurrentEntry
= (PHEAP_ENTRY
)((PCHAR
)CurrentEntry
+ Size
);
3359 /* Check total numbers of UCP and UCR */
3360 if (Segment
->NumberOfUnCommittedPages
!= UnCommittedPages
)
3362 DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
3363 Segment
, Segment
->NumberOfUnCommittedPages
, UnCommittedPages
);
3368 if (Segment
->NumberOfUnCommittedRanges
!= UnCommittedRanges
)
3370 DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
3371 Segment
, Segment
->NumberOfUnCommittedRanges
, UnCommittedRanges
);
3380 RtlpValidateHeap(PHEAP Heap
,
3381 BOOLEAN ForceValidation
)
3383 PHEAP_SEGMENT Segment
;
3385 UCHAR SegmentOffset
;
3386 SIZE_T Size
, TotalFreeSize
;
3388 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
3389 PLIST_ENTRY ListHead
, NextEntry
;
3390 PHEAP_FREE_ENTRY FreeEntry
;
3391 ULONG FreeBlocksCount
, FreeListEntriesCount
;
3394 if (!RtlpValidateHeapHeaders(Heap
, FALSE
))
3397 /* Skip validation if it's not needed */
3398 if (!ForceValidation
&& !(Heap
->Flags
& HEAP_VALIDATE_ALL_ENABLED
))
3401 /* Check free lists bitmaps */
3402 FreeListEntriesCount
= 0;
3403 ListHead
= &Heap
->FreeLists
[0];
3405 for (Size
= 0; Size
< HEAP_FREELISTS
; Size
++)
3409 /* This is a dedicated list. Check if it's empty */
3410 EmptyList
= IsListEmpty(ListHead
);
3412 if (Heap
->u
.FreeListsInUseBytes
[Size
>> 3] & (1 << (Size
& 7)))
3416 DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size
);
3424 DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size
);
3430 /* Now check this list entries */
3431 NextEntry
= ListHead
->Flink
;
3434 while (ListHead
!= NextEntry
)
3436 FreeEntry
= CONTAINING_RECORD(NextEntry
, HEAP_FREE_ENTRY
, FreeList
);
3437 NextEntry
= NextEntry
->Flink
;
3439 /* If there is an in-use entry in a free list - that's quite a big problem */
3440 if (FreeEntry
->Flags
& HEAP_ENTRY_BUSY
)
3442 DPRINT1("HEAP: %Ix-dedicated list free element %p is marked in-use\n", Size
, FreeEntry
);
3446 /* Check sizes according to that specific list's size */
3447 if ((Size
== 0) && (FreeEntry
->Size
< HEAP_FREELISTS
))
3449 DPRINT1("HEAP: Non dedicated list free element %p has size %x which would fit a dedicated list\n", FreeEntry
, FreeEntry
->Size
);
3452 else if (Size
&& (FreeEntry
->Size
!= Size
))
3454 DPRINT1("HEAP: %Ix-dedicated list free element %p has incorrect size %x\n", Size
, FreeEntry
, FreeEntry
->Size
);
3457 else if ((Size
== 0) && (FreeEntry
->Size
< PreviousSize
))
3459 DPRINT1("HEAP: Non dedicated list free element %p is not put in order\n", FreeEntry
);
3463 /* Remember previous size*/
3464 PreviousSize
= FreeEntry
->Size
;
3466 /* Add up to the total amount of free entries */
3467 FreeListEntriesCount
++;
3470 /* Go to the head of the next free list */
3474 /* Check big allocations */
3475 ListHead
= &Heap
->VirtualAllocdBlocks
;
3476 NextEntry
= ListHead
->Flink
;
3478 while (ListHead
!= NextEntry
)
3480 VirtualAllocBlock
= CONTAINING_RECORD(NextEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
3482 /* We can only check the fill pattern */
3483 if (VirtualAllocBlock
->BusyBlock
.Flags
& HEAP_ENTRY_FILL_PATTERN
)
3485 if (!RtlpCheckInUsePattern(&VirtualAllocBlock
->BusyBlock
))
3489 NextEntry
= NextEntry
->Flink
;
3492 /* Check all segments */
3493 FreeBlocksCount
= 0;
3496 for (SegmentOffset
= 0; SegmentOffset
< HEAP_SEGMENTS
; SegmentOffset
++)
3498 Segment
= Heap
->Segments
[SegmentOffset
];
3500 /* Go to the next one if there is no segment */
3501 if (!Segment
) continue;
3503 if (!RtlpValidateHeapSegment(Heap
,
3515 if (FreeListEntriesCount
!= FreeBlocksCount
)
3517 DPRINT1("HEAP: Free blocks count in arena (%lu) does not match free blocks number in the free lists (%lu)\n", FreeBlocksCount
, FreeListEntriesCount
);
3521 if (Heap
->TotalFreeSize
!= TotalFreeSize
)
3523 DPRINT1("HEAP: Total size of free blocks in arena (%Iu) does not equal to the one in heap header (%Iu)\n", TotalFreeSize
, Heap
->TotalFreeSize
);
3530 /***********************************************************************
3532 * Validates a specified heap.
3535 * Heap [in] Handle to the heap
3536 * Flags [in] Bit flags that control access during operation
3537 * Block [in] Optional pointer to memory block to validate
3548 BOOLEAN NTAPI
RtlValidateHeap(
3554 PHEAP Heap
= (PHEAP
)HeapPtr
;
3555 BOOLEAN HeapLocked
= FALSE
;
3558 /* Check for page heap */
3559 if (Heap
->ForceFlags
& HEAP_FLAG_PAGE_ALLOCS
)
3560 return RtlpDebugPageHeapValidate(HeapPtr
, Flags
, Block
);
3562 /* Check signature */
3563 if (Heap
->Signature
!= HEAP_SIGNATURE
)
3565 DPRINT1("HEAP: Signature %lx is invalid for heap %p\n", Heap
->Signature
, Heap
);
3570 Flags
= Heap
->ForceFlags
;
3572 /* Acquire the lock if necessary */
3573 if (!(Flags
& HEAP_NO_SERIALIZE
))
3575 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
3579 /* Either validate whole heap or just one entry */
3581 HeapValid
= RtlpValidateHeap(Heap
, TRUE
);
3583 HeapValid
= RtlpValidateHeapEntry(Heap
, (PHEAP_ENTRY
)Block
- 1);
3585 /* Unlock if it's lockable */
3588 RtlLeaveHeapLock(Heap
->LockVariable
);
3598 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3602 return STATUS_NOT_IMPLEMENTED
;
3610 RtlGetProcessHeaps(ULONG count
,
3622 RtlValidateProcessHeaps(VOID
)
3634 IN PVOID HeapHandle
,
3647 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3649 IN PVOID BaseAddress
,
3652 PHEAP Heap
= (PHEAP
)HeapHandle
;
3653 PHEAP_ENTRY HeapEntry
;
3654 PHEAP_ENTRY_EXTRA Extra
;
3655 BOOLEAN HeapLocked
= FALSE
, ValueSet
= FALSE
;
3658 Flags
|= Heap
->Flags
;
3660 /* Call special heap */
3661 if (RtlpHeapIsSpecial(Flags
))
3662 return RtlDebugSetUserValueHeap(Heap
, Flags
, BaseAddress
, UserValue
);
3664 /* Lock if it's lockable */
3665 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3667 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
3671 /* Get a pointer to the entry */
3672 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3674 /* If it's a free entry - return error */
3675 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3677 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3679 /* Release the heap lock if it was acquired */
3681 RtlLeaveHeapLock(Heap
->LockVariable
);
3686 /* Check if this entry has an extra stuff associated with it */
3687 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3689 /* Use extra to store the value */
3690 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3691 Extra
->Settable
= (ULONG_PTR
)UserValue
;
3693 /* Indicate that value was set */
3697 /* Release the heap lock if it was acquired */
3699 RtlLeaveHeapLock(Heap
->LockVariable
);
3709 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3711 IN PVOID BaseAddress
,
3712 IN ULONG UserFlagsReset
,
3713 IN ULONG UserFlagsSet
)
3715 PHEAP Heap
= (PHEAP
)HeapHandle
;
3716 PHEAP_ENTRY HeapEntry
;
3717 BOOLEAN HeapLocked
= FALSE
;
3720 Flags
|= Heap
->Flags
;
3722 /* Call special heap */
3723 if (RtlpHeapIsSpecial(Flags
))
3724 return RtlDebugSetUserFlagsHeap(Heap
, Flags
, BaseAddress
, UserFlagsReset
, UserFlagsSet
);
3726 /* Lock if it's lockable */
3727 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3729 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
3733 /* Get a pointer to the entry */
3734 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3736 /* If it's a free entry - return error */
3737 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3739 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3741 /* Release the heap lock if it was acquired */
3743 RtlLeaveHeapLock(Heap
->LockVariable
);
3748 /* Set / reset flags */
3749 HeapEntry
->Flags
&= ~(UserFlagsReset
>> 4);
3750 HeapEntry
->Flags
|= (UserFlagsSet
>> 4);
3752 /* Release the heap lock if it was acquired */
3754 RtlLeaveHeapLock(Heap
->LockVariable
);
3764 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3766 IN PVOID BaseAddress
,
3767 OUT PVOID
*UserValue
,
3768 OUT PULONG UserFlags
)
3770 PHEAP Heap
= (PHEAP
)HeapHandle
;
3771 PHEAP_ENTRY HeapEntry
;
3772 PHEAP_ENTRY_EXTRA Extra
;
3773 BOOLEAN HeapLocked
= FALSE
;
3776 Flags
|= Heap
->Flags
;
3778 /* Call special heap */
3779 if (RtlpHeapIsSpecial(Flags
))
3780 return RtlDebugGetUserInfoHeap(Heap
, Flags
, BaseAddress
, UserValue
, UserFlags
);
3782 /* Lock if it's lockable */
3783 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3785 RtlEnterHeapLock(Heap
->LockVariable
, TRUE
);
3789 /* Get a pointer to the entry */
3790 HeapEntry
= (PHEAP_ENTRY
)BaseAddress
- 1;
3792 /* If it's a free entry - return error */
3793 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3795 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3797 /* Release the heap lock if it was acquired */
3799 RtlLeaveHeapLock(Heap
->LockVariable
);
3804 /* Check if this entry has an extra stuff associated with it */
3805 if (HeapEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
3807 /* Get pointer to extra data */
3808 Extra
= RtlpGetExtraStuffPointer(HeapEntry
);
3810 /* Pass user value */
3812 *UserValue
= (PVOID
)Extra
->Settable
;
3815 /* Decode and return user flags */
3817 *UserFlags
= (HeapEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4;
3819 /* Release the heap lock if it was acquired */
3821 RtlLeaveHeapLock(Heap
->LockVariable
);
3831 RtlUsageHeap(IN HANDLE Heap
,
3833 OUT PRTL_HEAP_USAGE Usage
)
3837 return STATUS_NOT_IMPLEMENTED
;
3842 RtlQueryTagHeap(IN PVOID HeapHandle
,
3845 IN BOOLEAN ResetCounters
,
3846 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
3855 RtlExtendHeap(IN HANDLE Heap
,
3867 RtlCreateTagHeap(IN HANDLE HeapHandle
,
3870 IN PWSTR TagSubName
)
3879 RtlWalkHeap(IN HANDLE HeapHandle
,
3883 return STATUS_NOT_IMPLEMENTED
;
3888 RtlProtectHeap(IN PVOID HeapHandle
,
3889 IN BOOLEAN ReadOnly
)
3897 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
3898 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
3899 IN PVOID HeapInformation
,
3900 IN SIZE_T HeapInformationLength
)
3902 /* Setting heap information is not really supported except for enabling LFH */
3903 if (HeapInformationClass
== HeapCompatibilityInformation
)
3905 /* Check buffer length */
3906 if (HeapInformationLength
< sizeof(ULONG
))
3908 /* The provided buffer is too small */
3909 return STATUS_BUFFER_TOO_SMALL
;
3912 /* Check for a special magic value for enabling LFH */
3913 if (*(PULONG
)HeapInformation
!= 2)
3915 return STATUS_UNSUCCESSFUL
;
3918 DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
3919 return STATUS_SUCCESS
;
3922 return STATUS_SUCCESS
;
3927 RtlQueryHeapInformation(HANDLE HeapHandle
,
3928 HEAP_INFORMATION_CLASS HeapInformationClass
,
3929 PVOID HeapInformation
,
3930 SIZE_T HeapInformationLength
,
3931 PSIZE_T ReturnLength OPTIONAL
)
3933 PHEAP Heap
= (PHEAP
)HeapHandle
;
3935 /* Only HeapCompatibilityInformation is supported */
3936 if (HeapInformationClass
== HeapCompatibilityInformation
)
3938 /* Set result length */
3940 *ReturnLength
= sizeof(ULONG
);
3942 /* Check buffer length */
3943 if (HeapInformationLength
< sizeof(ULONG
))
3945 /* It's too small, return needed length */
3946 return STATUS_BUFFER_TOO_SMALL
;
3949 /* Return front end heap type */
3950 *(PULONG
)HeapInformation
= Heap
->FrontEndHeapType
;
3952 return STATUS_SUCCESS
;
3955 return STATUS_UNSUCCESSFUL
;
3960 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
3972 RtlMultipleFreeHeap(IN PVOID HeapHandle
,