1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS system libraries
4 * PURPOSE: RTL Heap backend allocator
5 * PROGRAMMERS: Copyright 2010 Aleksey Bragin
9 http://msdn.microsoft.com/en-us/library/ms810466.aspx
10 http://msdn.microsoft.com/en-us/library/ms810603.aspx
11 http://www.securitylab.ru/analytics/216376.php
12 http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
13 http://www.phreedom.org/research/exploits/asn1-bitstring/
14 http://illmatics.com/Understanding_the_LFH.pdf
17 /* INCLUDES *****************************************************************/
24 // Various defines, to be moved to a separate header file
25 #define HEAP_FREELISTS 128
26 #define HEAP_SEGMENTS 64
27 #define HEAP_MAX_PROCESS_HEAPS 16
29 #define HEAP_ENTRY_SIZE ((ULONG)sizeof(HEAP_ENTRY))
30 #define HEAP_ENTRY_SHIFT 3
31 #define HEAP_MAX_BLOCK_SIZE ((0x80000 - PAGE_SIZE) >> HEAP_ENTRY_SHIFT)
33 #define ARENA_INUSE_FILLER 0xBAADF00D
34 #define ARENA_FREE_FILLER 0xFEEEFEEE
35 #define HEAP_TAIL_FILL 0xab
37 // from ntifs.h, should go to another header!
38 #define HEAP_GLOBAL_TAG 0x0800
39 #define HEAP_PSEUDO_TAG_FLAG 0x8000
40 #define HEAP_TAG_MASK (HEAP_MAXIMUM_TAG << HEAP_TAG_SHIFT)
42 #define HEAP_EXTRA_FLAGS_MASK (HEAP_CAPTURE_STACK_BACKTRACES | \
43 HEAP_SETTABLE_USER_VALUE | \
44 (HEAP_TAG_MASK ^ (0xFF << HEAP_TAG_SHIFT)))
46 struct _HEAP_COMMON_ENTRY
54 UCHAR SmallTagIndex
; //0x3
58 PVOID SubSegmentCode
; // 0x0
59 USHORT PreviousSize
; // 0x4
62 UCHAR SegmentOffset
; // 0x6
63 UCHAR LFHFlags
; // 0x6
65 UCHAR UnusedBytes
; // 0x7
69 USHORT FunctionIndex
; // 0x0
70 USHORT ContextValue
; // 0x2
74 ULONG InterceptorValue
; // 0x0
75 USHORT UnusedBytesLength
; // 0x4
76 UCHAR EntryOffset
; // 0x6
77 UCHAR ExtendedBlockSignature
; // 0x7
86 ULONGLONG AgregateCode
; // 0x0
90 typedef struct _HEAP_FREE_ENTRY
92 struct _HEAP_COMMON_ENTRY
;
93 LIST_ENTRY FreeList
; // 0x8
94 } HEAP_FREE_ENTRY
, *PHEAP_FREE_ENTRY
;
96 typedef struct _HEAP_ENTRY
98 struct _HEAP_COMMON_ENTRY
;
99 } HEAP_ENTRY
, *PHEAP_ENTRY
;
101 C_ASSERT(sizeof(HEAP_ENTRY
) == 8);
102 C_ASSERT((1 << HEAP_ENTRY_SHIFT
) == sizeof(HEAP_ENTRY
));
104 typedef struct _HEAP_TAG_ENTRY
110 USHORT CreatorBackTraceIndex
;
112 } HEAP_TAG_ENTRY
, *PHEAP_TAG_ENTRY
;
114 typedef struct _HEAP_PSEUDO_TAG_ENTRY
119 } HEAP_PSEUDO_TAG_ENTRY
, *PHEAP_PSEUDO_TAG_ENTRY
;
121 typedef struct _HEAP_COUNTERS
123 ULONG TotalMemoryReserved
;
124 ULONG TotalMemoryCommitted
;
125 ULONG TotalMemoryLargeUCR
;
126 ULONG TotalSizeInVirtualBlocks
;
132 ULONG LockCollisions
;
135 ULONG CommitFailures
;
136 ULONG InBlockCommitFailures
;
137 ULONG CompactHeapCalls
;
139 ULONG InBlockDeccommits
;
140 ULONG InBlockDeccomitSize
;
141 } HEAP_COUNTERS
, *PHEAP_COUNTERS
;
143 typedef struct _HEAP_TUNING_PARAMETERS
145 ULONG CommittThresholdShift
;
146 ULONG MaxPreCommittThreshold
;
147 } HEAP_TUNING_PARAMETERS
, *PHEAP_TUNING_PARAMETERS
;
152 ULONG SegmentSignature
;
154 LIST_ENTRY SegmentListEntry
;
158 PHEAP_ENTRY FirstEntry
;
159 PHEAP_ENTRY LastValidEntry
;
160 ULONG NumberOfUnCommittedPages
;
161 ULONG NumberOfUnCommittedRanges
;
162 USHORT SegmentAllocatorBackTraceIndex
;
164 LIST_ENTRY UCRSegmentList
;
167 ULONG CompatibilityFlags
;
168 ULONG EncodeFlagMask
;
172 ULONG VirtualMemoryThreshold
;
174 ULONG SegmentReserve
;
176 ULONG DeCommitFreeBlockThreshold
;
177 ULONG DeCommitTotalFreeThreshold
;
179 ULONG MaximumAllocationSize
;
180 USHORT ProcessHeapsListIndex
;
181 USHORT HeaderValidateLength
;
182 PVOID HeaderValidateCopy
;
183 USHORT NextAvailableTagIndex
;
184 USHORT MaximumTagIndex
;
185 PHEAP_TAG_ENTRY TagEntries
;
189 LIST_ENTRY VirtualAllocdBlocks
;
190 LIST_ENTRY SegmentList
;
191 struct _HEAP_SEGMENT
*Segments
[HEAP_SEGMENTS
]; //FIXME: non-Vista
192 USHORT AllocatorBackTraceIndex
;
193 ULONG NonDedicatedListLength
;
196 PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries
;
197 LIST_ENTRY FreeLists
[HEAP_FREELISTS
]; //FIXME: non-Vista
200 ULONG FreeListsInUseUlong
[HEAP_FREELISTS
/ (sizeof(ULONG
) * 8)]; //FIXME: non-Vista
201 UCHAR FreeListsInUseBytes
[HEAP_FREELISTS
/ (sizeof(UCHAR
) * 8)]; //FIXME: non-Vista
203 PHEAP_LOCK LockVariable
;
204 PRTL_HEAP_COMMIT_ROUTINE CommitRoutine
;
206 USHORT FrontHeapLockCount
;
207 UCHAR FrontEndHeapType
;
208 HEAP_COUNTERS Counters
;
209 HEAP_TUNING_PARAMETERS TuningParameters
;
212 typedef struct _HEAP_SEGMENT
215 ULONG SegmentSignature
;
217 LIST_ENTRY SegmentListEntry
;
221 PHEAP_ENTRY FirstEntry
;
222 PHEAP_ENTRY LastValidEntry
;
223 ULONG NumberOfUnCommittedPages
;
224 ULONG NumberOfUnCommittedRanges
;
225 USHORT SegmentAllocatorBackTraceIndex
;
227 LIST_ENTRY UCRSegmentList
;
228 PHEAP_ENTRY LastEntryInSegment
; //FIXME: non-Vista
229 } HEAP_SEGMENT
, *PHEAP_SEGMENT
;
231 typedef struct _HEAP_UCR_DESCRIPTOR
233 LIST_ENTRY ListEntry
;
234 LIST_ENTRY SegmentEntry
;
237 } HEAP_UCR_DESCRIPTOR
, *PHEAP_UCR_DESCRIPTOR
;
239 typedef struct _HEAP_ENTRY_EXTRA
245 USHORT AllocatorBackTraceIndex
;
251 } HEAP_ENTRY_EXTRA
, *PHEAP_ENTRY_EXTRA
;
253 typedef HEAP_ENTRY_EXTRA HEAP_FREE_ENTRY_EXTRA
, *PHEAP_FREE_ENTRY_EXTRA
;
255 typedef struct _HEAP_VIRTUAL_ALLOC_ENTRY
258 HEAP_ENTRY_EXTRA ExtraStuff
;
261 HEAP_ENTRY BusyBlock
;
262 } HEAP_VIRTUAL_ALLOC_ENTRY
, *PHEAP_VIRTUAL_ALLOC_ENTRY
;
264 extern BOOLEAN RtlpPageHeapEnabled
;
266 RtlpSpecialHeapCreate(ULONG Flags
,
271 PRTL_HEAP_PARAMETERS Parameters
) { return NULL
; };
273 HEAP_LOCK RtlpProcessHeapsListLock
;
274 PHEAP RtlpProcessHeaps
[HEAP_MAX_PROCESS_HEAPS
]; /* Usermode only */
276 /* Heap entry flags */
277 #define HEAP_ENTRY_BUSY 0x01
278 #define HEAP_ENTRY_EXTRA_PRESENT 0x02
279 #define HEAP_ENTRY_FILL_PATTERN 0x04
280 #define HEAP_ENTRY_VIRTUAL_ALLOC 0x08
281 #define HEAP_ENTRY_LAST_ENTRY 0x10
282 #define HEAP_ENTRY_SETTABLE_FLAG1 0x20
283 #define HEAP_ENTRY_SETTABLE_FLAG2 0x40
284 #define HEAP_ENTRY_SETTABLE_FLAG3 0x80
285 #define HEAP_ENTRY_SETTABLE_FLAGS (HEAP_ENTRY_SETTABLE_FLAG1 | HEAP_ENTRY_SETTABLE_FLAG2 | HEAP_ENTRY_SETTABLE_FLAG3)
288 #define HEAP_SIGNATURE 0xeefeeff
289 #define HEAP_SEGMENT_SIGNATURE 0xffeeffee
292 #define HEAP_USER_ALLOCATED 0x1
296 /* How many least significant bits are clear */
297 UCHAR RtlpBitsClearLow
[] =
299 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
300 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
301 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
302 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
303 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
304 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
305 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
306 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
307 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
308 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
309 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
310 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
311 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
312 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
313 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
314 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
318 RtlpFindLeastSetBit(ULONG Bits
)
323 return RtlpBitsClearLow
[Bits
& 0xFF]; /* Lowest byte */
325 return RtlpBitsClearLow
[(Bits
>> 8) & 0xFF] + 8; /* 2nd byte */
329 if ((Bits
>> 16) & 0xFF)
330 return RtlpBitsClearLow
[(Bits
>> 16) & 0xFF] + 16; /* 3rd byte */
332 return RtlpBitsClearLow
[(Bits
>> 24) & 0xFF] + 24; /* Highest byte */
337 RtlCompareMemoryUlong(PVOID Source
, ULONG Length
, ULONG Value
);
339 PHEAP_FREE_ENTRY NTAPI
340 RtlpCoalesceFreeBlocks (PHEAP Heap
,
341 PHEAP_FREE_ENTRY FreeEntry
,
345 PHEAP_ENTRY_EXTRA NTAPI
346 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
);
348 /* FUNCTIONS *****************************************************************/
351 RtlpInitializeHeap(PHEAP Heap
,
354 BOOLEAN AllocateLock
,
357 PVOID NextHeapBase
= Heap
+ 1;
358 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
364 *HeaderSize
+= NumUCRs
* sizeof(*UcrDescriptor
);
366 /* Prepare a list of UCRs */
367 InitializeListHead(&Heap
->UCRList
);
368 InitializeListHead(&Heap
->UCRSegmentList
);
369 UcrDescriptor
= NextHeapBase
;
371 for (i
=0; i
<NumUCRs
; i
++, UcrDescriptor
++)
373 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
376 NextHeapBase
= UcrDescriptor
;
379 /* Round up header size again */
380 *HeaderSize
= ROUND_UP(*HeaderSize
, HEAP_ENTRY_SIZE
);
382 ASSERT(*HeaderSize
<= PAGE_SIZE
);
384 /* Initialize heap's header */
385 Heap
->Entry
.Size
= (*HeaderSize
) >> HEAP_ENTRY_SHIFT
;
386 Heap
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
388 Heap
->Signature
= HEAP_SIGNATURE
;
390 Heap
->ForceFlags
= (Flags
& (HEAP_NO_SERIALIZE
|
391 HEAP_GENERATE_EXCEPTIONS
|
393 HEAP_REALLOC_IN_PLACE_ONLY
|
394 HEAP_VALIDATE_PARAMETERS_ENABLED
|
395 HEAP_VALIDATE_ALL_ENABLED
|
396 HEAP_TAIL_CHECKING_ENABLED
|
397 HEAP_CREATE_ALIGN_16
|
398 HEAP_FREE_CHECKING_ENABLED
));
399 Heap
->HeaderValidateCopy
= NULL
;
400 Heap
->HeaderValidateLength
= ((PCHAR
)NextHeapBase
- (PCHAR
)Heap
);
402 /* Initialize free lists */
403 for (i
=0; i
<HEAP_FREELISTS
; i
++)
405 InitializeListHead(&Heap
->FreeLists
[i
]);
408 /* Initialize "big" allocations list */
409 InitializeListHead(&Heap
->VirtualAllocdBlocks
);
411 /* Initialize lock */
415 Status
= RtlInitializeHeapLock((PHEAP_LOCK
)Lock
);
416 if (!NT_SUCCESS(Status
))
418 DPRINT1("Initializing the lock failed!\n");
419 return /*NULL*/; // FIXME!
423 /* Set the lock variable */
424 Heap
->LockVariable
= Lock
;
428 RtlpSetFreeListsBit(PHEAP Heap
,
429 PHEAP_FREE_ENTRY FreeEntry
)
433 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
435 /* Calculate offset in the free list bitmap */
436 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
437 Bit
= 1 << (FreeEntry
->Size
& 7);
439 /* Assure it's not already set */
440 ASSERT((Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
) == 0);
443 Heap
->u
.FreeListsInUseBytes
[Index
] |= Bit
;
447 RtlpClearFreeListsBit(PHEAP Heap
,
448 PHEAP_FREE_ENTRY FreeEntry
)
452 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
454 /* Calculate offset in the free list bitmap */
455 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
456 Bit
= 1 << (FreeEntry
->Size
& 7);
458 /* Assure it was set and the corresponding free list is empty */
459 ASSERT(Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
);
460 ASSERT(IsListEmpty(&Heap
->FreeLists
[FreeEntry
->Size
]));
463 Heap
->u
.FreeListsInUseBytes
[Index
] ^= Bit
;
467 RtlpInsertFreeBlockHelper(PHEAP Heap
,
468 PHEAP_FREE_ENTRY FreeEntry
,
472 PLIST_ENTRY FreeListHead
, Current
;
473 PHEAP_FREE_ENTRY CurrentEntry
;
475 ASSERT(FreeEntry
->Size
== BlockSize
);
477 /* Fill if it's not denied */
480 FreeEntry
->Flags
&= ~(HEAP_ENTRY_FILL_PATTERN
|
481 HEAP_ENTRY_EXTRA_PRESENT
|
484 if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
486 RtlFillMemoryUlong((PCHAR
)(FreeEntry
+ 1),
487 (BlockSize
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
),
490 FreeEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
495 /* Clear out all flags except the last entry one */
496 FreeEntry
->Flags
&= HEAP_ENTRY_LAST_ENTRY
;
499 /* Check if PreviousSize of the next entry matches ours */
500 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
502 ASSERT(((PHEAP_ENTRY
)FreeEntry
+ BlockSize
)->PreviousSize
= BlockSize
);
505 /* Insert it either into dedicated or non-dedicated list */
506 if (BlockSize
< HEAP_FREELISTS
)
509 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
511 if (IsListEmpty(FreeListHead
))
513 RtlpSetFreeListsBit(Heap
, FreeEntry
);
518 /* Non-dedicated one */
519 FreeListHead
= &Heap
->FreeLists
[0];
520 Current
= FreeListHead
->Flink
;
522 /* Find a position where to insert it to (the list must be sorted) */
523 while (FreeListHead
!= Current
)
525 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
527 if (BlockSize
<= CurrentEntry
->Size
)
530 Current
= Current
->Flink
;
533 FreeListHead
= Current
;
536 /* Actually insert it into the list */
537 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
541 RtlpInsertFreeBlock(PHEAP Heap
,
542 PHEAP_FREE_ENTRY FreeEntry
,
545 USHORT Size
, PreviousSize
;
546 UCHAR SegmentOffset
, Flags
;
547 PHEAP_SEGMENT Segment
;
549 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
551 /* Increase the free size counter */
552 Heap
->TotalFreeSize
+= BlockSize
;
554 /* Remember certain values */
555 Flags
= FreeEntry
->Flags
;
556 PreviousSize
= FreeEntry
->PreviousSize
;
557 SegmentOffset
= FreeEntry
->SegmentOffset
;
558 Segment
= Heap
->Segments
[SegmentOffset
];
563 /* Check for the max size */
564 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
566 Size
= HEAP_MAX_BLOCK_SIZE
;
568 /* Special compensation if it goes above limit just by 1 */
569 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
572 FreeEntry
->Flags
= 0;
577 FreeEntry
->Flags
= Flags
;
580 /* Change its size and insert it into a free list */
581 FreeEntry
->Size
= Size
;
582 FreeEntry
->PreviousSize
= PreviousSize
;
583 FreeEntry
->SegmentOffset
= SegmentOffset
;
585 /* Call a helper to actually insert the block */
586 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
592 /* Go to the next entry */
593 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
595 /* Check if that's all */
596 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
599 /* Update previous size if needed */
600 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
601 FreeEntry
->PreviousSize
= PreviousSize
;
605 RtlpRemoveFreeBlock(PHEAP Heap
,
606 PHEAP_FREE_ENTRY FreeEntry
,
610 SIZE_T Result
, RealSize
;
611 PLIST_ENTRY OldBlink
, OldFlink
;
613 // FIXME: Maybe use RemoveEntryList?
615 /* Remove the free block */
616 OldFlink
= FreeEntry
->FreeList
.Flink
;
617 OldBlink
= FreeEntry
->FreeList
.Blink
;
618 OldBlink
->Flink
= OldFlink
;
619 OldFlink
->Blink
= OldBlink
;
621 /* Update the freelists bitmap */
622 if ((OldFlink
== OldBlink
) &&
623 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
625 RtlpClearFreeListsBit(Heap
, FreeEntry
);
628 /* Fill with pattern if necessary */
630 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
632 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
634 /* Deduct extra stuff from block's real size */
635 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
636 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
638 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
641 /* Check if the free filler is intact */
642 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
646 if (Result
!= RealSize
)
648 DPRINT1("Free heap block %p modified at %p after it was freed\n",
650 (PCHAR
)(FreeEntry
+ 1) + Result
);
656 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
658 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
660 /* Get pointer to the containing record */
661 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
663 /* Restore the real size */
664 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
667 PHEAP_UCR_DESCRIPTOR NTAPI
668 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
671 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
672 PHEAP Heap
= Segment
->Heap
;
674 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
676 /* Check if we have unused UCRs */
677 if (IsListEmpty(&Heap
->UCRList
))
682 /* There are unused UCRs, just get the first one */
683 Entry
= RemoveHeadList(&Heap
->UCRList
);
684 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
685 return UcrDescriptor
;
689 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
690 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
693 UcrDescriptor
->Address
= NULL
;
694 UcrDescriptor
->Size
= 0;
696 /* Put it into the heap's list of unused UCRs */
697 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
701 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
706 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
708 DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment
, Address
, Size
);
710 /* Go through the list of UCR descriptors, they are sorted from lowest address
712 Current
= Segment
->UCRSegmentList
.Flink
;
713 while(Current
!= &Segment
->UCRSegmentList
)
715 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
717 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
719 /* Check for a really lucky case */
720 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
723 UcrDescriptor
->Address
= (PVOID
)Address
;
724 UcrDescriptor
->Size
+= Size
;
728 /* We found the block after which the new one should go */
731 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
733 /* Modify this entry */
734 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
735 Size
+= UcrDescriptor
->Size
;
737 /* Remove it from the list and destroy it */
738 RemoveEntryList(Current
);
739 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
741 Segment
->NumberOfUnCommittedRanges
--;
745 /* Advance to the next descriptor */
746 Current
= Current
->Flink
;
750 /* Create a new UCR descriptor */
751 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
752 if (!UcrDescriptor
) return;
754 UcrDescriptor
->Address
= (PVOID
)Address
;
755 UcrDescriptor
->Size
= Size
;
757 /* "Current" is the descriptor after which our one should go */
758 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
760 DPRINT("Added segment UCR with base %p, size 0x%x\n", Address
, Size
);
762 /* Increase counters */
763 Segment
->NumberOfUnCommittedRanges
++;
766 PHEAP_FREE_ENTRY NTAPI
767 RtlpFindAndCommitPages(PHEAP Heap
,
768 PHEAP_SEGMENT Segment
,
773 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
774 PHEAP_ENTRY FirstEntry
, LastEntry
, PreviousLastEntry
;
777 DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap
, Segment
, *Size
, Address
);
779 /* Go through UCRs in a segment */
780 Current
= Segment
->UCRSegmentList
.Flink
;
781 while(Current
!= &Segment
->UCRSegmentList
)
783 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
785 /* Check if we can use that one right away */
786 if (UcrDescriptor
->Size
>= *Size
&&
787 (UcrDescriptor
->Address
== Address
|| !Address
))
789 /* Get the address */
790 Address
= UcrDescriptor
->Address
;
793 if (Heap
->CommitRoutine
)
795 Status
= Heap
->CommitRoutine(Heap
, &Address
, Size
);
799 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
807 DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size
, Address
, UcrDescriptor
->Size
);
809 /* Fail in unsuccessful case */
810 if (!NT_SUCCESS(Status
))
812 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
816 /* Update tracking numbers */
817 Segment
->NumberOfUnCommittedPages
-= *Size
/ PAGE_SIZE
;
819 /* Calculate first and last entries */
820 FirstEntry
= (PHEAP_ENTRY
)Address
;
822 if ((Segment
->LastEntryInSegment
->Flags
& HEAP_ENTRY_LAST_ENTRY
) &&
823 (ULONG_PTR
)(Segment
->LastEntryInSegment
+ Segment
->LastEntryInSegment
->Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
825 LastEntry
= Segment
->LastEntryInSegment
;
829 /* Go through the entries to find the last one */
832 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
834 LastEntry
= Segment
->FirstEntry
;
836 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
838 PreviousLastEntry
= LastEntry
;
839 LastEntry
+= LastEntry
->Size
;
841 if ((ULONG_PTR
)LastEntry
>= (ULONG_PTR
)Segment
->LastValidEntry
||
842 LastEntry
->Size
== 0)
844 if (LastEntry
== (PHEAP_ENTRY
)Address
)
847 LastEntry
= PreviousLastEntry
;
851 DPRINT1("Last entry not found in a committed range near to %p\n", PreviousLastEntry
);
857 /* Unmark it as a last entry */
858 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
860 /* Update UCR descriptor */
861 UcrDescriptor
->Address
= (PUCHAR
)UcrDescriptor
->Address
+ *Size
;
862 UcrDescriptor
->Size
-= *Size
;
864 DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
865 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
867 /* Check if anything left in this UCR */
868 if (UcrDescriptor
->Size
== 0)
870 /* It's fully exhausted */
871 if (UcrDescriptor
->Address
== Segment
->LastValidEntry
)
873 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
874 Segment
->LastEntryInSegment
= FirstEntry
;
878 FirstEntry
->Flags
= 0;
879 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
882 /* This UCR needs to be removed because it became useless */
883 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
885 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
886 Segment
->NumberOfUnCommittedRanges
--;
890 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
891 Segment
->LastEntryInSegment
= FirstEntry
;
894 /* Set various first entry fields*/
895 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
896 FirstEntry
->Size
= *Size
>> HEAP_ENTRY_SHIFT
;
897 FirstEntry
->PreviousSize
= LastEntry
->Size
;
899 /* Update previous size */
900 if (!(FirstEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
901 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
904 return (PHEAP_FREE_ENTRY
)FirstEntry
;
907 /* Advance to the next descriptor */
908 Current
= Current
->Flink
;
915 RtlpDeCommitFreeBlock(PHEAP Heap
,
916 PHEAP_FREE_ENTRY FreeEntry
,
920 PHEAP_SEGMENT Segment
;
921 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
922 PHEAP_FREE_ENTRY NextFreeEntry
;
923 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
924 ULONG PrecedingSize
, NextSize
, DecommitSize
;
928 /* We can't decommit if there is a commit routine! */
929 if (Heap
->CommitRoutine
)
931 /* Just add it back the usual way */
932 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
936 /* Get the segment */
937 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
939 /* Get the preceding entry */
940 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
941 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
943 if (PrecedingSize
== 1)
945 /* Just 1 heap entry, increase the base/size */
946 DecommitBase
+= PAGE_SIZE
;
947 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
949 else if (FreeEntry
->PreviousSize
&&
950 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
952 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
955 /* Get the next entry */
956 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ (Size
>> HEAP_ENTRY_SHIFT
));
957 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
958 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
962 /* Just 1 heap entry, increase the size */
963 DecommitSize
-= PAGE_SIZE
;
964 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
966 else if (NextSize
== 0 &&
967 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
969 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
972 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
974 if (DecommitSize
> DecommitBase
)
975 DecommitSize
-= DecommitBase
;
978 /* Nothing to decommit */
979 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
983 /* A decommit is necessary. Create a UCR descriptor */
984 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
987 DPRINT1("HEAP: Failed to create UCR descriptor\n");
988 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
992 /* Decommit the memory */
993 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
994 (PVOID
*)&DecommitBase
,
998 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
999 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
1001 if (!NT_SUCCESS(Status
))
1003 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
1007 /* Insert uncommitted pages */
1008 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
1009 Segment
->NumberOfUnCommittedPages
+= (DecommitSize
/ PAGE_SIZE
);
1013 /* Adjust size of this free entry and insert it */
1014 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
1015 FreeEntry
->Size
= PrecedingSize
;
1016 Heap
->TotalFreeSize
+= PrecedingSize
;
1017 Segment
->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
1018 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
);
1020 else if (NextInUseEntry
)
1022 /* Adjust preceding in use entry */
1023 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1024 Segment
->LastEntryInSegment
= PrecedingInUseEntry
;
1026 else if ((Segment
->LastEntryInSegment
>= (PHEAP_ENTRY
)DecommitBase
))
1028 /* Adjust last entry in the segment */
1029 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
1032 /* Now the next one */
1035 /* Adjust size of this free entry and insert it */
1036 NextFreeEntry
->Flags
= 0;
1037 NextFreeEntry
->PreviousSize
= 0;
1038 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
1039 NextFreeEntry
->Size
= NextSize
;
1041 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= NextSize
;
1043 Heap
->TotalFreeSize
+= PrecedingSize
;
1044 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
);
1046 else if (NextInUseEntry
)
1048 NextInUseEntry
->PreviousSize
= 0;
1051 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
1056 RtlpInitializeHeapSegment(PHEAP Heap
,
1057 PHEAP_SEGMENT Segment
,
1061 PVOID UncommittedBase
,
1064 ULONG Pages
, CommitSize
;
1065 PHEAP_ENTRY HeapEntry
;
1066 USHORT PreviousSize
= 0, NewSize
;
1069 Pages
= ((PCHAR
)LimitAddress
- (PCHAR
)BaseAddress
) / PAGE_SIZE
;
1071 HeapEntry
= (PHEAP_ENTRY
)ROUND_UP(Segment
+ 1, HEAP_ENTRY_SIZE
);
1073 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %p %p %p)\n", Heap
, Segment
, SegmentIndex
, Flags
, BaseAddress
, UncommittedBase
, LimitAddress
);
1074 DPRINT("Pages %x, HeapEntry %p, sizeof(HEAP_SEGMENT) %x\n", Pages
, HeapEntry
, sizeof(HEAP_SEGMENT
));
1076 /* Check if it's the first segment and remember its size */
1077 if (Heap
== BaseAddress
)
1078 PreviousSize
= Heap
->Entry
.Size
;
1080 NewSize
= ((PCHAR
)HeapEntry
- (PCHAR
)Segment
) >> HEAP_ENTRY_SHIFT
;
1082 if ((PVOID
)(HeapEntry
+ 1) >= UncommittedBase
)
1084 /* Check if it goes beyond the limit */
1085 if ((PVOID
)(HeapEntry
+ 1) >= LimitAddress
)
1088 /* Need to commit memory */
1089 CommitSize
= (PCHAR
)(HeapEntry
+ 1) - (PCHAR
)UncommittedBase
;
1090 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1091 (PVOID
)&UncommittedBase
,
1096 if (!NT_SUCCESS(Status
))
1098 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
1102 DPRINT("Committed %d bytes at base %p\n", CommitSize
, UncommittedBase
);
1104 /* Calcule the new uncommitted base */
1105 UncommittedBase
= (PVOID
)((PCHAR
)UncommittedBase
+ CommitSize
);
1108 /* Initialize the segment entry */
1109 Segment
->Entry
.PreviousSize
= PreviousSize
;
1110 Segment
->Entry
.Size
= NewSize
;
1111 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
1112 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
1114 /* Initialize the segment itself */
1115 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
1116 Segment
->Heap
= Heap
;
1117 Segment
->BaseAddress
= BaseAddress
;
1118 Segment
->FirstEntry
= HeapEntry
;
1119 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((PCHAR
)BaseAddress
+ Pages
* PAGE_SIZE
);
1120 Segment
->NumberOfPages
= Pages
;
1121 Segment
->NumberOfUnCommittedPages
= ((PCHAR
)LimitAddress
- (PCHAR
)UncommittedBase
) / PAGE_SIZE
;
1122 InitializeListHead(&Segment
->UCRSegmentList
);
1124 /* Insert uncommitted pages into UCR (uncommitted ranges) list */
1125 if (Segment
->NumberOfUnCommittedPages
)
1127 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
)UncommittedBase
, Segment
->NumberOfUnCommittedPages
* PAGE_SIZE
);
1130 /* Set the segment index pointer */
1131 Heap
->Segments
[SegmentIndex
] = Segment
;
1133 /* Prepare a free heap entry */
1134 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
1135 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
1136 HeapEntry
->SegmentOffset
= SegmentIndex
;
1138 /* Set last entry in segment */
1139 Segment
->LastEntryInSegment
= HeapEntry
;
1142 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, (PHEAP_ENTRY
)UncommittedBase
- HeapEntry
);
1148 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
1154 /* Make sure it's not user allocated */
1155 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
1157 BaseAddress
= Segment
->BaseAddress
;
1158 DPRINT1("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
1160 /* Release virtual memory */
1161 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
1166 if (!NT_SUCCESS(Status
))
1168 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
1172 /* Usermode only! */
1174 RtlpAddHeapToProcessList(PHEAP Heap
)
1179 Peb
= RtlGetCurrentPeb();
1181 /* Acquire the lock */
1182 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1185 /* Check if max number of heaps reached */
1186 if (Peb
->NumberOfHeaps
== Peb
->MaximumNumberOfHeaps
)
1188 // TODO: Handle this case
1192 /* Add the heap to the process heaps */
1193 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = Heap
;
1194 Peb
->NumberOfHeaps
++;
1195 Heap
->ProcessHeapsListIndex
= Peb
->NumberOfHeaps
;
1196 // } _SEH2_FINALLY {
1198 /* Release the lock */
1199 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1204 /* Usermode only! */
1206 RtlpRemoveHeapFromProcessList(PHEAP Heap
)
1209 PHEAP
*Current
, *Next
;
1213 Peb
= RtlGetCurrentPeb();
1215 /* Acquire the lock */
1216 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1218 /* Check if we don't need anything to do */
1219 if ((Heap
->ProcessHeapsListIndex
== 0) ||
1220 (Heap
->ProcessHeapsListIndex
> Peb
->NumberOfHeaps
) ||
1221 (Peb
->NumberOfHeaps
== 0))
1223 /* Release the lock */
1224 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1229 /* The process actually has more than one heap.
1230 Use classic, lernt from university times algorithm for removing an entry
1231 from a static array */
1233 Current
= (PHEAP
*)&Peb
->ProcessHeaps
[Heap
->ProcessHeapsListIndex
- 1];
1236 /* How many items we need to shift to the left */
1237 Count
= Peb
->NumberOfHeaps
- (Heap
->ProcessHeapsListIndex
- 1);
1239 /* Move them all in a loop */
1242 /* Copy it and advance next pointer */
1245 /* Update its index */
1246 (*Current
)->ProcessHeapsListIndex
-= 1;
1248 /* Advance pointers */
1253 /* Decrease total number of heaps */
1254 Peb
->NumberOfHeaps
--;
1256 /* Zero last unused item */
1257 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = NULL
;
1258 Heap
->ProcessHeapsListIndex
= 0;
1260 /* Release the lock */
1261 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1264 PHEAP_FREE_ENTRY NTAPI
1265 RtlpCoalesceHeap(PHEAP Heap
)
1271 PHEAP_FREE_ENTRY NTAPI
1272 RtlpCoalesceFreeBlocks (PHEAP Heap
,
1273 PHEAP_FREE_ENTRY FreeEntry
,
1277 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
1279 /* Get the previous entry */
1280 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
1283 if (CurrentEntry
!= FreeEntry
&&
1284 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1285 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
1287 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
1289 /* Remove it if asked for */
1292 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1293 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1295 /* Remove it only once! */
1299 /* Remove previous entry too */
1300 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1303 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1305 /* Update last entry in the segment */
1306 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1307 Heap
->Segments
[CurrentEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)CurrentEntry
;
1309 /* Advance FreeEntry and update sizes */
1310 FreeEntry
= CurrentEntry
;
1311 *FreeSize
+= CurrentEntry
->Size
;
1312 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1313 FreeEntry
->Size
= *FreeSize
;
1315 /* Also update previous size if needed */
1316 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1318 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1322 /* Check the next block if it exists */
1323 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1325 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1327 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1328 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1330 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1332 /* Remove it if asked for */
1335 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1336 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1340 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1342 /* Update last entry in the segment */
1343 if (FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1344 Heap
->Segments
[FreeEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
1346 /* Remove next entry now */
1347 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1350 *FreeSize
+= NextEntry
->Size
;
1351 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1352 FreeEntry
->Size
= *FreeSize
;
1354 /* Also update previous size if needed */
1355 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1357 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1364 PHEAP_FREE_ENTRY NTAPI
1365 RtlpExtendHeap(PHEAP Heap
,
1369 UCHAR Index
, EmptyIndex
;
1370 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1371 PHEAP_SEGMENT Segment
;
1372 PHEAP_FREE_ENTRY FreeEntry
;
1375 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1377 /* Calculate amount in pages */
1378 Pages
= (Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
1379 FreeSize
= Pages
* PAGE_SIZE
;
1380 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1382 /* Find an empty segment */
1383 EmptyIndex
= HEAP_SEGMENTS
;
1384 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1386 Segment
= Heap
->Segments
[Index
];
1388 if (Segment
) DPRINT("Segment[%d] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1390 /* Check if its size suits us */
1392 Pages
<= Segment
->NumberOfUnCommittedPages
)
1394 DPRINT("This segment is suitable\n");
1396 /* Commit needed amount */
1397 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1399 /* Coalesce it with adjacent entries */
1402 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1403 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1404 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1408 else if (!Segment
&&
1409 EmptyIndex
== HEAP_SEGMENTS
)
1411 /* Remember the first unused segment index */
1416 /* No luck, need to grow the heap */
1417 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1418 (EmptyIndex
!= HEAP_SEGMENTS
))
1422 /* Reserve the memory */
1423 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1424 ReserveSize
= Heap
->SegmentReserve
;
1426 ReserveSize
= Size
+ PAGE_SIZE
;
1428 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1435 /* If it failed, retry again with a half division algorithm */
1436 while (!NT_SUCCESS(Status
) &&
1437 ReserveSize
!= Size
+ PAGE_SIZE
)
1441 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1442 ReserveSize
= Size
+ PAGE_SIZE
;
1444 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1452 /* Proceed only if it's success */
1453 if (NT_SUCCESS(Status
))
1455 Heap
->SegmentReserve
+= ReserveSize
;
1457 /* Now commit the memory */
1458 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1459 CommitSize
= Heap
->SegmentCommit
;
1461 CommitSize
= Size
+ PAGE_SIZE
;
1463 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1470 DPRINT("Committed %d bytes at base %p\n", CommitSize
, Segment
);
1472 /* Initialize heap segment if commit was successful */
1473 if (NT_SUCCESS(Status
))
1475 if (!RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, Segment
,
1476 (PCHAR
)Segment
+ CommitSize
, (PCHAR
)Segment
+ ReserveSize
))
1478 Status
= STATUS_NO_MEMORY
;
1482 /* If everything worked - cool */
1483 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1485 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1487 /* Nope, we failed. Free memory */
1488 ZwFreeVirtualMemory(NtCurrentProcess(),
1495 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1499 if (RtlpGetMode() == UserMode
)
1501 /* If coalescing on free is disabled in usermode, then do it here */
1502 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1504 FreeEntry
= RtlpCoalesceHeap(Heap
);
1506 /* If it's a suitable one - return it */
1508 FreeEntry
->Size
>= Size
)
1518 /***********************************************************************
1521 * Handle of heap: Success
1527 RtlCreateHeap(ULONG Flags
,
1532 PRTL_HEAP_PARAMETERS Parameters
)
1534 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1536 RTL_HEAP_PARAMETERS SafeParams
= {0};
1538 ULONG_PTR MaximumUserModeAddress
;
1539 SYSTEM_BASIC_INFORMATION SystemInformation
;
1540 MEMORY_BASIC_INFORMATION MemoryInfo
;
1541 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1542 ULONG HeapSegmentFlags
= 0;
1544 ULONG MaxBlockSize
, HeaderSize
;
1545 BOOLEAN AllocateLock
= FALSE
;
1547 /* Check for a special heap */
1548 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1550 Heap
= RtlpSpecialHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1551 if (Heap
) return Heap
;
1554 DPRINT1("Enabling page heap failed\n");
1557 /* Check validation flags */
1558 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1560 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1561 Flags
&= HEAP_CREATE_VALID_MASK
;
1564 /* TODO: Capture parameters, once we decide to use SEH */
1565 if (!Parameters
) Parameters
= &SafeParams
;
1567 /* Check global flags */
1568 if (NtGlobalFlags
& FLG_HEAP_DISABLE_COALESCING
)
1569 Flags
|= HEAP_DISABLE_COALESCE_ON_FREE
;
1571 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1572 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1574 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1575 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1577 if (Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1578 DPRINT1("TailChecking!\n");
1580 if (RtlpGetMode() == UserMode
)
1582 /* Also check these flags if in usermode */
1583 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1584 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1586 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1587 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1589 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1590 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1593 Peb
= RtlGetCurrentPeb();
1595 /* Apply defaults for non-set parameters */
1596 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= Peb
->HeapSegmentCommit
;
1597 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= Peb
->HeapSegmentReserve
;
1598 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= Peb
->HeapDeCommitFreeBlockThreshold
;
1599 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= Peb
->HeapDeCommitTotalFreeThreshold
;
1603 /* Apply defaults for non-set parameters */
1605 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= MmHeapSegmentCommit
;
1606 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= MmHeapSegmentReserve
;
1607 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
1608 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
1612 // FIXME: Move to memory manager
1613 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= PAGE_SIZE
* 2;
1614 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= 1048576;
1615 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= PAGE_SIZE
;
1616 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= 65536;
1618 /* Get the max um address */
1619 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1621 sizeof(SystemInformation
),
1624 if (!NT_SUCCESS(Status
))
1626 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1630 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1632 /* Calculate max alloc size */
1633 if (!Parameters
->MaximumAllocationSize
)
1634 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1636 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1638 if (!Parameters
->VirtualMemoryThreshold
||
1639 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1641 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1644 /* Check reserve/commit sizes and set default values */
1647 CommitSize
= PAGE_SIZE
;
1649 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1651 TotalSize
= 64 * PAGE_SIZE
;
1655 /* Round up the commit size to be at least the page size */
1656 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1659 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1661 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1664 /* Calculate header size */
1665 HeaderSize
= sizeof(HEAP
);
1666 if (!(Flags
& HEAP_NO_SERIALIZE
))
1670 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
1674 HeaderSize
+= sizeof(HEAP_LOCK
);
1675 AllocateLock
= TRUE
;
1680 /* Invalid parameters */
1684 /* See if we are already provided with an address for the heap */
1687 if (Parameters
->CommitRoutine
)
1689 /* There is a commit routine, so no problem here, check params */
1690 if ((Flags
& HEAP_GROWABLE
) ||
1691 !Parameters
->InitialCommit
||
1692 !Parameters
->InitialReserve
||
1693 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1699 /* Calculate committed and uncommitted addresses */
1700 CommittedAddress
= Addr
;
1701 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1702 TotalSize
= Parameters
->InitialReserve
;
1704 /* Zero the initial page ourselves */
1705 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1708 /* Commit routine is absent, so query how much memory caller reserved */
1709 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1711 MemoryBasicInformation
,
1716 if (!NT_SUCCESS(Status
))
1718 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1723 if (MemoryInfo
.BaseAddress
!= Addr
||
1724 MemoryInfo
.State
== MEM_FREE
)
1729 /* Validation checks passed, set committed/uncommitted addresses */
1730 CommittedAddress
= Addr
;
1732 /* Check if it's committed or not */
1733 if (MemoryInfo
.State
== MEM_COMMIT
)
1735 /* Zero it out because it's already committed */
1736 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1738 /* Calculate uncommitted address value */
1739 CommitSize
= MemoryInfo
.RegionSize
;
1740 TotalSize
= CommitSize
;
1741 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1743 /* Check if uncommitted address is reserved */
1744 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1746 MemoryBasicInformation
,
1751 if (NT_SUCCESS(Status
) &&
1752 MemoryInfo
.State
== MEM_RESERVE
)
1754 /* It is, so add it up to the reserve size */
1755 TotalSize
+= MemoryInfo
.RegionSize
;
1760 /* It's not committed, inform following code that a commit is necessary */
1761 CommitSize
= PAGE_SIZE
;
1762 UncommittedAddress
= Addr
;
1766 /* Mark this as a user-committed mem */
1767 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1772 /* Check commit routine */
1773 if (Parameters
->CommitRoutine
) return NULL
;
1775 /* Reserve memory */
1776 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1783 if (!NT_SUCCESS(Status
))
1785 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1789 /* Set base addresses */
1790 CommittedAddress
= Heap
;
1791 UncommittedAddress
= Heap
;
1794 /* Check if we need to commit something */
1795 if (CommittedAddress
== UncommittedAddress
)
1797 /* Commit the required size */
1798 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1805 DPRINT("Committed %d bytes at base %p\n", CommitSize
, CommittedAddress
);
1807 if (!NT_SUCCESS(Status
))
1809 DPRINT1("Failure, Status 0x%08X\n", Status
);
1811 /* Release memory if it was reserved */
1812 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1820 /* Calculate new uncommitted address */
1821 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1824 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1826 /* Initialize the heap */
1827 RtlpInitializeHeap(Heap
, &HeaderSize
, Flags
, AllocateLock
, Lock
);
1829 /* Initialize heap's first segment */
1830 if (!RtlpInitializeHeapSegment(Heap
,
1831 (PHEAP_SEGMENT
)((PCHAR
)Heap
+ HeaderSize
),
1836 (PCHAR
)CommittedAddress
+ TotalSize
))
1838 DPRINT1("Failed to initialize heap segment\n");
1842 /* Set other data */
1843 Heap
->ProcessHeapsListIndex
= 0;
1844 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
1845 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
1846 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
1847 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
1848 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
1849 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, HEAP_ENTRY_SIZE
) >> HEAP_ENTRY_SHIFT
;
1850 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
1853 if (Flags
& HEAP_CREATE_ALIGN_16
)
1855 Heap
->AlignMask
= (ULONG
)~15;
1856 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
1860 Heap
->AlignMask
= (ULONG
)~(HEAP_ENTRY_SIZE
- 1);
1861 Heap
->AlignRound
= HEAP_ENTRY_SIZE
- 1 + sizeof(HEAP_ENTRY
);
1864 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1865 Heap
->AlignRound
+= HEAP_ENTRY_SIZE
;
1867 /* Add heap to process list in case of usermode heap */
1868 if (RtlpGetMode() == UserMode
)
1870 RtlpAddHeapToProcessList(Heap
);
1872 // FIXME: What about lookasides?
1878 /***********************************************************************
1887 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1888 * Failure: The Heap handle, if heap is the process heap.
1891 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1893 PHEAP Heap
= (PHEAP
)HeapPtr
;
1894 PLIST_ENTRY Current
;
1895 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
1896 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1900 PHEAP_SEGMENT Segment
;
1902 if (!HeapPtr
) return NULL
;
1904 // TODO: Check for special heap
1906 /* Check for a process heap */
1907 if (RtlpGetMode() == UserMode
&&
1908 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1910 /* Free up all big allocations */
1911 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1912 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1914 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1915 BaseAddress
= (PVOID
)VirtualEntry
;
1917 ZwFreeVirtualMemory(NtCurrentProcess(),
1923 /* Delete tags and remove heap from the process heaps list in user mode */
1924 if (RtlpGetMode() == UserMode
)
1926 // FIXME DestroyTags
1927 RtlpRemoveHeapFromProcessList(Heap
);
1930 /* Delete the heap lock */
1931 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1933 /* Delete it if it wasn't user allocated */
1934 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1935 RtlDeleteHeapLock(Heap
->LockVariable
);
1937 /* Clear out the lock variable */
1938 Heap
->LockVariable
= NULL
;
1941 /* Go through heap's global uncommitted ranges list and free them */
1942 DPRINT1("HEAP: Freeing segment's UCRs is not yet implemented!\n");
1943 Current
= Heap
->UCRSegmentList
.Flink
;
1944 while(Current
!= &Heap
->UCRSegmentList
)
1946 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
1950 BaseAddress
= UcrDescriptor
->Address
;
1953 /* Release that memory */
1954 ZwFreeVirtualMemory(NtCurrentProcess(),
1960 /* Advance to the next descriptor */
1961 Current
= Current
->Flink
;
1964 /* Go through segments and destroy them */
1965 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1967 Segment
= Heap
->Segments
[i
];
1968 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1975 RtlpSplitEntry(PHEAP Heap
,
1976 PHEAP_FREE_ENTRY FreeBlock
,
1977 SIZE_T AllocationSize
,
1981 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1983 PHEAP_ENTRY InUseEntry
;
1986 /* Save flags, update total free size */
1987 FreeFlags
= FreeBlock
->Flags
;
1988 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1990 /* Make this block an in-use one */
1991 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1992 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
;
1993 InUseEntry
->SmallTagIndex
= 0;
1995 /* Calculate the extra amount */
1996 FreeSize
= InUseEntry
->Size
- Index
;
1998 /* Update it's size fields (we don't need their data anymore) */
1999 InUseEntry
->Size
= Index
;
2000 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2002 /* If there is something to split - do the split */
2005 /* Don't split if resulting entry can't contain any payload data
2006 (i.e. being just HEAP_ENTRY_SIZE) */
2009 /* Increase sizes of the in-use entry */
2011 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
2015 /* Calculate a pointer to the new entry */
2016 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2019 SplitBlock
->Flags
= FreeFlags
;
2020 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2021 SplitBlock
->Size
= FreeSize
;
2022 SplitBlock
->PreviousSize
= Index
;
2024 /* Check if it's the last entry */
2025 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2027 /* Insert it to the free list if it's the last entry */
2028 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2029 Heap
->TotalFreeSize
+= FreeSize
;
2033 /* Not so easy - need to update next's previous size too */
2034 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2036 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2038 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
2039 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2040 Heap
->TotalFreeSize
+= FreeSize
;
2044 /* Even more complex - the next entry is free, so we can merge them into one! */
2045 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2047 /* Remove that next entry */
2048 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2051 FreeSize
+= SplitBlock2
->Size
;
2052 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2054 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2056 /* Insert it back */
2057 SplitBlock
->Size
= FreeSize
;
2059 /* Don't forget to update previous size of the next entry! */
2060 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2062 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2065 /* Actually insert it */
2066 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, (USHORT
)FreeSize
, FALSE
);
2068 /* Update total size */
2069 Heap
->TotalFreeSize
+= FreeSize
;
2073 /* Resulting block is quite big */
2074 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2079 /* Reset flags of the free entry */
2082 /* Update last entry in segment */
2083 if (SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
2085 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2090 /* Set last entry flag */
2091 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2092 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
2098 RtlpAllocateNonDedicated(PHEAP Heap
,
2101 SIZE_T AllocationSize
,
2105 PLIST_ENTRY FreeListHead
, Next
;
2106 PHEAP_FREE_ENTRY FreeBlock
;
2107 PHEAP_ENTRY InUseEntry
;
2108 PHEAP_ENTRY_EXTRA Extra
;
2109 EXCEPTION_RECORD ExceptionRecord
;
2111 /* Go through the zero list to find a place where to insert the new entry */
2112 FreeListHead
= &Heap
->FreeLists
[0];
2114 /* Start from the largest block to reduce time */
2115 Next
= FreeListHead
->Blink
;
2116 if (FreeListHead
!= Next
)
2118 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
2120 if (FreeBlock
->Size
>= Index
)
2122 /* Our request is smaller than the largest entry in the zero list */
2124 /* Go through the list to find insertion place */
2125 Next
= FreeListHead
->Flink
;
2126 while (FreeListHead
!= Next
)
2128 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
2130 if (FreeBlock
->Size
>= Index
)
2132 /* Found minimally fitting entry. Proceed to either using it as it is
2133 or splitting it to two entries */
2134 RemoveEntryList(&FreeBlock
->FreeList
);
2137 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2139 /* Release the lock */
2140 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2142 /* Zero memory if that was requested */
2143 if (Flags
& HEAP_ZERO_MEMORY
)
2144 RtlZeroMemory(InUseEntry
+ 1, Size
);
2145 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2147 /* Fill this block with a special pattern */
2148 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2151 /* Fill tail of the block with a special pattern too if requested */
2152 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2154 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2155 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2158 /* Prepare extra if it's present */
2159 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2161 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2162 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2167 /* Return pointer to the */
2168 return InUseEntry
+ 1;
2171 /* Advance to the next entry */
2177 /* Extend the heap, 0 list didn't have anything suitable */
2178 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
2180 /* Use the new biggest entry we've got */
2183 RemoveEntryList(&FreeBlock
->FreeList
);
2186 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2188 /* Release the lock */
2189 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2191 /* Zero memory if that was requested */
2192 if (Flags
& HEAP_ZERO_MEMORY
)
2193 RtlZeroMemory(InUseEntry
+ 1, Size
);
2194 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2196 /* Fill this block with a special pattern */
2197 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2200 /* Fill tail of the block with a special pattern too if requested */
2201 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2203 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2204 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2207 /* Prepare extra if it's present */
2208 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2210 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2211 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2216 /* Return pointer to the */
2217 return InUseEntry
+ 1;
2220 /* Really unfortunate, out of memory condition */
2221 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2223 /* Generate an exception */
2224 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2226 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2227 ExceptionRecord
.ExceptionRecord
= NULL
;
2228 ExceptionRecord
.NumberParameters
= 1;
2229 ExceptionRecord
.ExceptionFlags
= 0;
2230 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2232 RtlRaiseException(&ExceptionRecord
);
2235 /* Release the lock */
2236 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2240 /***********************************************************************
2241 * HeapAlloc (KERNEL32.334)
2243 * Pointer to allocated memory block
2245 * 0x7d030f60--invalid flags in RtlHeapAllocate
2249 RtlAllocateHeap(IN PVOID HeapPtr
,
2253 PHEAP Heap
= (PHEAP
)HeapPtr
;
2254 PULONG FreeListsInUse
;
2255 ULONG FreeListsInUseUlong
;
2256 SIZE_T AllocationSize
;
2258 PLIST_ENTRY FreeListHead
;
2259 PHEAP_ENTRY InUseEntry
;
2260 PHEAP_FREE_ENTRY FreeBlock
;
2261 ULONG InUseIndex
, i
;
2263 EXCEPTION_RECORD ExceptionRecord
;
2264 BOOLEAN HeapLocked
= FALSE
;
2265 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
2266 PHEAP_ENTRY_EXTRA Extra
;
2270 Flags
|= Heap
->ForceFlags
;
2272 /* Check for the maximum size */
2273 if (Size
>= 0x80000000)
2275 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2280 HEAP_VALIDATE_ALL_ENABLED
|
2281 HEAP_VALIDATE_PARAMETERS_ENABLED
|
2282 HEAP_FLAG_PAGE_ALLOCS
|
2283 HEAP_CREATE_ENABLE_TRACING
|
2284 HEAP_CREATE_ALIGN_16
))
2286 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
2289 if (Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2290 DPRINT1("TailChecking, Heap %p\n!", Heap
);
2292 /* Calculate allocation size and index */
2294 AllocationSize
= Size
;
2297 AllocationSize
= (AllocationSize
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2298 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2300 /* Acquire the lock if necessary */
2301 if (!(Flags
& HEAP_NO_SERIALIZE
))
2303 RtlEnterHeapLock(Heap
->LockVariable
);
2307 /* Depending on the size, the allocation is going to be done from dedicated,
2308 non-dedicated lists or a virtual block of memory */
2309 if (Index
< HEAP_FREELISTS
)
2311 FreeListHead
= &Heap
->FreeLists
[Index
];
2313 if (!IsListEmpty(FreeListHead
))
2315 /* There is a free entry in this list */
2316 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2320 /* Save flags and remove the free entry */
2321 FreeFlags
= FreeBlock
->Flags
;
2322 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2324 /* Update the total free size of the heap */
2325 Heap
->TotalFreeSize
-= Index
;
2327 /* Initialize this block */
2328 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2329 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2330 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2331 InUseEntry
->SmallTagIndex
= 0;
2335 /* Find smallest free block which this request could fit in */
2336 InUseIndex
= Index
>> 5;
2337 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2339 /* This bit magic disables all sizes which are less than the requested allocation size */
2340 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2342 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2344 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2346 /* Go through the list */
2347 for (i
= InUseIndex
; i
< 4; i
++)
2349 if (FreeListsInUseUlong
)
2351 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2355 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2358 /* Nothing found, search in the non-dedicated list */
2360 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2362 /* That list is found, now calculate exact block */
2363 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2365 /* Take this entry and remove it from the list of free blocks */
2366 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2369 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, FALSE
);
2372 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2375 /* Release the lock */
2376 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2378 /* Zero memory if that was requested */
2379 if (Flags
& HEAP_ZERO_MEMORY
)
2380 RtlZeroMemory(InUseEntry
+ 1, Size
);
2381 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2383 /* Fill this block with a special pattern */
2384 RtlFillMemoryUlong(InUseEntry
+ 1, Size
& ~0x3, ARENA_INUSE_FILLER
);
2387 /* Fill tail of the block with a special pattern too if requested */
2388 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2390 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
, sizeof(HEAP_ENTRY
), HEAP_TAIL_FILL
);
2391 InUseEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
2394 /* Prepare extra if it's present */
2395 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2397 Extra
= RtlpGetExtraStuffPointer(InUseEntry
);
2398 RtlZeroMemory(Extra
, sizeof(HEAP_ENTRY_EXTRA
));
2403 /* User data starts right after the entry's header */
2404 return InUseEntry
+ 1;
2406 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2408 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2409 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2411 else if (Heap
->Flags
& HEAP_GROWABLE
)
2413 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2414 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2416 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2417 (PVOID
*)&VirtualBlock
,
2423 if (!NT_SUCCESS(Status
))
2426 /* Release the lock */
2427 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2431 /* Initialize the newly allocated block */
2432 VirtualBlock
->BusyBlock
.Size
= (AllocationSize
- Size
);
2433 VirtualBlock
->BusyBlock
.Flags
= HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
| HEAP_ENTRY_BUSY
;
2434 VirtualBlock
->CommitSize
= AllocationSize
;
2435 VirtualBlock
->ReserveSize
= AllocationSize
;
2437 /* Insert it into the list of virtual allocations */
2438 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2440 /* Release the lock */
2441 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2443 /* Return pointer to user data */
2444 return VirtualBlock
+ 1;
2447 /* Generate an exception */
2448 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2450 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2451 ExceptionRecord
.ExceptionRecord
= NULL
;
2452 ExceptionRecord
.NumberParameters
= 1;
2453 ExceptionRecord
.ExceptionFlags
= 0;
2454 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2456 RtlRaiseException(&ExceptionRecord
);
2459 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL
);
2461 /* Release the lock */
2462 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2467 /***********************************************************************
2468 * HeapFree (KERNEL32.338)
2475 BOOLEAN NTAPI
RtlFreeHeap(
2476 HANDLE HeapPtr
, /* [in] Handle of heap */
2477 ULONG Flags
, /* [in] Heap freeing flags */
2478 PVOID Ptr
/* [in] Address of memory to free */
2482 PHEAP_ENTRY HeapEntry
;
2483 USHORT TagIndex
= 0;
2485 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2486 BOOLEAN Locked
= FALSE
;
2489 /* Freeing NULL pointer is a legal operation */
2490 if (!Ptr
) return TRUE
;
2492 /* Get pointer to the heap and force flags */
2493 Heap
= (PHEAP
)HeapPtr
;
2494 Flags
|= Heap
->ForceFlags
;
2496 /* Lock if necessary */
2497 if (!(Flags
& HEAP_NO_SERIALIZE
))
2499 RtlEnterHeapLock(Heap
->LockVariable
);
2503 /* Get pointer to the heap entry */
2504 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2506 /* Check this entry, fail if it's invalid */
2507 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2508 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2509 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2511 /* This is an invalid block */
2512 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2513 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2515 /* Release the heap lock */
2516 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2520 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2522 /* Big allocation */
2523 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2525 /* Remove it from the list */
2526 RemoveEntryList(&VirtualEntry
->Entry
);
2531 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2532 (PVOID
*)&VirtualEntry
,
2536 if (!NT_SUCCESS(Status
))
2538 DPRINT1("Failed releasing memory with Status 0x%08X\n", Status
);
2539 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status
);
2544 /* Normal allocation */
2545 BlockSize
= HeapEntry
->Size
;
2549 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2550 if (RtlpGetMode() == KernelMode
||
2551 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2553 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2554 (PHEAP_FREE_ENTRY
)HeapEntry
,
2559 /* If there is no need to decommit the block - put it into a free list */
2560 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2561 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2563 /* Check if it needs to go to a 0 list */
2564 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2566 /* General-purpose 0 list */
2567 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2571 /* Usual free list */
2572 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2574 /* Assert sizes are consistent */
2575 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2577 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2580 /* Increase the free size */
2581 Heap
->TotalFreeSize
+= BlockSize
;
2585 if (RtlpGetMode() == UserMode
&&
2594 /* Decommit this block */
2595 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2599 /* Release the heap lock */
2600 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2606 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2608 IN PHEAP_ENTRY InUseEntry
,
2612 /* We always fail growing in place now */
2616 PHEAP_ENTRY_EXTRA NTAPI
2617 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2619 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2621 /* Check if it's a big block */
2622 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2624 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2626 /* Return a pointer to the extra stuff*/
2627 return &VirtualEntry
->ExtraStuff
;
2631 /* This is a usual entry, which means extra stuff follows this block */
2632 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2637 /***********************************************************************
2640 * Heap [in] Handle of heap block
2641 * Flags [in] Heap reallocation flags
2642 * Ptr, [in] Address of memory to reallocate
2643 * Size [in] Number of bytes to reallocate
2646 * Pointer to reallocated memory block
2648 * 0x7d030f60--invalid flags in RtlHeapAllocate
2652 RtlReAllocateHeap(HANDLE HeapPtr
,
2657 PHEAP Heap
= (PHEAP
)HeapPtr
;
2658 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2659 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2660 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2661 BOOLEAN HeapLocked
= FALSE
;
2662 PVOID NewBaseAddress
;
2663 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2664 SIZE_T OldSize
, Index
, OldIndex
;
2668 SIZE_T RemainderBytes
, ExtraSize
;
2669 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2670 EXCEPTION_RECORD ExceptionRecord
;
2672 /* Return success in case of a null pointer */
2675 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS
);
2679 /* Force heap flags */
2680 Flags
|= Heap
->ForceFlags
;
2682 // Check for special heap
2684 /* Make sure size is valid */
2685 if (Size
>= 0x80000000)
2687 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY
);
2691 /* Calculate allocation size and index */
2692 if (!Size
) Size
= 1;
2693 AllocationSize
= (Size
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2695 /* Add up extra stuff, if it is present anywhere */
2696 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2697 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2698 Heap
->PseudoTagEntries
)
2700 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2703 /* Acquire the lock if necessary */
2704 if (!(Flags
& HEAP_NO_SERIALIZE
))
2706 RtlEnterHeapLock(Heap
->LockVariable
);
2708 Flags
^= HEAP_NO_SERIALIZE
;
2711 /* Get the pointer to the in-use entry */
2712 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2714 /* If that entry is not really in-use, we have a problem */
2715 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2717 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
2719 /* Release the lock and return */
2721 RtlLeaveHeapLock(Heap
->LockVariable
);
2725 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2727 /* This is a virtually allocated block. Get its size */
2728 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2730 /* Convert it to an index */
2731 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2733 /* Calculate new allocation size and round it to the page size */
2734 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2735 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2740 OldIndex
= InUseEntry
->Size
;
2742 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2745 /* Calculate new index */
2746 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2748 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2749 if (Index
<= OldIndex
)
2751 /* Difference must be greater than 1, adjust if it's not so */
2752 if (Index
+ 1 == OldIndex
)
2755 AllocationSize
+= sizeof(HEAP_ENTRY
);
2758 /* Calculate new size */
2759 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2761 /* Simple in case of a virtual alloc - just an unused size */
2762 InUseEntry
->Size
= AllocationSize
- Size
;
2764 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2766 /* There is extra stuff, take it into account */
2767 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2768 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2769 *NewExtra
= *OldExtra
;
2771 // FIXME Tagging, TagIndex
2773 /* Update unused bytes count */
2774 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2778 // FIXME Tagging, SmallTagIndex
2779 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2782 /* If new size is bigger than the old size */
2785 /* Zero out that additional space if required */
2786 if (Flags
& HEAP_ZERO_MEMORY
)
2788 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2790 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2792 /* Fill it on free if required */
2793 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2796 RemainderBytes
= 4 - RemainderBytes
;
2798 if (Size
> (OldSize
+ RemainderBytes
))
2800 /* Calculate actual amount of extra bytes to fill */
2801 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2803 /* Fill them if there are any */
2806 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2808 ARENA_INUSE_FILLER
);
2814 /* Fill tail of the heap entry if required */
2815 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2817 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2822 /* Check if the difference is significant or not */
2823 if (Index
!= OldIndex
)
2826 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2828 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2830 /* This is a virtual block allocation */
2831 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2835 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2836 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2838 /* Release the memory */
2839 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2840 (PVOID
*)&DecommitBase
,
2844 if (!NT_SUCCESS(Status
))
2846 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2850 /* Otherwise reduce the commit size */
2851 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2856 /* Reduce size of the block and possibly split it */
2857 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2859 /* Initialize this entry */
2860 SplitBlock
->Flags
= FreeFlags
;
2861 SplitBlock
->PreviousSize
= Index
;
2862 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2864 /* Remember free size */
2865 FreeSize
= InUseEntry
->Size
- Index
;
2868 InUseEntry
->Size
= Index
;
2869 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2871 /* Is that the last entry */
2872 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2874 /* Update segment's last entry */
2875 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2877 /* Set its size and insert it to the list */
2878 SplitBlock
->Size
= (USHORT
)FreeSize
;
2879 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2881 /* Update total free size */
2882 Heap
->TotalFreeSize
+= FreeSize
;
2886 /* Get the block after that one */
2887 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2889 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2891 /* It's in use, add it here*/
2892 SplitBlock
->Size
= (USHORT
)FreeSize
;
2894 /* Update previous size of the next entry */
2895 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2897 /* Insert it to the list */
2898 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2900 /* Update total size */
2901 Heap
->TotalFreeSize
+= FreeSize
;
2905 /* Next entry is free, so merge with it */
2906 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2908 /* Remove it, update total size */
2909 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2910 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2912 /* Calculate total free size */
2913 FreeSize
+= SplitBlock2
->Size
;
2915 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2917 SplitBlock
->Size
= FreeSize
;
2919 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2921 /* Update previous size of the next entry */
2922 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2926 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2929 /* Insert the new one back and update total size */
2930 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2931 Heap
->TotalFreeSize
+= FreeSize
;
2936 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2945 /* We're growing the block */
2946 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
2947 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
2949 /* Growing in place failed, so growing out of place */
2950 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
2952 DPRINT1("Realloc in place failed, but it was the only option\n");
2957 /* Clear tag bits */
2958 Flags
&= ~HEAP_TAG_MASK
;
2960 /* Process extra stuff */
2961 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2963 /* Preserve user settable flags */
2964 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
2966 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
2970 else if (InUseEntry
->SmallTagIndex
)
2972 /* Take small tag index into account */
2973 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
2976 /* Allocate new block from the heap */
2977 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
2978 Flags
& ~HEAP_ZERO_MEMORY
,
2981 /* Proceed if it didn't fail */
2984 /* Get new entry pointer */
2985 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
2987 /* Process extra stuff if it exists */
2988 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2990 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
2992 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2994 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2995 NewExtra
->Settable
= OldExtra
->Settable
;
2999 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
3003 /* Copy actual user bits */
3005 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
3007 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
3009 /* Zero remaining part if required */
3010 if (Size
> OldSize
&&
3011 (Flags
& HEAP_ZERO_MEMORY
))
3013 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
3016 /* Free the old block */
3017 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
3020 Ptr
= NewBaseAddress
;
3025 /* Did resizing fail? */
3026 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
3028 /* Generate an exception if required */
3029 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
3030 ExceptionRecord
.ExceptionRecord
= NULL
;
3031 ExceptionRecord
.NumberParameters
= 1;
3032 ExceptionRecord
.ExceptionFlags
= 0;
3033 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
3035 RtlRaiseException(&ExceptionRecord
);
3038 /* Release the heap lock if it was acquired */
3040 RtlLeaveHeapLock(Heap
->LockVariable
);
3046 /***********************************************************************
3052 RtlCompactHeap(HANDLE Heap
,
3060 /***********************************************************************
3062 * Attempts to acquire the critical section object for a specified heap.
3065 * Heap [in] Handle of heap to lock for exclusive access
3074 RtlLockHeap(IN HANDLE HeapPtr
)
3076 PHEAP Heap
= (PHEAP
)HeapPtr
;
3078 // FIXME Check for special heap
3080 /* Check if it's really a heap */
3081 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3083 /* Lock if it's lockable */
3084 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3086 RtlEnterHeapLock(Heap
->LockVariable
);
3093 /***********************************************************************
3095 * Releases ownership of the critical section object.
3098 * Heap [in] Handle to the heap to unlock
3107 RtlUnlockHeap(HANDLE HeapPtr
)
3109 PHEAP Heap
= (PHEAP
)HeapPtr
;
3111 // FIXME Check for special heap
3113 /* Check if it's really a heap */
3114 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3116 /* Lock if it's lockable */
3117 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3119 RtlLeaveHeapLock(Heap
->LockVariable
);
3126 /***********************************************************************
3129 * Heap [in] Handle of heap
3130 * Flags [in] Heap size control flags
3131 * Ptr [in] Address of memory to return size for
3134 * Size in bytes of allocated memory
3135 * 0xffffffff: Failure
3146 PHEAP Heap
= (PHEAP
)HeapPtr
;
3147 PHEAP_ENTRY HeapEntry
;
3150 // FIXME This is a hack around missing SEH support!
3153 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3158 Flags
|= Heap
->Flags
;
3160 // FIXME Special heap
3162 /* Get the heap entry pointer */
3163 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3165 /* Return -1 if that entry is free */
3166 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3168 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER
);
3172 /* Get size of this block depending if it's a usual or a big one */
3173 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3175 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3180 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3183 /* Return calculated size */
3188 /***********************************************************************
3190 * Validates a specified heap.
3193 * Heap [in] Handle to the heap
3194 * Flags [in] Bit flags that control access during operation
3195 * Block [in] Optional pointer to memory block to validate
3206 BOOLEAN NTAPI
RtlValidateHeap(
3214 /* Imitate success */
3219 RtlInitializeHeapManager(VOID
)
3224 Peb
= RtlGetCurrentPeb();
3226 /* Initialize heap-related fields of PEB */
3227 Peb
->NumberOfHeaps
= 0;
3228 Peb
->MaximumNumberOfHeaps
= HEAP_MAX_PROCESS_HEAPS
;
3229 Peb
->ProcessHeaps
= (PVOID
)RtlpProcessHeaps
;
3231 /* Initialize the process heaps list protecting lock */
3232 RtlInitializeHeapLock(&RtlpProcessHeapsListLock
);
3240 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3244 return STATUS_NOT_IMPLEMENTED
;
3252 RtlGetProcessHeaps(ULONG count
,
3264 RtlValidateProcessHeaps(VOID
)
3276 IN PVOID HeapHandle
,
3289 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3291 IN PVOID BaseAddress
,
3303 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3305 IN PVOID BaseAddress
,
3316 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3318 IN PVOID BaseAddress
,
3319 OUT PVOID
*UserValue
,
3320 OUT PULONG UserFlags
)
3331 RtlUsageHeap(IN HANDLE Heap
,
3333 OUT PRTL_HEAP_USAGE Usage
)
3337 return STATUS_NOT_IMPLEMENTED
;
3342 RtlQueryTagHeap(IN PVOID HeapHandle
,
3345 IN BOOLEAN ResetCounters
,
3346 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
3355 RtlExtendHeap(IN HANDLE Heap
,
3367 RtlCreateTagHeap(IN HANDLE HeapHandle
,
3370 IN PWSTR TagSubName
)
3379 RtlWalkHeap(IN HANDLE HeapHandle
,
3383 return STATUS_NOT_IMPLEMENTED
;
3388 RtlProtectHeap(IN PVOID HeapHandle
,
3389 IN BOOLEAN ReadOnly
)
3397 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
3398 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
3399 IN PVOID HeapInformation
,
3400 IN SIZE_T HeapInformationLength
)
3408 RtlQueryHeapInformation(HANDLE HeapHandle
,
3409 HEAP_INFORMATION_CLASS HeapInformationClass
,
3410 PVOID HeapInformation OPTIONAL
,
3411 SIZE_T HeapInformationLength OPTIONAL
,
3412 PSIZE_T ReturnLength OPTIONAL
)
3420 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
3432 RtlMultipleFreeHeap(IN PVOID HeapHandle
,