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 0x55555555
34 #define ARENA_FREE_FILLER 0xaaaaaaaa
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
;
265 RtlpSpecialHeapCreate(ULONG Flags
,
270 PRTL_HEAP_PARAMETERS Parameters
) { return NULL
; };
272 HEAP_LOCK RtlpProcessHeapsListLock
;
273 PHEAP RtlpProcessHeaps
[HEAP_MAX_PROCESS_HEAPS
]; /* Usermode only */
275 /* Heap entry flags */
276 #define HEAP_ENTRY_BUSY 0x01
277 #define HEAP_ENTRY_EXTRA_PRESENT 0x02
278 #define HEAP_ENTRY_FILL_PATTERN 0x04
279 #define HEAP_ENTRY_VIRTUAL_ALLOC 0x08
280 #define HEAP_ENTRY_LAST_ENTRY 0x10
281 #define HEAP_ENTRY_SETTABLE_FLAG1 0x20
282 #define HEAP_ENTRY_SETTABLE_FLAG2 0x40
283 #define HEAP_ENTRY_SETTABLE_FLAG3 0x80
284 #define HEAP_ENTRY_SETTABLE_FLAGS (HEAP_ENTRY_SETTABLE_FLAG1 | HEAP_ENTRY_SETTABLE_FLAG2 | HEAP_ENTRY_SETTABLE_FLAG3)
287 #define HEAP_SIGNATURE 0xeefeeff
288 #define HEAP_SEGMENT_SIGNATURE 0xffeeffee
291 #define HEAP_USER_ALLOCATED 0x1
295 /* How many least significant bits are clear */
296 UCHAR RtlpBitsClearLow
[] =
298 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
299 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
300 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
301 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
302 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
303 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
304 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
305 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
306 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
307 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
308 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
309 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
310 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
311 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
312 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
313 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
317 RtlpFindLeastSetBit(ULONG Bits
)
322 return RtlpBitsClearLow
[Bits
& 0xFF]; /* Lowest byte */
324 return RtlpBitsClearLow
[(Bits
>> 8) & 0xFF] + 8; /* 2nd byte */
328 if ((Bits
>> 16) & 0xFF)
329 return RtlpBitsClearLow
[(Bits
>> 16) & 0xFF] + 16; /* 3rd byte */
331 return RtlpBitsClearLow
[(Bits
>> 24) & 0xFF] + 24; /* Highest byte */
336 RtlCompareMemoryUlong(PVOID Source
, ULONG Length
, ULONG Value
);
338 PHEAP_FREE_ENTRY NTAPI
339 RtlpCoalesceFreeBlocks (PHEAP Heap
,
340 PHEAP_FREE_ENTRY FreeEntry
,
344 /* FUNCTIONS *****************************************************************/
347 RtlpInitializeHeap(PHEAP Heap
,
350 BOOLEAN AllocateLock
,
353 PVOID NextHeapBase
= Heap
+ 1;
354 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
360 *HeaderSize
+= NumUCRs
* sizeof(*UcrDescriptor
);
362 /* Prepare a list of UCRs */
363 InitializeListHead(&Heap
->UCRList
);
364 InitializeListHead(&Heap
->UCRSegmentList
);
365 UcrDescriptor
= NextHeapBase
;
367 for (i
=0; i
<NumUCRs
; i
++, UcrDescriptor
++)
369 InsertTailList(&Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
372 NextHeapBase
= UcrDescriptor
;
375 /* Round up header size again */
376 *HeaderSize
= ROUND_UP(*HeaderSize
, HEAP_ENTRY_SIZE
);
378 ASSERT(*HeaderSize
<= PAGE_SIZE
);
380 /* Initialize heap's header */
381 Heap
->Entry
.Size
= (*HeaderSize
) >> HEAP_ENTRY_SHIFT
;
382 Heap
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
384 Heap
->Signature
= HEAP_SIGNATURE
;
386 Heap
->ForceFlags
= (Flags
& (HEAP_NO_SERIALIZE
|
387 HEAP_GENERATE_EXCEPTIONS
|
389 HEAP_REALLOC_IN_PLACE_ONLY
|
390 HEAP_VALIDATE_PARAMETERS_ENABLED
|
391 HEAP_VALIDATE_ALL_ENABLED
|
392 HEAP_TAIL_CHECKING_ENABLED
|
393 HEAP_CREATE_ALIGN_16
|
394 HEAP_FREE_CHECKING_ENABLED
));
395 Heap
->HeaderValidateCopy
= NULL
;
396 Heap
->HeaderValidateLength
= ((PCHAR
)NextHeapBase
- (PCHAR
)Heap
);
398 /* Initialize free lists */
399 for (i
=0; i
<HEAP_FREELISTS
; i
++)
401 InitializeListHead(&Heap
->FreeLists
[i
]);
404 /* Initialize "big" allocations list */
405 InitializeListHead(&Heap
->VirtualAllocdBlocks
);
407 /* Initialize lock */
411 Status
= RtlInitializeHeapLock((PHEAP_LOCK
)Lock
);
412 if (!NT_SUCCESS(Status
))
414 DPRINT1("Initializing the lock failed!\n");
415 return /*NULL*/; // FIXME!
419 /* Set the lock variable */
420 Heap
->LockVariable
= Lock
;
424 RtlpSetFreeListsBit(PHEAP Heap
,
425 PHEAP_FREE_ENTRY FreeEntry
)
429 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
431 /* Calculate offset in the free list bitmap */
432 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
433 Bit
= 1 << (FreeEntry
->Size
& 7);
435 /* Assure it's not already set */
436 ASSERT((Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
) == 0);
439 Heap
->u
.FreeListsInUseBytes
[Index
] |= Bit
;
443 RtlpClearFreeListsBit(PHEAP Heap
,
444 PHEAP_FREE_ENTRY FreeEntry
)
448 ASSERT(FreeEntry
->Size
< HEAP_FREELISTS
);
450 /* Calculate offset in the free list bitmap */
451 Index
= FreeEntry
->Size
>> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
452 Bit
= 1 << (FreeEntry
->Size
& 7);
454 /* Assure it was set and the corresponding free list is empty */
455 ASSERT(Heap
->u
.FreeListsInUseBytes
[Index
] & Bit
);
456 ASSERT(IsListEmpty(&Heap
->FreeLists
[FreeEntry
->Size
]));
459 Heap
->u
.FreeListsInUseBytes
[Index
] ^= Bit
;
463 RtlpInsertFreeBlockHelper(PHEAP Heap
,
464 PHEAP_FREE_ENTRY FreeEntry
,
468 PLIST_ENTRY FreeListHead
, Current
;
469 PHEAP_FREE_ENTRY CurrentEntry
;
471 ASSERT(FreeEntry
->Size
== BlockSize
);
473 /* Fill if it's not denied */
476 FreeEntry
->Flags
&= ~(HEAP_ENTRY_FILL_PATTERN
|
477 HEAP_ENTRY_EXTRA_PRESENT
|
480 if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
482 RtlFillMemoryUlong((PCHAR
)(FreeEntry
+ 1),
483 (BlockSize
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
),
486 FreeEntry
->Flags
|= HEAP_ENTRY_FILL_PATTERN
;
491 /* Clear out all flags except the last entry one */
492 FreeEntry
->Flags
&= HEAP_ENTRY_LAST_ENTRY
;
495 /* Check if PreviousSize of the next entry matches ours */
496 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
498 ASSERT(((PHEAP_ENTRY
)FreeEntry
+ BlockSize
)->PreviousSize
= BlockSize
);
501 /* Insert it either into dedicated or non-dedicated list */
502 if (BlockSize
< HEAP_FREELISTS
)
505 FreeListHead
= &Heap
->FreeLists
[BlockSize
];
507 if (IsListEmpty(FreeListHead
))
509 RtlpSetFreeListsBit(Heap
, FreeEntry
);
514 /* Non-dedicated one */
515 FreeListHead
= &Heap
->FreeLists
[0];
516 Current
= FreeListHead
->Flink
;
518 /* Find a position where to insert it to (the list must be sorted) */
519 while (FreeListHead
!= Current
)
521 CurrentEntry
= CONTAINING_RECORD(Current
, HEAP_FREE_ENTRY
, FreeList
);
523 if (BlockSize
<= CurrentEntry
->Size
)
526 Current
= Current
->Flink
;
529 FreeListHead
= Current
;
532 /* Actually insert it into the list */
533 InsertTailList(FreeListHead
, &FreeEntry
->FreeList
);
537 RtlpInsertFreeBlock(PHEAP Heap
,
538 PHEAP_FREE_ENTRY FreeEntry
,
541 USHORT Size
, PreviousSize
;
542 UCHAR SegmentOffset
, Flags
;
543 PHEAP_SEGMENT Segment
;
545 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap
, FreeEntry
, BlockSize
);
547 /* Increase the free size counter */
548 Heap
->TotalFreeSize
+= BlockSize
;
550 /* Remember certain values */
551 Flags
= FreeEntry
->Flags
;
552 PreviousSize
= FreeEntry
->PreviousSize
;
553 SegmentOffset
= FreeEntry
->SegmentOffset
;
554 Segment
= Heap
->Segments
[SegmentOffset
];
559 /* Check for the max size */
560 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
562 Size
= HEAP_MAX_BLOCK_SIZE
;
564 /* Special compensation if it goes above limit just by 1 */
565 if (BlockSize
== (HEAP_MAX_BLOCK_SIZE
+ 1))
568 FreeEntry
->Flags
= 0;
573 FreeEntry
->Flags
= Flags
;
576 /* Change its size and insert it into a free list */
577 FreeEntry
->Size
= Size
;
578 FreeEntry
->PreviousSize
= PreviousSize
;
579 FreeEntry
->SegmentOffset
= SegmentOffset
;
581 /* Call a helper to actually insert the block */
582 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, Size
, FALSE
);
588 /* Go to the next entry */
589 FreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ Size
);
591 /* Check if that's all */
592 if ((PHEAP_ENTRY
)FreeEntry
>= Segment
->LastValidEntry
) return;
595 /* Update previous size if needed */
596 if (!(Flags
& HEAP_ENTRY_LAST_ENTRY
))
597 FreeEntry
->PreviousSize
= PreviousSize
;
601 RtlpRemoveFreeBlock(PHEAP Heap
,
602 PHEAP_FREE_ENTRY FreeEntry
,
606 SIZE_T Result
, RealSize
;
607 PLIST_ENTRY OldBlink
, OldFlink
;
609 // FIXME: Maybe use RemoveEntryList?
611 /* Remove the free block */
612 OldFlink
= FreeEntry
->FreeList
.Flink
;
613 OldBlink
= FreeEntry
->FreeList
.Blink
;
614 OldBlink
->Flink
= OldFlink
;
615 OldFlink
->Blink
= OldBlink
;
617 /* Update the freelists bitmap */
618 if ((OldFlink
== OldBlink
) &&
619 (Dedicated
|| (!Dedicated
&& FreeEntry
->Size
< HEAP_FREELISTS
)))
621 RtlpClearFreeListsBit(Heap
, FreeEntry
);
624 /* Fill with pattern if necessary */
626 (FreeEntry
->Flags
& HEAP_ENTRY_FILL_PATTERN
))
628 RealSize
= (FreeEntry
->Size
<< HEAP_ENTRY_SHIFT
) - sizeof(*FreeEntry
);
630 /* Deduct extra stuff from block's real size */
631 if (FreeEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
&&
632 RealSize
> sizeof(HEAP_FREE_ENTRY_EXTRA
))
634 RealSize
-= sizeof(HEAP_FREE_ENTRY_EXTRA
);
637 /* Check if the free filler is intact */
638 Result
= RtlCompareMemoryUlong((PCHAR
)(FreeEntry
+ 1),
642 if (Result
!= RealSize
)
644 DPRINT1("Free heap block %p modified at %p after it was freed\n",
646 (PCHAR
)(FreeEntry
+ 1) + Result
);
652 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry
)
654 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
656 /* Get pointer to the containing record */
657 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
659 /* Restore the real size */
660 return VirtualEntry
->CommitSize
- HeapEntry
->Size
;
663 PHEAP_UCR_DESCRIPTOR NTAPI
664 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment
)
667 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
668 PHEAP Heap
= Segment
->Heap
;
670 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment
);
672 /* Check if we have unused UCRs */
673 if (IsListEmpty(&Heap
->UCRList
))
678 /* There are unused UCRs, just get the first one */
679 Entry
= RemoveHeadList(&Heap
->UCRList
);
680 UcrDescriptor
= CONTAINING_RECORD(Entry
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
681 return UcrDescriptor
;
685 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment
,
686 PHEAP_UCR_DESCRIPTOR UcrDescriptor
)
689 UcrDescriptor
->Address
= NULL
;
690 UcrDescriptor
->Size
= 0;
692 /* Put it into the heap's list of unused UCRs */
693 InsertHeadList(&Segment
->Heap
->UCRList
, &UcrDescriptor
->ListEntry
);
697 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment
,
702 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
704 DPRINT("RtlpInsertUnCommittedPages(%p %p %x)\n", Segment
, Address
, Size
);
706 /* Go through the list of UCR descriptors, they are sorted from lowest address
708 Current
= Segment
->UCRSegmentList
.Flink
;
709 while(Current
!= &Segment
->UCRSegmentList
)
711 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
713 if ((ULONG_PTR
)UcrDescriptor
->Address
> Address
)
715 /* Check for a really lucky case */
716 if ((Address
+ Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
719 UcrDescriptor
->Address
= (PVOID
)Address
;
720 UcrDescriptor
->Size
+= Size
;
724 /* We found the block after which the new one should go */
727 else if (((ULONG_PTR
)UcrDescriptor
->Address
+ UcrDescriptor
->Size
) == Address
)
729 /* Modify this entry */
730 Address
= (ULONG_PTR
)UcrDescriptor
->Address
;
731 Size
+= UcrDescriptor
->Size
;
733 /* Remove it from the list and destroy it */
734 RemoveEntryList(Current
);
735 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
737 Segment
->NumberOfUnCommittedRanges
--;
741 /* Advance to the next descriptor */
742 Current
= Current
->Flink
;
746 /* Create a new UCR descriptor */
747 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
748 if (!UcrDescriptor
) return;
750 UcrDescriptor
->Address
= (PVOID
)Address
;
751 UcrDescriptor
->Size
= Size
;
753 /* "Current" is the descriptor after which our one should go */
754 InsertTailList(Current
, &UcrDescriptor
->SegmentEntry
);
756 DPRINT("Added segment UCR with base %p, size 0x%x\n", Address
, Size
);
758 /* Increase counters */
759 Segment
->NumberOfUnCommittedRanges
++;
762 PHEAP_FREE_ENTRY NTAPI
763 RtlpFindAndCommitPages(PHEAP Heap
,
764 PHEAP_SEGMENT Segment
,
769 PHEAP_UCR_DESCRIPTOR UcrDescriptor
, PreviousUcr
= NULL
;
770 PHEAP_ENTRY FirstEntry
, LastEntry
, PreviousLastEntry
;
773 DPRINT("RtlpFindAndCommitPages(%p %p %x %p)\n", Heap
, Segment
, *Size
, Address
);
775 /* Go through UCRs in a segment */
776 Current
= Segment
->UCRSegmentList
.Flink
;
777 while(Current
!= &Segment
->UCRSegmentList
)
779 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, SegmentEntry
);
781 /* Check if we can use that one right away */
782 if (UcrDescriptor
->Size
>= *Size
&&
783 (UcrDescriptor
->Address
== Address
|| !Address
))
785 /* Get the address */
786 Address
= UcrDescriptor
->Address
;
789 if (Heap
->CommitRoutine
)
791 Status
= Heap
->CommitRoutine(Heap
, &Address
, Size
);
795 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
803 DPRINT("Committed %d bytes at base %p, UCR size is %d\n", *Size
, Address
, UcrDescriptor
->Size
);
805 /* Fail in unsuccessful case */
806 if (!NT_SUCCESS(Status
))
808 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
812 /* Update tracking numbers */
813 Segment
->NumberOfUnCommittedPages
-= *Size
/ PAGE_SIZE
;
815 /* Calculate first and last entries */
816 FirstEntry
= (PHEAP_ENTRY
)Address
;
818 if ((Segment
->LastEntryInSegment
->Flags
& HEAP_ENTRY_LAST_ENTRY
) &&
819 (ULONG_PTR
)(Segment
->LastEntryInSegment
+ Segment
->LastEntryInSegment
->Size
) == (ULONG_PTR
)UcrDescriptor
->Address
)
821 LastEntry
= Segment
->LastEntryInSegment
;
825 /* Go through the entries to find the last one */
828 LastEntry
= (PHEAP_ENTRY
)((ULONG_PTR
)PreviousUcr
->Address
+ PreviousUcr
->Size
);
830 LastEntry
= Segment
->FirstEntry
;
832 while (!(LastEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
834 PreviousLastEntry
= LastEntry
;
835 LastEntry
+= LastEntry
->Size
;
837 if ((ULONG_PTR
)LastEntry
>= (ULONG_PTR
)Segment
->LastValidEntry
||
838 LastEntry
->Size
== 0)
840 if (LastEntry
== (PHEAP_ENTRY
)Address
)
843 LastEntry
= PreviousLastEntry
;
847 DPRINT1("Last entry not found in a committed range near to %p\n", PreviousLastEntry
);
853 /* Unmark it as a last entry */
854 LastEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
856 /* Update UCR descriptor */
857 UcrDescriptor
->Address
= (PUCHAR
)UcrDescriptor
->Address
+ *Size
;
858 UcrDescriptor
->Size
-= *Size
;
860 DPRINT("Updating UcrDescriptor %p, new Address %p, size %d\n",
861 UcrDescriptor
, UcrDescriptor
->Address
, UcrDescriptor
->Size
);
863 /* Check if anything left in this UCR */
864 if (UcrDescriptor
->Size
== 0)
866 /* It's fully exhausted */
867 if (UcrDescriptor
->Address
== Segment
->LastValidEntry
)
869 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
870 Segment
->LastEntryInSegment
= FirstEntry
;
874 FirstEntry
->Flags
= 0;
875 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
878 /* This UCR needs to be removed because it became useless */
879 RemoveEntryList(&UcrDescriptor
->SegmentEntry
);
881 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
882 Segment
->NumberOfUnCommittedRanges
--;
886 FirstEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
887 Segment
->LastEntryInSegment
= FirstEntry
;
890 /* Set various first entry fields*/
891 FirstEntry
->SegmentOffset
= LastEntry
->SegmentOffset
;
892 FirstEntry
->Size
= *Size
>> HEAP_ENTRY_SHIFT
;
893 FirstEntry
->PreviousSize
= LastEntry
->Size
;
895 /* Update previous size */
896 if (!(FirstEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
897 (FirstEntry
+ FirstEntry
->Size
)->PreviousSize
= FirstEntry
->Size
;
900 return (PHEAP_FREE_ENTRY
)FirstEntry
;
903 /* Advance to the next descriptor */
904 Current
= Current
->Flink
;
911 RtlpDeCommitFreeBlock(PHEAP Heap
,
912 PHEAP_FREE_ENTRY FreeEntry
,
916 PHEAP_SEGMENT Segment
;
917 PHEAP_ENTRY PrecedingInUseEntry
= NULL
, NextInUseEntry
= NULL
;
918 PHEAP_FREE_ENTRY NextFreeEntry
;
919 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
920 ULONG PrecedingSize
, NextSize
, DecommitSize
;
924 /* We can't decommit if there is a commit routine! */
925 if (Heap
->CommitRoutine
)
927 /* Just add it back the usual way */
928 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
932 /* Get the segment */
933 Segment
= Heap
->Segments
[FreeEntry
->SegmentOffset
];
935 /* Get the preceding entry */
936 DecommitBase
= ROUND_UP(FreeEntry
, PAGE_SIZE
);
937 PrecedingSize
= (PHEAP_ENTRY
)DecommitBase
- (PHEAP_ENTRY
)FreeEntry
;
939 if (PrecedingSize
== 1)
941 /* Just 1 heap entry, increase the base/size */
942 DecommitBase
+= PAGE_SIZE
;
943 PrecedingSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
945 else if (FreeEntry
->PreviousSize
&&
946 (DecommitBase
== (ULONG_PTR
)FreeEntry
))
948 PrecedingInUseEntry
= (PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
;
951 /* Get the next entry */
952 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ (Size
>> HEAP_ENTRY_SHIFT
));
953 DecommitSize
= ROUND_DOWN(NextFreeEntry
, PAGE_SIZE
);
954 NextSize
= (PHEAP_ENTRY
)NextFreeEntry
- (PHEAP_ENTRY
)DecommitSize
;
958 /* Just 1 heap entry, increase the size */
959 DecommitSize
-= PAGE_SIZE
;
960 NextSize
+= PAGE_SIZE
>> HEAP_ENTRY_SHIFT
;
962 else if (NextSize
== 0 &&
963 !(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
965 NextInUseEntry
= (PHEAP_ENTRY
)NextFreeEntry
;
968 NextFreeEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
- NextSize
);
970 if (DecommitSize
> DecommitBase
)
971 DecommitSize
-= DecommitBase
;
974 /* Nothing to decommit */
975 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
979 /* A decommit is necessary. Create a UCR descriptor */
980 UcrDescriptor
= RtlpCreateUnCommittedRange(Segment
);
983 DPRINT1("HEAP: Failed to create UCR descriptor\n");
984 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
988 /* Decommit the memory */
989 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
990 (PVOID
*)&DecommitBase
,
994 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
995 RtlpDestroyUnCommittedRange(Segment
, UcrDescriptor
);
997 if (!NT_SUCCESS(Status
))
999 RtlpInsertFreeBlock(Heap
, FreeEntry
, PrecedingSize
);
1003 /* Insert uncommitted pages */
1004 RtlpInsertUnCommittedPages(Segment
, DecommitBase
, DecommitSize
);
1005 Segment
->NumberOfUnCommittedPages
+= (DecommitSize
/ PAGE_SIZE
);
1009 /* Adjust size of this free entry and insert it */
1010 FreeEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
1011 FreeEntry
->Size
= PrecedingSize
;
1012 Heap
->TotalFreeSize
+= PrecedingSize
;
1013 Segment
->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
1014 RtlpInsertFreeBlockHelper(Heap
, FreeEntry
, PrecedingSize
);
1016 else if (NextInUseEntry
)
1018 /* Adjust preceding in use entry */
1019 PrecedingInUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
1020 Segment
->LastEntryInSegment
= PrecedingInUseEntry
;
1022 else if ((Segment
->LastEntryInSegment
>= (PHEAP_ENTRY
)DecommitBase
))
1024 /* Adjust last entry in the segment */
1025 Segment
->LastEntryInSegment
= Segment
->FirstEntry
;
1028 /* Now the next one */
1031 /* Adjust size of this free entry and insert it */
1032 NextFreeEntry
->Flags
= 0;
1033 NextFreeEntry
->PreviousSize
= 0;
1034 NextFreeEntry
->SegmentOffset
= Segment
->Entry
.SegmentOffset
;
1035 NextFreeEntry
->Size
= NextSize
;
1037 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)NextFreeEntry
+ NextSize
))->PreviousSize
= NextSize
;
1039 Heap
->TotalFreeSize
+= PrecedingSize
;
1040 RtlpInsertFreeBlockHelper(Heap
, NextFreeEntry
, NextSize
);
1042 else if (NextInUseEntry
)
1044 NextInUseEntry
->PreviousSize
= 0;
1047 RtlpInsertFreeBlock(Heap
, FreeEntry
, Size
);
1052 RtlpInitializeHeapSegment(PHEAP Heap
,
1053 PHEAP_SEGMENT Segment
,
1057 PVOID UncommittedBase
,
1060 ULONG Pages
, CommitSize
;
1061 PHEAP_ENTRY HeapEntry
;
1062 USHORT PreviousSize
= 0, NewSize
;
1065 Pages
= ((PCHAR
)LimitAddress
- (PCHAR
)BaseAddress
) / PAGE_SIZE
;
1067 HeapEntry
= (PHEAP_ENTRY
)ROUND_UP(Segment
+ 1, HEAP_ENTRY_SIZE
);
1069 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %p %p %p)\n", Heap
, Segment
, SegmentIndex
, Flags
, BaseAddress
, UncommittedBase
, LimitAddress
);
1070 DPRINT("Pages %x, HeapEntry %p, sizeof(HEAP_SEGMENT) %x\n", Pages
, HeapEntry
, sizeof(HEAP_SEGMENT
));
1072 /* Check if it's the first segment and remember its size */
1073 if (Heap
== BaseAddress
)
1074 PreviousSize
= Heap
->Entry
.Size
;
1076 NewSize
= ((PCHAR
)HeapEntry
- (PCHAR
)Segment
) >> HEAP_ENTRY_SHIFT
;
1078 if ((PVOID
)(HeapEntry
+ 1) >= UncommittedBase
)
1080 /* Check if it goes beyond the limit */
1081 if ((PVOID
)(HeapEntry
+ 1) >= LimitAddress
)
1084 /* Need to commit memory */
1085 CommitSize
= (PCHAR
)(HeapEntry
+ 1) - (PCHAR
)UncommittedBase
;
1086 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1087 (PVOID
)&UncommittedBase
,
1092 if (!NT_SUCCESS(Status
))
1094 DPRINT1("Committing page failed with status 0x%08X\n", Status
);
1098 DPRINT("Committed %d bytes at base %p\n", CommitSize
, UncommittedBase
);
1100 /* Calcule the new uncommitted base */
1101 UncommittedBase
= (PVOID
)((PCHAR
)UncommittedBase
+ CommitSize
);
1104 /* Initialize the segment entry */
1105 Segment
->Entry
.PreviousSize
= PreviousSize
;
1106 Segment
->Entry
.Size
= NewSize
;
1107 Segment
->Entry
.Flags
= HEAP_ENTRY_BUSY
;
1108 Segment
->Entry
.SegmentOffset
= SegmentIndex
;
1110 /* Initialize the segment itself */
1111 Segment
->SegmentSignature
= HEAP_SEGMENT_SIGNATURE
;
1112 Segment
->Heap
= Heap
;
1113 Segment
->BaseAddress
= BaseAddress
;
1114 Segment
->FirstEntry
= HeapEntry
;
1115 Segment
->LastValidEntry
= (PHEAP_ENTRY
)((PCHAR
)BaseAddress
+ Pages
* PAGE_SIZE
);
1116 Segment
->NumberOfPages
= Pages
;
1117 Segment
->NumberOfUnCommittedPages
= ((PCHAR
)LimitAddress
- (PCHAR
)UncommittedBase
) / PAGE_SIZE
;
1118 InitializeListHead(&Segment
->UCRSegmentList
);
1120 /* Insert uncommitted pages into UCR (uncommitted ranges) list */
1121 if (Segment
->NumberOfUnCommittedPages
)
1123 RtlpInsertUnCommittedPages(Segment
, (ULONG_PTR
)UncommittedBase
, Segment
->NumberOfUnCommittedPages
* PAGE_SIZE
);
1126 /* Set the segment index pointer */
1127 Heap
->Segments
[SegmentIndex
] = Segment
;
1129 /* Prepare a free heap entry */
1130 HeapEntry
->Flags
= HEAP_ENTRY_LAST_ENTRY
;
1131 HeapEntry
->PreviousSize
= Segment
->Entry
.Size
;
1132 HeapEntry
->SegmentOffset
= SegmentIndex
;
1134 /* Set last entry in segment */
1135 Segment
->LastEntryInSegment
= HeapEntry
;
1138 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, (PHEAP_ENTRY
)UncommittedBase
- HeapEntry
);
1144 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment
)
1150 /* Make sure it's not user allocated */
1151 if (Segment
->SegmentFlags
& HEAP_USER_ALLOCATED
) return;
1153 BaseAddress
= Segment
->BaseAddress
;
1154 DPRINT1("Destroying segment %p, BA %p\n", Segment
, BaseAddress
);
1156 /* Release virtual memory */
1157 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
1162 if (!NT_SUCCESS(Status
))
1164 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status
);
1168 /* Usermode only! */
1170 RtlpAddHeapToProcessList(PHEAP Heap
)
1175 Peb
= RtlGetCurrentPeb();
1177 /* Acquire the lock */
1178 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1181 /* Check if max number of heaps reached */
1182 if (Peb
->NumberOfHeaps
== Peb
->MaximumNumberOfHeaps
)
1184 // TODO: Handle this case
1188 /* Add the heap to the process heaps */
1189 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = Heap
;
1190 Peb
->NumberOfHeaps
++;
1191 Heap
->ProcessHeapsListIndex
= Peb
->NumberOfHeaps
;
1192 // } _SEH2_FINALLY {
1194 /* Release the lock */
1195 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1200 /* Usermode only! */
1202 RtlpRemoveHeapFromProcessList(PHEAP Heap
)
1205 PHEAP
*Current
, *Next
;
1209 Peb
= RtlGetCurrentPeb();
1211 /* Acquire the lock */
1212 RtlEnterHeapLock(&RtlpProcessHeapsListLock
);
1214 /* Check if we don't need anything to do */
1215 if ((Heap
->ProcessHeapsListIndex
== 0) ||
1216 (Heap
->ProcessHeapsListIndex
> Peb
->NumberOfHeaps
) ||
1217 (Peb
->NumberOfHeaps
== 0))
1219 /* Release the lock */
1220 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1225 /* The process actually has more than one heap.
1226 Use classic, lernt from university times algorithm for removing an entry
1227 from a static array */
1229 Current
= (PHEAP
*)&Peb
->ProcessHeaps
[Heap
->ProcessHeapsListIndex
- 1];
1232 /* How many items we need to shift to the left */
1233 Count
= Peb
->NumberOfHeaps
- (Heap
->ProcessHeapsListIndex
- 1);
1235 /* Move them all in a loop */
1238 /* Copy it and advance next pointer */
1241 /* Update its index */
1242 (*Current
)->ProcessHeapsListIndex
-= 1;
1244 /* Advance pointers */
1249 /* Decrease total number of heaps */
1250 Peb
->NumberOfHeaps
--;
1252 /* Zero last unused item */
1253 Peb
->ProcessHeaps
[Peb
->NumberOfHeaps
] = NULL
;
1254 Heap
->ProcessHeapsListIndex
= 0;
1256 /* Release the lock */
1257 RtlLeaveHeapLock(&RtlpProcessHeapsListLock
);
1260 PHEAP_FREE_ENTRY NTAPI
1261 RtlpCoalesceHeap(PHEAP Heap
)
1267 PHEAP_FREE_ENTRY NTAPI
1268 RtlpCoalesceFreeBlocks (PHEAP Heap
,
1269 PHEAP_FREE_ENTRY FreeEntry
,
1273 PHEAP_FREE_ENTRY CurrentEntry
, NextEntry
;
1275 /* Get the previous entry */
1276 CurrentEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
- FreeEntry
->PreviousSize
);
1279 if (CurrentEntry
!= FreeEntry
&&
1280 !(CurrentEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1281 (*FreeSize
+ CurrentEntry
->Size
) <= HEAP_MAX_BLOCK_SIZE
)
1283 ASSERT(FreeEntry
->PreviousSize
== CurrentEntry
->Size
);
1285 /* Remove it if asked for */
1288 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1289 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1291 /* Remove it only once! */
1295 /* Remove previous entry too */
1296 RtlpRemoveFreeBlock(Heap
, CurrentEntry
, FALSE
, FALSE
);
1299 CurrentEntry
->Flags
= FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1301 /* Update last entry in the segment */
1302 if (CurrentEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1303 Heap
->Segments
[CurrentEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)CurrentEntry
;
1305 /* Advance FreeEntry and update sizes */
1306 FreeEntry
= CurrentEntry
;
1307 *FreeSize
+= CurrentEntry
->Size
;
1308 Heap
->TotalFreeSize
-= CurrentEntry
->Size
;
1309 FreeEntry
->Size
= *FreeSize
;
1311 /* Also update previous size if needed */
1312 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1314 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1318 /* Check the next block if it exists */
1319 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1321 NextEntry
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
);
1323 if (!(NextEntry
->Flags
& HEAP_ENTRY_BUSY
) &&
1324 NextEntry
->Size
+ *FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
1326 ASSERT(*FreeSize
== NextEntry
->PreviousSize
);
1328 /* Remove it if asked for */
1331 RtlpRemoveFreeBlock(Heap
, FreeEntry
, FALSE
, FALSE
);
1332 Heap
->TotalFreeSize
-= FreeEntry
->Size
;
1336 FreeEntry
->Flags
= NextEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
;
1338 /* Update last entry in the segment */
1339 if (FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
1340 Heap
->Segments
[FreeEntry
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)FreeEntry
;
1342 /* Remove next entry now */
1343 RtlpRemoveFreeBlock(Heap
, NextEntry
, FALSE
, FALSE
);
1346 *FreeSize
+= NextEntry
->Size
;
1347 Heap
->TotalFreeSize
-= NextEntry
->Size
;
1348 FreeEntry
->Size
= *FreeSize
;
1350 /* Also update previous size if needed */
1351 if (!(FreeEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
1353 ((PHEAP_ENTRY
)FreeEntry
+ *FreeSize
)->PreviousSize
= *FreeSize
;
1360 PHEAP_FREE_ENTRY NTAPI
1361 RtlpExtendHeap(PHEAP Heap
,
1365 UCHAR Index
, EmptyIndex
;
1366 SIZE_T FreeSize
, CommitSize
, ReserveSize
;
1367 PHEAP_SEGMENT Segment
;
1368 PHEAP_FREE_ENTRY FreeEntry
;
1371 DPRINT("RtlpExtendHeap(%p %x)\n", Heap
, Size
);
1373 /* Calculate amount in pages */
1374 Pages
= (Size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
1375 FreeSize
= Pages
* PAGE_SIZE
;
1376 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages
, FreeSize
);
1378 /* Find an empty segment */
1379 EmptyIndex
= HEAP_SEGMENTS
;
1380 for (Index
= 0; Index
< HEAP_SEGMENTS
; Index
++)
1382 Segment
= Heap
->Segments
[Index
];
1384 if (Segment
) DPRINT("Segment[%d] %p with NOUCP %x\n", Index
, Segment
, Segment
->NumberOfUnCommittedPages
);
1386 /* Check if its size suits us */
1388 Pages
<= Segment
->NumberOfUnCommittedPages
)
1390 DPRINT("This segment is suitable\n");
1392 /* Commit needed amount */
1393 FreeEntry
= RtlpFindAndCommitPages(Heap
, Segment
, &FreeSize
, NULL
);
1395 /* Coalesce it with adjacent entries */
1398 FreeSize
= FreeSize
>> HEAP_ENTRY_SHIFT
;
1399 FreeEntry
= RtlpCoalesceFreeBlocks(Heap
, FreeEntry
, &FreeSize
, FALSE
);
1400 RtlpInsertFreeBlock(Heap
, FreeEntry
, FreeSize
);
1404 else if (!Segment
&&
1405 EmptyIndex
== HEAP_SEGMENTS
)
1407 /* Remember the first unused segment index */
1412 /* No luck, need to grow the heap */
1413 if ((Heap
->Flags
& HEAP_GROWABLE
) &&
1414 (EmptyIndex
!= HEAP_SEGMENTS
))
1418 /* Reserve the memory */
1419 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentReserve
)
1420 ReserveSize
= Heap
->SegmentReserve
;
1422 ReserveSize
= Size
+ PAGE_SIZE
;
1424 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1431 /* If it failed, retry again with a half division algorithm */
1432 while (!NT_SUCCESS(Status
) &&
1433 ReserveSize
!= Size
+ PAGE_SIZE
)
1437 if (ReserveSize
< (Size
+ PAGE_SIZE
))
1438 ReserveSize
= Size
+ PAGE_SIZE
;
1440 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1448 /* Proceed only if it's success */
1449 if (NT_SUCCESS(Status
))
1451 Heap
->SegmentReserve
+= ReserveSize
;
1453 /* Now commit the memory */
1454 if ((Size
+ PAGE_SIZE
) <= Heap
->SegmentCommit
)
1455 CommitSize
= Heap
->SegmentCommit
;
1457 CommitSize
= Size
+ PAGE_SIZE
;
1459 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1466 DPRINT("Committed %d bytes at base %p\n", CommitSize
, Segment
);
1468 /* Initialize heap segment if commit was successful */
1469 if (NT_SUCCESS(Status
))
1471 if (!RtlpInitializeHeapSegment(Heap
, Segment
, EmptyIndex
, 0, Segment
,
1472 (PCHAR
)Segment
+ CommitSize
, (PCHAR
)Segment
+ ReserveSize
))
1474 Status
= STATUS_NO_MEMORY
;
1478 /* If everything worked - cool */
1479 if (NT_SUCCESS(Status
)) return (PHEAP_FREE_ENTRY
)Segment
->FirstEntry
;
1481 DPRINT1("Committing failed with status 0x%08X\n", Status
);
1483 /* Nope, we failed. Free memory */
1484 ZwFreeVirtualMemory(NtCurrentProcess(),
1491 DPRINT1("Reserving failed with status 0x%08X\n", Status
);
1495 if (RtlpGetMode() == UserMode
)
1497 /* If coalescing on free is disabled in usermode, then do it here */
1498 if (Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)
1500 FreeEntry
= RtlpCoalesceHeap(Heap
);
1502 /* If it's a suitable one - return it */
1504 FreeEntry
->Size
>= Size
)
1514 /***********************************************************************
1517 * Handle of heap: Success
1523 RtlCreateHeap(ULONG Flags
,
1528 PRTL_HEAP_PARAMETERS Parameters
)
1530 PVOID CommittedAddress
= NULL
, UncommittedAddress
= NULL
;
1532 RTL_HEAP_PARAMETERS SafeParams
= {0};
1534 ULONG_PTR MaximumUserModeAddress
;
1535 SYSTEM_BASIC_INFORMATION SystemInformation
;
1536 MEMORY_BASIC_INFORMATION MemoryInfo
;
1537 ULONG NtGlobalFlags
= RtlGetNtGlobalFlags();
1538 ULONG HeapSegmentFlags
= 0;
1540 ULONG MaxBlockSize
, HeaderSize
;
1541 BOOLEAN AllocateLock
= FALSE
;
1543 /* Check for a special heap */
1544 if (RtlpPageHeapEnabled
&& !Addr
&& !Lock
)
1546 Heap
= RtlpSpecialHeapCreate(Flags
, Addr
, TotalSize
, CommitSize
, Lock
, Parameters
);
1547 if (Heap
) return Heap
;
1552 /* Check validation flags */
1553 if (!(Flags
& HEAP_SKIP_VALIDATION_CHECKS
) && (Flags
& ~HEAP_CREATE_VALID_MASK
))
1555 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags
);
1556 Flags
&= HEAP_CREATE_VALID_MASK
;
1559 /* TODO: Capture parameters, once we decide to use SEH */
1560 if (!Parameters
) Parameters
= &SafeParams
;
1562 /* Check global flags */
1563 if (NtGlobalFlags
& FLG_HEAP_ENABLE_FREE_CHECK
)
1564 Flags
|= HEAP_FREE_CHECKING_ENABLED
;
1566 if (NtGlobalFlags
& FLG_HEAP_ENABLE_TAIL_CHECK
)
1567 Flags
|= HEAP_TAIL_CHECKING_ENABLED
;
1569 if (RtlpGetMode() == UserMode
)
1571 /* Also check these flags if in usermode */
1572 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_ALL
)
1573 Flags
|= HEAP_VALIDATE_ALL_ENABLED
;
1575 if (NtGlobalFlags
& FLG_HEAP_VALIDATE_PARAMETERS
)
1576 Flags
|= HEAP_VALIDATE_PARAMETERS_ENABLED
;
1578 if (NtGlobalFlags
& FLG_USER_STACK_TRACE_DB
)
1579 Flags
|= HEAP_CAPTURE_STACK_BACKTRACES
;
1582 Peb
= RtlGetCurrentPeb();
1584 /* Apply defaults for non-set parameters */
1585 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= Peb
->HeapSegmentCommit
;
1586 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= Peb
->HeapSegmentReserve
;
1587 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= Peb
->HeapDeCommitFreeBlockThreshold
;
1588 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= Peb
->HeapDeCommitTotalFreeThreshold
;
1592 /* Apply defaults for non-set parameters */
1594 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= MmHeapSegmentCommit
;
1595 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= MmHeapSegmentReserve
;
1596 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
1597 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
1601 // FIXME: Move to memory manager
1602 if (!Parameters
->SegmentCommit
) Parameters
->SegmentCommit
= PAGE_SIZE
* 2;
1603 if (!Parameters
->SegmentReserve
) Parameters
->SegmentReserve
= 1048576;
1604 if (!Parameters
->DeCommitFreeBlockThreshold
) Parameters
->DeCommitFreeBlockThreshold
= PAGE_SIZE
;
1605 if (!Parameters
->DeCommitTotalFreeThreshold
) Parameters
->DeCommitTotalFreeThreshold
= 65536;
1607 /* Get the max um address */
1608 Status
= ZwQuerySystemInformation(SystemBasicInformation
,
1610 sizeof(SystemInformation
),
1613 if (!NT_SUCCESS(Status
))
1615 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status
);
1619 MaximumUserModeAddress
= SystemInformation
.MaximumUserModeAddress
;
1621 /* Calculate max alloc size */
1622 if (!Parameters
->MaximumAllocationSize
)
1623 Parameters
->MaximumAllocationSize
= MaximumUserModeAddress
- (ULONG_PTR
)0x10000 - PAGE_SIZE
;
1625 MaxBlockSize
= 0x80000 - PAGE_SIZE
;
1627 if (!Parameters
->VirtualMemoryThreshold
||
1628 Parameters
->VirtualMemoryThreshold
> MaxBlockSize
)
1630 Parameters
->VirtualMemoryThreshold
= MaxBlockSize
;
1633 /* Check reserve/commit sizes and set default values */
1636 CommitSize
= PAGE_SIZE
;
1638 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1640 TotalSize
= 64 * PAGE_SIZE
;
1644 /* Round up the commit size to be at least the page size */
1645 CommitSize
= ROUND_UP(CommitSize
, PAGE_SIZE
);
1648 TotalSize
= ROUND_UP(TotalSize
, PAGE_SIZE
);
1650 TotalSize
= ROUND_UP(CommitSize
, 16 * PAGE_SIZE
);
1653 /* Calculate header size */
1654 HeaderSize
= sizeof(HEAP
);
1655 if (!(Flags
& HEAP_NO_SERIALIZE
))
1659 Flags
|= HEAP_LOCK_USER_ALLOCATED
;
1663 HeaderSize
+= sizeof(HEAP_LOCK
);
1664 AllocateLock
= TRUE
;
1669 /* Invalid parameters */
1673 /* See if we are already provided with an address for the heap */
1676 if (Parameters
->CommitRoutine
)
1678 /* There is a commit routine, so no problem here, check params */
1679 if ((Flags
& HEAP_GROWABLE
) ||
1680 !Parameters
->InitialCommit
||
1681 !Parameters
->InitialReserve
||
1682 (Parameters
->InitialCommit
> Parameters
->InitialReserve
))
1688 /* Calculate committed and uncommitted addresses */
1689 CommittedAddress
= Addr
;
1690 UncommittedAddress
= (PCHAR
)Addr
+ Parameters
->InitialCommit
;
1691 TotalSize
= Parameters
->InitialReserve
;
1693 /* Zero the initial page ourselves */
1694 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1697 /* Commit routine is absent, so query how much memory caller reserved */
1698 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1700 MemoryBasicInformation
,
1705 if (!NT_SUCCESS(Status
))
1707 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status
);
1712 if (MemoryInfo
.BaseAddress
!= Addr
||
1713 MemoryInfo
.State
== MEM_FREE
)
1718 /* Validation checks passed, set committed/uncommitted addresses */
1719 CommittedAddress
= Addr
;
1721 /* Check if it's committed or not */
1722 if (MemoryInfo
.State
== MEM_COMMIT
)
1724 /* Zero it out because it's already committed */
1725 RtlZeroMemory(CommittedAddress
, PAGE_SIZE
);
1727 /* Calculate uncommitted address value */
1728 CommitSize
= MemoryInfo
.RegionSize
;
1729 TotalSize
= CommitSize
;
1730 UncommittedAddress
= (PCHAR
)Addr
+ CommitSize
;
1732 /* Check if uncommitted address is reserved */
1733 Status
= ZwQueryVirtualMemory(NtCurrentProcess(),
1735 MemoryBasicInformation
,
1740 if (NT_SUCCESS(Status
) &&
1741 MemoryInfo
.State
== MEM_RESERVE
)
1743 /* It is, so add it up to the reserve size */
1744 TotalSize
+= MemoryInfo
.RegionSize
;
1749 /* It's not committed, inform following code that a commit is necessary */
1750 CommitSize
= PAGE_SIZE
;
1751 UncommittedAddress
= Addr
;
1755 /* Mark this as a user-committed mem */
1756 HeapSegmentFlags
= HEAP_USER_ALLOCATED
;
1761 /* Check commit routine */
1762 if (Parameters
->CommitRoutine
) return NULL
;
1764 /* Reserve memory */
1765 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1772 if (!NT_SUCCESS(Status
))
1774 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status
);
1778 /* Set base addresses */
1779 CommittedAddress
= Heap
;
1780 UncommittedAddress
= Heap
;
1783 /* Check if we need to commit something */
1784 if (CommittedAddress
== UncommittedAddress
)
1786 /* Commit the required size */
1787 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
1794 DPRINT("Committed %d bytes at base %p\n", CommitSize
, CommittedAddress
);
1796 if (!NT_SUCCESS(Status
))
1798 DPRINT1("Failure, Status 0x%08X\n", Status
);
1800 /* Release memory if it was reserved */
1801 if (!Addr
) ZwFreeVirtualMemory(NtCurrentProcess(),
1809 /* Calculate new uncommitted address */
1810 UncommittedAddress
= (PCHAR
)UncommittedAddress
+ CommitSize
;
1813 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap
, CommitSize
, TotalSize
);
1815 /* Initialize the heap */
1816 RtlpInitializeHeap(Heap
, &HeaderSize
, Flags
, AllocateLock
, Lock
);
1818 /* Initialize heap's first segment */
1819 if (!RtlpInitializeHeapSegment(Heap
,
1820 (PHEAP_SEGMENT
)((PCHAR
)Heap
+ HeaderSize
),
1825 (PCHAR
)CommittedAddress
+ TotalSize
))
1827 DPRINT1("Failed to initialize heap segment\n");
1831 /* Set other data */
1832 Heap
->ProcessHeapsListIndex
= 0;
1833 Heap
->SegmentCommit
= Parameters
->SegmentCommit
;
1834 Heap
->SegmentReserve
= Parameters
->SegmentReserve
;
1835 Heap
->DeCommitFreeBlockThreshold
= Parameters
->DeCommitFreeBlockThreshold
>> HEAP_ENTRY_SHIFT
;
1836 Heap
->DeCommitTotalFreeThreshold
= Parameters
->DeCommitTotalFreeThreshold
>> HEAP_ENTRY_SHIFT
;
1837 Heap
->MaximumAllocationSize
= Parameters
->MaximumAllocationSize
;
1838 Heap
->VirtualMemoryThreshold
= ROUND_UP(Parameters
->VirtualMemoryThreshold
, HEAP_ENTRY_SIZE
) >> HEAP_ENTRY_SHIFT
;
1839 Heap
->CommitRoutine
= Parameters
->CommitRoutine
;
1842 if (Flags
& HEAP_CREATE_ALIGN_16
)
1844 Heap
->AlignMask
= (ULONG
)~15;
1845 Heap
->AlignRound
= 15 + sizeof(HEAP_ENTRY
);
1849 Heap
->AlignMask
= (ULONG
)~(HEAP_ENTRY_SIZE
- 1);
1850 Heap
->AlignRound
= HEAP_ENTRY_SIZE
- 1 + sizeof(HEAP_ENTRY
);
1853 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
1854 Heap
->AlignRound
+= HEAP_ENTRY_SIZE
;
1856 /* Add heap to process list in case of usermode heap */
1857 if (RtlpGetMode() == UserMode
)
1859 RtlpAddHeapToProcessList(Heap
);
1861 // FIXME: What about lookasides?
1867 /***********************************************************************
1876 * Success: A NULL HANDLE, if heap is NULL or it was destroyed
1877 * Failure: The Heap handle, if heap is the process heap.
1880 RtlDestroyHeap(HANDLE HeapPtr
) /* [in] Handle of heap */
1882 PHEAP Heap
= (PHEAP
)HeapPtr
;
1883 PLIST_ENTRY Current
;
1884 PHEAP_UCR_DESCRIPTOR UcrDescriptor
;
1885 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
1889 PHEAP_SEGMENT Segment
;
1891 if (!HeapPtr
) return NULL
;
1893 // TODO: Check for special heap
1895 /* Check for a process heap */
1896 if (RtlpGetMode() == UserMode
&&
1897 HeapPtr
== NtCurrentPeb()->ProcessHeap
) return HeapPtr
;
1899 /* Free up all big allocations */
1900 Current
= Heap
->VirtualAllocdBlocks
.Flink
;
1901 while (Current
!= &Heap
->VirtualAllocdBlocks
)
1903 VirtualEntry
= CONTAINING_RECORD(Current
, HEAP_VIRTUAL_ALLOC_ENTRY
, Entry
);
1904 BaseAddress
= (PVOID
)VirtualEntry
;
1906 ZwFreeVirtualMemory(NtCurrentProcess(),
1912 /* Delete tags and remove heap from the process heaps list in user mode */
1913 if (RtlpGetMode() == UserMode
)
1915 // FIXME DestroyTags
1916 RtlpRemoveHeapFromProcessList(Heap
);
1919 /* Delete the heap lock */
1920 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
1922 /* Delete it if it wasn't user allocated */
1923 if (!(Heap
->Flags
& HEAP_LOCK_USER_ALLOCATED
))
1924 RtlDeleteHeapLock(Heap
->LockVariable
);
1926 /* Clear out the lock variable */
1927 Heap
->LockVariable
= NULL
;
1930 /* Go through heap's global uncommitted ranges list and free them */
1931 DPRINT1("HEAP: Freeing segment's UCRs is not yet implemented!\n");
1932 Current
= Heap
->UCRSegmentList
.Flink
;
1933 while(Current
!= &Heap
->UCRSegmentList
)
1935 UcrDescriptor
= CONTAINING_RECORD(Current
, HEAP_UCR_DESCRIPTOR
, ListEntry
);
1939 BaseAddress
= UcrDescriptor
->Address
;
1942 /* Release that memory */
1943 ZwFreeVirtualMemory(NtCurrentProcess(),
1949 /* Advance to the next descriptor */
1950 Current
= Current
->Flink
;
1953 /* Go through segments and destroy them */
1954 for (i
= HEAP_SEGMENTS
- 1; i
>= 0; i
--)
1956 Segment
= Heap
->Segments
[i
];
1957 if (Segment
) RtlpDestroyHeapSegment(Segment
);
1964 RtlpSplitEntry(PHEAP Heap
,
1965 PHEAP_FREE_ENTRY FreeBlock
,
1966 SIZE_T AllocationSize
,
1970 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
1972 PHEAP_ENTRY InUseEntry
;
1975 /* Save flags, update total free size */
1976 FreeFlags
= FreeBlock
->Flags
;
1977 Heap
->TotalFreeSize
-= FreeBlock
->Size
;
1979 /* Make this block an in-use one */
1980 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
1981 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
;
1982 InUseEntry
->SmallTagIndex
= 0;
1984 /* Calculate the extra amount */
1985 FreeSize
= InUseEntry
->Size
- Index
;
1987 /* Update it's size fields (we don't need their data anymore) */
1988 InUseEntry
->Size
= Index
;
1989 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
1991 /* If there is something to split - do the split */
1994 /* Don't split if resulting entry can't contain any payload data
1995 (i.e. being just HEAP_ENTRY_SIZE) */
1998 /* Increase sizes of the in-use entry */
2000 InUseEntry
->UnusedBytes
+= sizeof(HEAP_ENTRY
);
2004 /* Calculate a pointer to the new entry */
2005 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2008 SplitBlock
->Flags
= FreeFlags
;
2009 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2010 SplitBlock
->Size
= FreeSize
;
2011 SplitBlock
->PreviousSize
= Index
;
2013 /* Check if it's the last entry */
2014 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2016 /* Insert it to the free list if it's the last entry */
2017 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, TRUE
);
2018 Heap
->TotalFreeSize
+= FreeSize
;
2022 /* Not so easy - need to update next's previous size too */
2023 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2025 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2027 SplitBlock2
->PreviousSize
= (USHORT
)FreeSize
;
2028 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, TRUE
);
2029 Heap
->TotalFreeSize
+= FreeSize
;
2033 /* Even more complex - the next entry is free, so we can merge them into one! */
2034 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2036 /* Remove that next entry */
2037 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, TRUE
);
2040 FreeSize
+= SplitBlock2
->Size
;
2041 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2043 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2045 /* Insert it back */
2046 SplitBlock
->Size
= FreeSize
;
2048 /* Don't forget to update previous size of the next entry! */
2049 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2051 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2054 /* Actually insert it */
2055 RtlpInsertFreeBlockHelper( Heap
, SplitBlock
, (USHORT
)FreeSize
, TRUE
);
2057 /* Update total size */
2058 Heap
->TotalFreeSize
+= FreeSize
;
2062 /* Resulting block is quite big */
2063 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2068 /* Reset flags of the free entry */
2071 /* Update last entry in segment */
2072 if (SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
)
2074 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2079 /* Set last entry flag */
2080 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2081 InUseEntry
->Flags
|= HEAP_ENTRY_LAST_ENTRY
;
2087 RtlpAllocateNonDedicated(PHEAP Heap
,
2090 SIZE_T AllocationSize
,
2094 PLIST_ENTRY FreeListHead
, Next
;
2095 PHEAP_FREE_ENTRY FreeBlock
;
2096 PHEAP_ENTRY InUseEntry
;
2097 EXCEPTION_RECORD ExceptionRecord
;
2099 /* Go through the zero list to find a place where to insert the new entry */
2100 FreeListHead
= &Heap
->FreeLists
[0];
2102 /* Start from the largest block to reduce time */
2103 Next
= FreeListHead
->Blink
;
2104 if (FreeListHead
!= Next
)
2106 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
2108 if (FreeBlock
->Size
>= Index
)
2110 /* Our request is smaller than the largest entry in the zero list */
2112 /* Go through the list to find insertion place */
2113 Next
= FreeListHead
->Flink
;
2114 while (FreeListHead
!= Next
)
2116 FreeBlock
= CONTAINING_RECORD(Next
, HEAP_FREE_ENTRY
, FreeList
);
2118 if (FreeBlock
->Size
>= Index
)
2120 /* Found minimally fitting entry. Proceed to either using it as it is
2121 or splitting it to two entries */
2122 RemoveEntryList(&FreeBlock
->FreeList
);
2125 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2127 /* Release the lock */
2128 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2130 /* Zero memory if that was requested */
2131 if (Flags
& HEAP_ZERO_MEMORY
)
2132 RtlZeroMemory(InUseEntry
+ 1, Size
);
2134 /* Return pointer to the */
2135 return InUseEntry
+ 1;
2138 /* Advance to the next entry */
2144 /* Extend the heap, 0 list didn't have anything suitable */
2145 FreeBlock
= RtlpExtendHeap(Heap
, AllocationSize
);
2147 /* Use the new biggest entry we've got */
2150 RemoveEntryList(&FreeBlock
->FreeList
);
2153 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2155 /* Release the lock */
2156 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2158 /* Zero memory if that was requested */
2159 if (Flags
& HEAP_ZERO_MEMORY
)
2160 RtlZeroMemory(InUseEntry
+ 1, Size
);
2162 /* Return pointer to the */
2163 return InUseEntry
+ 1;
2166 /* Really unfortunate, out of memory condition */
2169 /* Generate an exception */
2170 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2172 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2173 ExceptionRecord
.ExceptionRecord
= NULL
;
2174 ExceptionRecord
.NumberParameters
= 1;
2175 ExceptionRecord
.ExceptionFlags
= 0;
2176 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2178 RtlRaiseException(&ExceptionRecord
);
2181 /* Release the lock */
2182 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2186 /***********************************************************************
2187 * HeapAlloc (KERNEL32.334)
2189 * Pointer to allocated memory block
2191 * 0x7d030f60--invalid flags in RtlHeapAllocate
2195 RtlAllocateHeap(IN PVOID HeapPtr
,
2199 PHEAP Heap
= (PHEAP
)HeapPtr
;
2200 PULONG FreeListsInUse
;
2201 ULONG FreeListsInUseUlong
;
2202 SIZE_T AllocationSize
;
2204 PLIST_ENTRY FreeListHead
;
2205 PHEAP_ENTRY InUseEntry
;
2206 PHEAP_FREE_ENTRY FreeBlock
;
2207 ULONG InUseIndex
, i
;
2209 EXCEPTION_RECORD ExceptionRecord
;
2210 BOOLEAN HeapLocked
= FALSE
;
2211 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock
= NULL
;
2215 Flags
|= Heap
->ForceFlags
;
2217 /* Check for the maximum size */
2218 if (Size
>= 0x80000000)
2225 HEAP_VALIDATE_ALL_ENABLED
|
2226 HEAP_VALIDATE_PARAMETERS_ENABLED
|
2227 HEAP_FLAG_PAGE_ALLOCS
|
2228 HEAP_EXTRA_FLAGS_MASK
|
2229 HEAP_CREATE_ENABLE_TRACING
|
2230 HEAP_FREE_CHECKING_ENABLED
|
2231 HEAP_TAIL_CHECKING_ENABLED
|
2232 HEAP_CREATE_ALIGN_16
))
2234 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags
);
2237 /* Calculate allocation size and index */
2238 if (!Size
) Size
= 1;
2239 AllocationSize
= (Size
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2240 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2242 /* Acquire the lock if necessary */
2243 if (!(Flags
& HEAP_NO_SERIALIZE
))
2245 RtlEnterHeapLock( Heap
->LockVariable
);
2249 /* Depending on the size, the allocation is going to be done from dedicated,
2250 non-dedicated lists or a virtual block of memory */
2251 if (Index
< HEAP_FREELISTS
)
2253 FreeListHead
= &Heap
->FreeLists
[Index
];
2255 if (!IsListEmpty(FreeListHead
))
2257 /* There is a free entry in this list */
2258 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2262 /* Save flags and remove the free entry */
2263 FreeFlags
= FreeBlock
->Flags
;
2264 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, TRUE
);
2266 /* Update the total free size of the heap */
2267 Heap
->TotalFreeSize
-= Index
;
2269 /* Initialize this block */
2270 InUseEntry
= (PHEAP_ENTRY
)FreeBlock
;
2271 InUseEntry
->Flags
= HEAP_ENTRY_BUSY
| (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
);
2272 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2273 InUseEntry
->SmallTagIndex
= 0;
2277 /* Find smallest free block which this request could fit in */
2278 InUseIndex
= Index
>> 5;
2279 FreeListsInUse
= &Heap
->u
.FreeListsInUseUlong
[InUseIndex
];
2281 /* This bit magic disables all sizes which are less than the requested allocation size */
2282 FreeListsInUseUlong
= *FreeListsInUse
++ & ~((1 << ((ULONG
)Index
& 0x1f)) - 1);
2284 /* If size is definitily more than our lists - go directly to the non-dedicated one */
2286 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2288 /* Go through the list */
2289 for (i
= InUseIndex
; i
< 4; i
++)
2291 if (FreeListsInUseUlong
)
2293 FreeListHead
= &Heap
->FreeLists
[i
* 32];
2297 if (i
< 3) FreeListsInUseUlong
= *FreeListsInUse
++;
2300 /* Nothing found, search in the non-dedicated list */
2302 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2304 /* That list is found, now calculate exact block */
2305 FreeListHead
+= RtlpFindLeastSetBit(FreeListsInUseUlong
);
2307 /* Take this entry and remove it from the list of free blocks */
2308 FreeBlock
= CONTAINING_RECORD(FreeListHead
->Blink
,
2311 RtlpRemoveFreeBlock(Heap
, FreeBlock
, TRUE
, TRUE
);
2314 InUseEntry
= RtlpSplitEntry(Heap
, FreeBlock
, AllocationSize
, Index
, Size
);
2317 /* Release the lock */
2318 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2320 /* Zero memory if that was requested */
2321 if (Flags
& HEAP_ZERO_MEMORY
)
2322 RtlZeroMemory(InUseEntry
+ 1, Size
);
2324 /* User data starts right after the entry's header */
2325 return InUseEntry
+ 1;
2327 else if (Index
<= Heap
->VirtualMemoryThreshold
)
2329 /* The block is too large for dedicated lists, but fine for a non-dedicated one */
2330 return RtlpAllocateNonDedicated(Heap
, Flags
, Size
, AllocationSize
, Index
, HeapLocked
);
2332 else if (Heap
->Flags
& HEAP_GROWABLE
)
2334 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
2335 AllocationSize
+= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY
) - sizeof(HEAP_ENTRY
);
2337 Status
= ZwAllocateVirtualMemory(NtCurrentProcess(),
2338 (PVOID
*)&VirtualBlock
,
2344 if (!NT_SUCCESS(Status
))
2347 /* Release the lock */
2348 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2352 /* Initialize the newly allocated block */
2353 VirtualBlock
->BusyBlock
.Size
= (AllocationSize
- Size
);
2354 VirtualBlock
->BusyBlock
.Flags
= HEAP_ENTRY_VIRTUAL_ALLOC
| HEAP_ENTRY_EXTRA_PRESENT
| HEAP_ENTRY_BUSY
;
2355 VirtualBlock
->CommitSize
= AllocationSize
;
2356 VirtualBlock
->ReserveSize
= AllocationSize
;
2358 /* Insert it into the list of virtual allocations */
2359 InsertTailList(&Heap
->VirtualAllocdBlocks
, &VirtualBlock
->Entry
);
2361 /* Release the lock */
2362 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2364 /* Return pointer to user data */
2365 return VirtualBlock
+ 1;
2368 /* Generate an exception */
2369 if (Flags
& HEAP_GENERATE_EXCEPTIONS
)
2371 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2372 ExceptionRecord
.ExceptionRecord
= NULL
;
2373 ExceptionRecord
.NumberParameters
= 1;
2374 ExceptionRecord
.ExceptionFlags
= 0;
2375 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2377 RtlRaiseException(&ExceptionRecord
);
2380 //STATUS_BUFFER_TOO_SMALL;
2382 /* Release the lock */
2383 if (HeapLocked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2388 /***********************************************************************
2389 * HeapFree (KERNEL32.338)
2396 BOOLEAN NTAPI
RtlFreeHeap(
2397 HANDLE HeapPtr
, /* [in] Handle of heap */
2398 ULONG Flags
, /* [in] Heap freeing flags */
2399 PVOID Ptr
/* [in] Address of memory to free */
2403 PHEAP_ENTRY HeapEntry
;
2404 USHORT TagIndex
= 0;
2406 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2407 BOOLEAN Locked
= FALSE
;
2410 /* Freeing NULL pointer is a legal operation */
2411 if (!Ptr
) return TRUE
;
2413 /* Get pointer to the heap and force flags */
2414 Heap
= (PHEAP
)HeapPtr
;
2415 Flags
|= Heap
->ForceFlags
;
2417 /* Lock if necessary */
2418 if (!(Flags
& HEAP_NO_SERIALIZE
))
2420 RtlEnterHeapLock(Heap
->LockVariable
);
2424 /* Get pointer to the heap entry */
2425 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2427 /* Check this entry, fail if it's invalid */
2428 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
) ||
2429 (((ULONG_PTR
)Ptr
& 0x7) != 0) ||
2430 (HeapEntry
->SegmentOffset
>= HEAP_SEGMENTS
))
2432 /* This is an invalid block */
2433 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr
);
2434 // FIXME: Set STATUS_INVALID_PARAMETER
2436 /* Release the heap lock */
2437 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2441 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2443 /* Big allocation */
2444 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2446 /* Remove it from the list */
2447 RemoveEntryList(&VirtualEntry
->Entry
);
2452 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2453 (PVOID
*)&VirtualEntry
,
2457 if (!NT_SUCCESS(Status
))
2459 DPRINT1("Failed releasing memory with Status 0x%08X\n", Status
);
2460 // TODO: Set this status in user mode
2465 /* Normal allocation */
2466 BlockSize
= HeapEntry
->Size
;
2470 /* Coalesce in kernel mode, and in usermode if it's not disabled */
2471 if (RtlpGetMode() == KernelMode
||
2472 (RtlpGetMode() == UserMode
&& !(Heap
->Flags
& HEAP_DISABLE_COALESCE_ON_FREE
)))
2474 HeapEntry
= (PHEAP_ENTRY
)RtlpCoalesceFreeBlocks(Heap
,
2475 (PHEAP_FREE_ENTRY
)HeapEntry
,
2480 /* If there is no need to decommit the block - put it into a free list */
2481 if (BlockSize
< Heap
->DeCommitFreeBlockThreshold
||
2482 (Heap
->TotalFreeSize
+ BlockSize
< Heap
->DeCommitTotalFreeThreshold
))
2484 /* Check if it needs to go to a 0 list */
2485 if (BlockSize
> HEAP_MAX_BLOCK_SIZE
)
2487 /* General-purpose 0 list */
2488 RtlpInsertFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2492 /* Usual free list */
2493 RtlpInsertFreeBlockHelper(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
, FALSE
);
2495 /* Assert sizes are consistent */
2496 if (!(HeapEntry
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2498 ASSERT((HeapEntry
+ BlockSize
)->PreviousSize
== BlockSize
);
2501 /* Increase the free size */
2502 Heap
->TotalFreeSize
+= BlockSize
;
2506 if (RtlpGetMode() == UserMode
&&
2515 /* Decommit this block */
2516 RtlpDeCommitFreeBlock(Heap
, (PHEAP_FREE_ENTRY
)HeapEntry
, BlockSize
);
2520 /* Release the heap lock */
2521 if (Locked
) RtlLeaveHeapLock(Heap
->LockVariable
);
2527 RtlpGrowBlockInPlace (IN PHEAP Heap
,
2529 IN PHEAP_ENTRY InUseEntry
,
2533 /* We always fail growing in place now */
2537 PHEAP_ENTRY_EXTRA NTAPI
2538 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry
)
2540 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry
;
2542 /* Check if it's a big block */
2543 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2545 VirtualEntry
= CONTAINING_RECORD(HeapEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2547 /* Return a pointer to the extra stuff*/
2548 return &VirtualEntry
->ExtraStuff
;
2552 /* This is a usual entry, which means extra stuff follows this block */
2553 return (PHEAP_ENTRY_EXTRA
)(HeapEntry
+ HeapEntry
->Size
- 1);
2558 /***********************************************************************
2561 * Heap [in] Handle of heap block
2562 * Flags [in] Heap reallocation flags
2563 * Ptr, [in] Address of memory to reallocate
2564 * Size [in] Number of bytes to reallocate
2567 * Pointer to reallocated memory block
2569 * 0x7d030f60--invalid flags in RtlHeapAllocate
2573 RtlReAllocateHeap(HANDLE HeapPtr
,
2578 PHEAP Heap
= (PHEAP
)HeapPtr
;
2579 PHEAP_ENTRY InUseEntry
, NewInUseEntry
;
2580 PHEAP_ENTRY_EXTRA OldExtra
, NewExtra
;
2581 SIZE_T AllocationSize
, FreeSize
, DecommitSize
;
2582 BOOLEAN HeapLocked
= FALSE
;
2583 PVOID NewBaseAddress
;
2584 PHEAP_FREE_ENTRY SplitBlock
, SplitBlock2
;
2585 SIZE_T OldSize
, Index
, OldIndex
;
2589 SIZE_T RemainderBytes
, ExtraSize
;
2590 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock
;
2591 EXCEPTION_RECORD ExceptionRecord
;
2593 /* Return success in case of a null pointer */
2600 /* Force heap flags */
2601 Flags
|= Heap
->ForceFlags
;
2603 // Check for special heap
2605 /* Make sure size is valid */
2606 if (Size
>= 0x80000000)
2612 /* Calculate allocation size and index */
2613 if (!Size
) Size
= 1;
2614 AllocationSize
= (Size
+ Heap
->AlignRound
) & Heap
->AlignMask
;
2616 /* Add up extra stuff, if it is present anywhere */
2617 if (((((PHEAP_ENTRY
)Ptr
)-1)->Flags
& HEAP_ENTRY_EXTRA_PRESENT
) ||
2618 (Flags
& HEAP_EXTRA_FLAGS_MASK
) ||
2619 Heap
->PseudoTagEntries
)
2621 AllocationSize
+= sizeof(HEAP_ENTRY_EXTRA
);
2624 /* Acquire the lock if necessary */
2625 if (!(Flags
& HEAP_NO_SERIALIZE
))
2627 RtlEnterHeapLock(Heap
->LockVariable
);
2629 Flags
^= HEAP_NO_SERIALIZE
;
2632 /* Get the pointer to the in-use entry */
2633 InUseEntry
= (PHEAP_ENTRY
)Ptr
- 1;
2635 /* If that entry is not really in-use, we have a problem */
2636 if (!(InUseEntry
->Flags
& HEAP_ENTRY_BUSY
))
2638 // STATUS_INVALID_PARAMETER
2640 /* Release the lock and return */
2642 RtlLeaveHeapLock(Heap
->LockVariable
);
2646 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2648 /* This is a virtually allocated block. Get its size */
2649 OldSize
= RtlpGetSizeOfBigBlock(InUseEntry
);
2651 /* Convert it to an index */
2652 OldIndex
= (OldSize
+ InUseEntry
->Size
) >> HEAP_ENTRY_SHIFT
;
2654 /* Calculate new allocation size and round it to the page size */
2655 AllocationSize
+= FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2656 AllocationSize
= ROUND_UP(AllocationSize
, PAGE_SIZE
);
2661 OldIndex
= InUseEntry
->Size
;
2663 OldSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - InUseEntry
->UnusedBytes
;
2666 /* Calculate new index */
2667 Index
= AllocationSize
>> HEAP_ENTRY_SHIFT
;
2669 /* Check for 4 different scenarios (old size, new size, old index, new index) */
2670 if (Index
<= OldIndex
)
2672 /* Difference must be greater than 1, adjust if it's not so */
2673 if (Index
+ 1 == OldIndex
)
2676 AllocationSize
+= sizeof(HEAP_ENTRY
);
2679 /* Calculate new size */
2680 if (InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2682 /* Simple in case of a virtual alloc - just an unused size */
2683 InUseEntry
->Size
= AllocationSize
- Size
;
2685 else if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2687 /* There is extra stuff, take it into account */
2688 OldExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ InUseEntry
->Size
- 1);
2689 NewExtra
= (PHEAP_ENTRY_EXTRA
)(InUseEntry
+ Index
- 1);
2690 *NewExtra
= *OldExtra
;
2692 // FIXME Tagging, TagIndex
2694 /* Update unused bytes count */
2695 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2699 // FIXME Tagging, SmallTagIndex
2700 InUseEntry
->UnusedBytes
= AllocationSize
- Size
;
2703 /* If new size is bigger than the old size */
2706 /* Zero out that additional space if required */
2707 if (Flags
& HEAP_ZERO_MEMORY
)
2709 RtlZeroMemory((PCHAR
)Ptr
+ OldSize
, Size
- OldSize
);
2711 else if (Heap
->Flags
& HEAP_FREE_CHECKING_ENABLED
)
2713 /* Fill it on free if required */
2714 RemainderBytes
= OldSize
& (sizeof(ULONG
) - 1);
2717 RemainderBytes
= 4 - RemainderBytes
;
2719 if (Size
> (OldSize
+ RemainderBytes
))
2721 /* Calculate actual amount of extra bytes to fill */
2722 ExtraSize
= (Size
- (OldSize
+ RemainderBytes
)) & ~(sizeof(ULONG
) - 1);
2724 /* Fill them if there are any */
2727 RtlFillMemoryUlong((PCHAR
)(InUseEntry
+ 1) + OldSize
+ RemainderBytes
,
2729 ARENA_INUSE_FILLER
);
2735 /* Fill tail of the heap entry if required */
2736 if (Heap
->Flags
& HEAP_TAIL_CHECKING_ENABLED
)
2738 RtlFillMemory((PCHAR
)(InUseEntry
+ 1) + Size
,
2743 /* Check if the difference is significant or not */
2744 if (Index
!= OldIndex
)
2747 FreeFlags
= InUseEntry
->Flags
& ~HEAP_ENTRY_BUSY
;
2749 if (FreeFlags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
2751 /* This is a virtual block allocation */
2752 VirtualAllocBlock
= CONTAINING_RECORD(InUseEntry
, HEAP_VIRTUAL_ALLOC_ENTRY
, BusyBlock
);
2756 DecommitBase
= (PCHAR
)VirtualAllocBlock
+ AllocationSize
;
2757 DecommitSize
= (OldIndex
<< HEAP_ENTRY_SHIFT
) - AllocationSize
;
2759 /* Release the memory */
2760 Status
= ZwFreeVirtualMemory(NtCurrentProcess(),
2761 (PVOID
*)&DecommitBase
,
2765 if (!NT_SUCCESS(Status
))
2767 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase
, DecommitSize
, Status
);
2771 /* Otherwise reduce the commit size */
2772 VirtualAllocBlock
->CommitSize
-= DecommitSize
;
2777 /* Reduce size of the block and possibly split it */
2778 SplitBlock
= (PHEAP_FREE_ENTRY
)(InUseEntry
+ Index
);
2780 /* Initialize this entry */
2781 SplitBlock
->Flags
= FreeFlags
;
2782 SplitBlock
->PreviousSize
= Index
;
2783 SplitBlock
->SegmentOffset
= InUseEntry
->SegmentOffset
;
2785 /* Remember free size */
2786 FreeSize
= InUseEntry
->Size
- Index
;
2789 InUseEntry
->Size
= Index
;
2790 InUseEntry
->Flags
&= ~HEAP_ENTRY_LAST_ENTRY
;
2792 /* Is that the last entry */
2793 if (FreeFlags
& HEAP_ENTRY_LAST_ENTRY
)
2795 /* Update segment's last entry */
2796 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2798 /* Set its size and insert it to the list */
2799 SplitBlock
->Size
= (USHORT
)FreeSize
;
2800 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2802 /* Update total free size */
2803 Heap
->TotalFreeSize
+= FreeSize
;
2807 /* Get the block after that one */
2808 SplitBlock2
= (PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
);
2810 if (SplitBlock2
->Flags
& HEAP_ENTRY_BUSY
)
2812 /* It's in use, add it here*/
2813 SplitBlock
->Size
= (USHORT
)FreeSize
;
2815 /* Update previous size of the next entry */
2816 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= (USHORT
)FreeSize
;
2818 /* Insert it to the list */
2819 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2821 /* Update total size */
2822 Heap
->TotalFreeSize
+= FreeSize
;
2826 /* Next entry is free, so merge with it */
2827 SplitBlock
->Flags
= SplitBlock2
->Flags
;
2829 /* Remove it, update total size */
2830 RtlpRemoveFreeBlock(Heap
, SplitBlock2
, FALSE
, FALSE
);
2831 Heap
->TotalFreeSize
-= SplitBlock2
->Size
;
2833 /* Calculate total free size */
2834 FreeSize
+= SplitBlock2
->Size
;
2836 if (FreeSize
<= HEAP_MAX_BLOCK_SIZE
)
2838 SplitBlock
->Size
= FreeSize
;
2840 if (!(SplitBlock
->Flags
& HEAP_ENTRY_LAST_ENTRY
))
2842 /* Update previous size of the next entry */
2843 ((PHEAP_FREE_ENTRY
)((PHEAP_ENTRY
)SplitBlock
+ FreeSize
))->PreviousSize
= FreeSize
;
2847 Heap
->Segments
[SplitBlock
->SegmentOffset
]->LastEntryInSegment
= (PHEAP_ENTRY
)SplitBlock
;
2850 /* Insert the new one back and update total size */
2851 RtlpInsertFreeBlockHelper(Heap
, SplitBlock
, FreeSize
, FALSE
);
2852 Heap
->TotalFreeSize
+= FreeSize
;
2857 RtlpInsertFreeBlock(Heap
, SplitBlock
, FreeSize
);
2866 /* We're growing the block */
2867 if ((InUseEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
) ||
2868 !RtlpGrowBlockInPlace(Heap
, Flags
, InUseEntry
, Size
, Index
))
2870 /* Growing in place failed, so growing out of place */
2871 if (Flags
& HEAP_REALLOC_IN_PLACE_ONLY
)
2873 DPRINT1("Realloc in place failed, but it was the only option\n");
2878 /* Clear tag bits */
2879 Flags
&= ~HEAP_TAG_MASK
;
2881 /* Process extra stuff */
2882 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2884 /* Preserve user settable flags */
2885 Flags
&= ~HEAP_SETTABLE_USER_FLAGS
;
2887 Flags
|= HEAP_SETTABLE_USER_VALUE
| ((InUseEntry
->Flags
& HEAP_ENTRY_SETTABLE_FLAGS
) << 4);
2891 else if (InUseEntry
->SmallTagIndex
)
2893 /* Take small tag index into account */
2894 Flags
|= InUseEntry
->SmallTagIndex
<< HEAP_TAG_SHIFT
;
2897 /* Allocate new block from the heap */
2898 NewBaseAddress
= RtlAllocateHeap(HeapPtr
,
2899 Flags
& ~HEAP_ZERO_MEMORY
,
2902 /* Proceed if it didn't fail */
2905 /* Get new entry pointer */
2906 NewInUseEntry
= (PHEAP_ENTRY
)NewBaseAddress
- 1;
2908 /* Process extra stuff if it exists */
2909 if (NewInUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2911 NewExtra
= RtlpGetExtraStuffPointer(NewInUseEntry
);
2913 if (InUseEntry
->Flags
& HEAP_ENTRY_EXTRA_PRESENT
)
2915 OldExtra
= RtlpGetExtraStuffPointer(InUseEntry
);
2916 NewExtra
->Settable
= OldExtra
->Settable
;
2920 RtlZeroMemory(NewExtra
, sizeof(*NewExtra
));
2924 /* Copy actual user bits */
2926 RtlMoveMemory(NewBaseAddress
, Ptr
, Size
);
2928 RtlMoveMemory(NewBaseAddress
, Ptr
, OldSize
);
2930 /* Zero remaining part if required */
2931 if (Size
> OldSize
&&
2932 (Flags
& HEAP_ZERO_MEMORY
))
2934 RtlZeroMemory((PCHAR
)NewBaseAddress
+ OldSize
, Size
- OldSize
);
2937 /* Free the old block */
2938 RtlFreeHeap(HeapPtr
, Flags
, Ptr
);
2941 Ptr
= NewBaseAddress
;
2946 /* Did resizing fail? */
2947 if (!Ptr
&& (Flags
& HEAP_GENERATE_EXCEPTIONS
))
2949 /* Generate an exception if required */
2950 ExceptionRecord
.ExceptionCode
= STATUS_NO_MEMORY
;
2951 ExceptionRecord
.ExceptionRecord
= NULL
;
2952 ExceptionRecord
.NumberParameters
= 1;
2953 ExceptionRecord
.ExceptionFlags
= 0;
2954 ExceptionRecord
.ExceptionInformation
[0] = AllocationSize
;
2956 RtlRaiseException(&ExceptionRecord
);
2959 /* Release the heap lock if it was acquired */
2961 RtlLeaveHeapLock(Heap
->LockVariable
);
2967 /***********************************************************************
2973 RtlCompactHeap(HANDLE Heap
,
2981 /***********************************************************************
2983 * Attempts to acquire the critical section object for a specified heap.
2986 * Heap [in] Handle of heap to lock for exclusive access
2995 RtlLockHeap(IN HANDLE HeapPtr
)
2997 PHEAP Heap
= (PHEAP
)HeapPtr
;
2999 // FIXME Check for special heap
3001 /* Check if it's really a heap */
3002 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3004 /* Lock if it's lockable */
3005 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3007 RtlEnterHeapLock(Heap
->LockVariable
);
3014 /***********************************************************************
3016 * Releases ownership of the critical section object.
3019 * Heap [in] Handle to the heap to unlock
3028 RtlUnlockHeap(HANDLE HeapPtr
)
3030 PHEAP Heap
= (PHEAP
)HeapPtr
;
3032 // FIXME Check for special heap
3034 /* Check if it's really a heap */
3035 if (Heap
->Signature
!= HEAP_SIGNATURE
) return FALSE
;
3037 /* Lock if it's lockable */
3038 if (!(Heap
->Flags
& HEAP_NO_SERIALIZE
))
3040 RtlLeaveHeapLock(Heap
->LockVariable
);
3047 /***********************************************************************
3050 * Heap [in] Handle of heap
3051 * Flags [in] Heap size control flags
3052 * Ptr [in] Address of memory to return size for
3055 * Size in bytes of allocated memory
3056 * 0xffffffff: Failure
3067 PHEAP Heap
= (PHEAP
)HeapPtr
;
3068 PHEAP_ENTRY HeapEntry
;
3071 // FIXME This is a hack around missing SEH support!
3074 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE
);
3079 Flags
|= Heap
->Flags
;
3081 // FIXME Special heap
3083 /* Get the heap entry pointer */
3084 HeapEntry
= (PHEAP_ENTRY
)Ptr
- 1;
3086 /* Return -1 if that entry is free */
3087 if (!(HeapEntry
->Flags
& HEAP_ENTRY_BUSY
))
3089 // STATUS_INVALID_PARAMETER
3093 /* Get size of this block depending if it's a usual or a big one */
3094 if (HeapEntry
->Flags
& HEAP_ENTRY_VIRTUAL_ALLOC
)
3096 EntrySize
= RtlpGetSizeOfBigBlock(HeapEntry
);
3101 EntrySize
= (HeapEntry
->Size
<< HEAP_ENTRY_SHIFT
) - HeapEntry
->UnusedBytes
;
3104 /* Return calculated size */
3109 /***********************************************************************
3111 * Validates a specified heap.
3114 * Heap [in] Handle to the heap
3115 * Flags [in] Bit flags that control access during operation
3116 * Block [in] Optional pointer to memory block to validate
3127 BOOLEAN NTAPI
RtlValidateHeap(
3138 RtlInitializeHeapManager(VOID
)
3143 Peb
= RtlGetCurrentPeb();
3145 /* Initialize heap-related fields of PEB */
3146 Peb
->NumberOfHeaps
= 0;
3147 Peb
->MaximumNumberOfHeaps
= HEAP_MAX_PROCESS_HEAPS
;
3148 Peb
->ProcessHeaps
= (PVOID
)RtlpProcessHeaps
;
3150 /* Initialize the process heaps list protecting lock */
3151 RtlInitializeHeapLock(&RtlpProcessHeapsListLock
);
3159 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine
,
3163 return STATUS_NOT_IMPLEMENTED
;
3171 RtlGetProcessHeaps(ULONG count
,
3183 RtlValidateProcessHeaps(VOID
)
3195 IN PVOID HeapHandle
,
3208 RtlSetUserValueHeap(IN PVOID HeapHandle
,
3210 IN PVOID BaseAddress
,
3222 RtlSetUserFlagsHeap(IN PVOID HeapHandle
,
3224 IN PVOID BaseAddress
,
3235 RtlGetUserInfoHeap(IN PVOID HeapHandle
,
3237 IN PVOID BaseAddress
,
3238 OUT PVOID
*UserValue
,
3239 OUT PULONG UserFlags
)
3250 RtlUsageHeap(IN HANDLE Heap
,
3252 OUT PRTL_HEAP_USAGE Usage
)
3256 return STATUS_NOT_IMPLEMENTED
;
3261 RtlQueryTagHeap(IN PVOID HeapHandle
,
3264 IN BOOLEAN ResetCounters
,
3265 OUT PRTL_HEAP_TAG_INFO HeapTagInfo
)
3274 RtlExtendHeap(IN HANDLE Heap
,
3286 RtlCreateTagHeap(IN HANDLE HeapHandle
,
3289 IN PWSTR TagSubName
)
3298 RtlWalkHeap(IN HANDLE HeapHandle
,
3302 return STATUS_NOT_IMPLEMENTED
;
3307 RtlProtectHeap(IN PVOID HeapHandle
,
3308 IN BOOLEAN ReadOnly
)
3316 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL
,
3317 IN HEAP_INFORMATION_CLASS HeapInformationClass
,
3318 IN PVOID HeapInformation
,
3319 IN SIZE_T HeapInformationLength
)
3327 RtlQueryHeapInformation(HANDLE HeapHandle
,
3328 HEAP_INFORMATION_CLASS HeapInformationClass
,
3329 PVOID HeapInformation OPTIONAL
,
3330 SIZE_T HeapInformationLength OPTIONAL
,
3331 PSIZE_T ReturnLength OPTIONAL
)
3339 RtlMultipleAllocateHeap(IN PVOID HeapHandle
,
3351 RtlMultipleFreeHeap(IN PVOID HeapHandle
,