3397 lines
139 KiB
C
3397 lines
139 KiB
C
/*++
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
heapdll.c
|
|
|
|
Abstract:
|
|
This module implements the user mode only portions of the heap allocator.
|
|
|
|
Author:
|
|
Steve Wood (stevewo) 20-Sep-1994
|
|
*/
|
|
|
|
#include "ntrtlp.h"
|
|
#include "heap.h"
|
|
#include "heappriv.h"
|
|
|
|
#ifdef NTHEAP_ENABLED
|
|
#include "heapp.h"
|
|
#endif // NTHEAP_ENABLED
|
|
|
|
|
|
// This structure is used by RtlUsageHeap to keep track of heap usage between calls.
|
|
// This package typecasts an extra reserved buffer passed in by the user to hold this information
|
|
typedef struct _RTL_HEAP_USAGE_INTERNAL {
|
|
PVOID Base;
|
|
SIZE_T ReservedSize;
|
|
SIZE_T CommittedSize;
|
|
PRTL_HEAP_USAGE_ENTRY FreeList;
|
|
PRTL_HEAP_USAGE_ENTRY LargeEntriesSentinal;
|
|
ULONG Reserved;
|
|
} RTL_HEAP_USAGE_INTERNAL, *PRTL_HEAP_USAGE_INTERNAL;
|
|
|
|
|
|
// Note that the following variables are specific to each process
|
|
|
|
// This is a lock used to protect access the this processes heap list
|
|
HEAP_LOCK RtlpProcessHeapsListLock;
|
|
|
|
// This is a specific list of heaps initialized and used by the process
|
|
#define RTLP_STATIC_HEAP_LIST_SIZE 16
|
|
PHEAP RtlpProcessHeapsListBuffer[RTLP_STATIC_HEAP_LIST_SIZE];
|
|
|
|
// This variable stores a pointer to the heap used to storage global heap tags
|
|
PHEAP RtlpGlobalTagHeap = NULL;
|
|
|
|
// This varible is used by the process as work space to build up names for pseudo tags
|
|
static WCHAR RtlpPseudoTagNameBuffer[24];
|
|
|
|
BOOLEAN RtlpGrowBlockInPlace(IN PHEAP Heap,
|
|
IN ULONG Flags,
|
|
IN PHEAP_ENTRY BusyBlock,
|
|
IN SIZE_T Size,
|
|
IN SIZE_T AllocationIndex);
|
|
PVOID RtlDebugReAllocateHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN SIZE_T Size);
|
|
BOOLEAN RtlDebugGetUserInfoHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
OUT PVOID *UserValue OPTIONAL,
|
|
OUT PULONG UserFlags OPTIONAL);
|
|
BOOLEAN RtlDebugSetUserValueHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN PVOID UserValue);
|
|
BOOLEAN RtlDebugSetUserFlagsHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN ULONG UserFlagsReset,
|
|
IN ULONG UserFlagsSet);
|
|
SIZE_T RtlDebugCompactHeap(IN PVOID HeapHandle, IN ULONG Flags);
|
|
NTSTATUS RtlDebugCreateTagHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PWSTR TagPrefix OPTIONAL,
|
|
IN PWSTR TagNames);
|
|
PWSTR RtlDebugQueryTagHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN USHORT TagIndex,
|
|
IN BOOLEAN ResetCounters,
|
|
OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL);
|
|
NTSTATUS RtlDebugUsageHeap(IN PVOID HeapHandle, IN ULONG Flags, IN OUT PRTL_HEAP_USAGE Usage);
|
|
BOOLEAN RtlDebugWalkHeap(IN PVOID HeapHandle, IN OUT PRTL_HEAP_WALK_ENTRY Entry);
|
|
PHEAP_TAG_ENTRY RtlpAllocateTags(PHEAP Heap, ULONG NumberOfTags);
|
|
PRTL_HEAP_USAGE_ENTRY RtlpFreeHeapUsageEntry(PRTL_HEAP_USAGE_INTERNAL Buffer,
|
|
PRTL_HEAP_USAGE_ENTRY p);
|
|
NTSTATUS RtlpAllocateHeapUsageEntry(PRTL_HEAP_USAGE_INTERNAL Buffer, PRTL_HEAP_USAGE_ENTRY *pp);
|
|
|
|
// Declared in ntrtl.h
|
|
|
|
NTSTATUS RtlInitializeHeapManager(VOID)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to initialize the heap manager for the current process
|
|
Return Value:
|
|
None.
|
|
*/
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
|
|
#if DBG
|
|
// Sanity check the sizes of the header entry structures
|
|
if (sizeof(HEAP_ENTRY) != sizeof(HEAP_ENTRY_EXTRA)) {
|
|
HeapDebugPrint(("Heap header and extra header sizes disagree\n"));
|
|
HeapDebugBreak(NULL);
|
|
}
|
|
|
|
if (sizeof(HEAP_ENTRY) != CHECK_HEAP_TAIL_SIZE) {
|
|
HeapDebugPrint(("Heap header and tail fill sizes disagree\n"));
|
|
HeapDebugBreak(NULL);
|
|
}
|
|
|
|
if (sizeof(HEAP_FREE_ENTRY) != (2 * sizeof(HEAP_ENTRY))) {
|
|
HeapDebugPrint(("Heap header and free header sizes disagree\n"));
|
|
HeapDebugBreak(NULL);
|
|
}
|
|
#endif
|
|
|
|
// Initialize the heap specific structures in the current peb
|
|
Peb->NumberOfHeaps = 0;
|
|
Peb->MaximumNumberOfHeaps = RTLP_STATIC_HEAP_LIST_SIZE;
|
|
Peb->ProcessHeaps = RtlpProcessHeapsListBuffer;
|
|
|
|
#ifdef NTHEAP_ENABLED
|
|
{
|
|
(VOID)RtlInitializeNtHeapManager();
|
|
}
|
|
#endif
|
|
|
|
return RtlInitializeLockRoutine(&RtlpProcessHeapsListLock.Lock);// Initialize the lock and return to our caller
|
|
}
|
|
|
|
|
|
// Declared in ntrtl.h
|
|
|
|
VOID RtlProtectHeap(IN PVOID HeapHandle, IN BOOLEAN MakeReadOnly)
|
|
/*++
|
|
Routine Description:
|
|
This routine will change the protection on all the pages in a heap to be either readonly or readwrite
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being altered
|
|
MakeReadOnly - Specifies if the heap is to be made readonly or readwrite
|
|
*/
|
|
{
|
|
PHEAP Heap;
|
|
UCHAR SegmentIndex;
|
|
PHEAP_SEGMENT Segment;
|
|
MEMORY_BASIC_INFORMATION VaInfo;
|
|
NTSTATUS Status;
|
|
PVOID Address;
|
|
PVOID ProtectAddress;
|
|
SIZE_T Size;
|
|
ULONG OldProtect;
|
|
ULONG NewProtect;
|
|
|
|
Heap = (PHEAP)HeapHandle;
|
|
|
|
// For every valid segment in the heap we will zoom through all its regions and for those that are committed we'll change it protection
|
|
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
if (Segment) {
|
|
// Starting from the first address for the segment and going to the last address in the segment we'll step through by regions
|
|
Address = Segment->BaseAddress;
|
|
|
|
while ((ULONG_PTR)Address < (ULONG_PTR)(Segment->LastValidEntry)) {
|
|
// Query the current region to get its state and size
|
|
Status = ZwQueryVirtualMemory(NtCurrentProcess(),
|
|
Address,
|
|
MemoryBasicInformation,
|
|
&VaInfo,
|
|
sizeof(VaInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
HeapDebugPrint(("VirtualQuery Failed 0x%08x %x\n", Address, Status));
|
|
return;
|
|
}
|
|
|
|
// If we found a commited block then set its protection
|
|
if (VaInfo.State == MEM_COMMIT) {
|
|
Size = VaInfo.RegionSize;
|
|
ProtectAddress = Address;
|
|
if (MakeReadOnly) {
|
|
NewProtect = PAGE_READONLY;
|
|
}
|
|
else {
|
|
NewProtect = PAGE_READWRITE;
|
|
}
|
|
|
|
Status = ZwProtectVirtualMemory(NtCurrentProcess(),
|
|
&ProtectAddress,
|
|
&Size,
|
|
NewProtect,
|
|
&OldProtect);
|
|
if (!NT_SUCCESS(Status)) {
|
|
HeapDebugPrint(("VirtualProtect Failed 0x%08x %x\n", Address, Status));
|
|
return;
|
|
}
|
|
}
|
|
|
|
Address = (PVOID)((PCHAR)Address + VaInfo.RegionSize);// Now calculate the address of the next region in the segment
|
|
}
|
|
}
|
|
}
|
|
|
|
return;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
BOOLEAN RtlLockHeap(IN PVOID HeapHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used by lock access to a specific heap structure
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being locked
|
|
Return Value:
|
|
BOOLEAN - TRUE if the heap is now locked and FALSE otherwise (i.e., the heap is ill-formed).
|
|
TRUE is returned even if the heap is not lockable.
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, RtlpDebugPageHeapLock(HeapHandle));// Check for the heap protected by guard pages
|
|
|
|
if (!RtlpCheckHeapSignature(Heap, "RtlLockHeap")) {// Validate that HeapAddress points to a HEAP structure.
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {// Lock the heap. And disable the lookaside list by incrementing its lock count.
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
Heap->LookasideLockCount += 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
BOOLEAN RtlUnlockHeap(IN PVOID HeapHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to unlock access to a specific heap structure
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heep being unlocked
|
|
Return Value:
|
|
BOOLEAN - TRUE if the heap is now unlocked and FALSE otherwise (i.e., the heap is ill-formed).
|
|
TRUE is also returned if the heap was never locked to begin with because it is not seralizable.
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, RtlpDebugPageHeapUnlock(HeapHandle));// Check for the heap protected by guard pages
|
|
|
|
if (!RtlpCheckHeapSignature(Heap, "RtlUnlockHeap")) {// Validate that HeapAddress points to a HEAP structure.
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {// Unlock the heap. And enable the lookaside logic by decrementing its lock count
|
|
Heap->LookasideLockCount -= 1;
|
|
RtlReleaseLockRoutine(Heap->LockVariable);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
PVOID RtlReAllocateHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN SIZE_T Size)
|
|
/*++
|
|
Routine Description:
|
|
This routine will resize a user specified heap block.
|
|
The new size can either be smaller or larger than the current block size.
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of heap flags to augment those already enforced by the heap
|
|
BaseAddress - Supplies the current address of a block allocated from heap.
|
|
We will try and resize this block at its current address, but it could possibly move if this heap structure allows for relocation
|
|
Size - Supplies the size, in bytes, for the newly resized heap block
|
|
Return Value:
|
|
PVOID - A pointer to the resized block. If the block had to move then this address will not be equal to the input base address
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
SIZE_T AllocationSize;
|
|
PHEAP_ENTRY BusyBlock, NewBusyBlock;
|
|
PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
|
|
SIZE_T FreeSize;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
PVOID NewBaseAddress;
|
|
PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
|
|
SIZE_T OldSize;
|
|
SIZE_T AllocationIndex;
|
|
SIZE_T OldAllocationIndex;
|
|
UCHAR FreeFlags;
|
|
NTSTATUS Status;
|
|
PVOID DeCommitAddress;
|
|
SIZE_T DeCommitSize;
|
|
EXCEPTION_RECORD ExceptionRecord;
|
|
|
|
if (BaseAddress == NULL) {// If there isn't an address to relocate the heap at then our work is done
|
|
SET_LAST_STATUS(STATUS_SUCCESS);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef NTHEAP_ENABLED
|
|
{
|
|
if (Heap->Flags & NTHEAP_ENABLED_FLAG) {
|
|
return RtlReAllocateNtHeap(HeapHandle, Flags, BaseAddress, Size);
|
|
}
|
|
}
|
|
#endif // NTHEAP_ENABLED
|
|
|
|
Flags |= Heap->ForceFlags;// Augment the heap flags
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check if we should simply call the debug version of heap to do the work
|
|
return RtlDebugReAllocateHeap(HeapHandle, Flags, BaseAddress, Size);
|
|
}
|
|
|
|
if (Size > 0x7fffffff) {// Make sure we didn't get a negative heap size
|
|
SET_LAST_STATUS(STATUS_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
// Round the requested size up to the allocation granularity.
|
|
// Note that if the request is for 0 bytes, we still allocate memory,
|
|
// because we add in an extra byte to protect ourselves from idiots.
|
|
AllocationSize = ((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask;
|
|
|
|
if ((Flags & HEAP_NEED_EXTRA_FLAGS) ||
|
|
(Heap->PseudoTagEntries != NULL) ||
|
|
((((PHEAP_ENTRY)BaseAddress) - 1)->Flags & HEAP_ENTRY_EXTRA_PRESENT))
|
|
{
|
|
AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
|
|
}
|
|
|
|
try {
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {// Lock the heap
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
Flags ^= HEAP_NO_SERIALIZE;// Because it is now zero the following statement will set the no serialize bit
|
|
}
|
|
|
|
try {
|
|
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;// Compute the heap block address for user specified block
|
|
|
|
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {// Check if the block is not in use then it is an error
|
|
SET_LAST_STATUS(STATUS_INVALID_PARAMETER);
|
|
leave;// Bail if not a busy block.
|
|
|
|
// We need the current (i.e., old) size and allocation of the block.
|
|
// Check if the block is a big allocation. The size field of a big block is really the unused by count
|
|
}
|
|
else if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
|
|
OldSize = RtlpGetSizeOfBigBlock(BusyBlock);
|
|
OldAllocationIndex = (OldSize + BusyBlock->Size) >> HEAP_GRANULARITY_SHIFT;
|
|
|
|
// We'll need to adjust the new allocation size to account for the big block header and then round it up to a page
|
|
AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
|
|
AllocationSize = ROUND_UP_TO_POWER2(AllocationSize, PAGE_SIZE);
|
|
}
|
|
else {// Otherwise the block is in use and is a small allocation
|
|
OldAllocationIndex = BusyBlock->Size;
|
|
OldSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) - BusyBlock->UnusedBytes;
|
|
}
|
|
|
|
AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;// Compute the new allocation index
|
|
|
|
// At this point we have the old size and index, and the new size and index
|
|
|
|
// See if new size less than or equal to the current size.
|
|
if (AllocationIndex <= OldAllocationIndex) {
|
|
// If the new allocation index is only one less then the current index then make the sizes equal
|
|
if (AllocationIndex + 1 == OldAllocationIndex) {
|
|
AllocationIndex += 1;
|
|
AllocationSize += sizeof(HEAP_ENTRY);
|
|
}
|
|
|
|
if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {// Calculate new residual (unused) amount
|
|
BusyBlock->Size = (USHORT)(AllocationSize - Size);// In a big block the size is really the unused byte count
|
|
}
|
|
else if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
// The extra stuff struct goes after the data. So compute the old and new extra stuff location and copy the data
|
|
OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
|
|
NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
|
|
*NewExtraStuff = *OldExtraStuff;
|
|
|
|
if (IS_HEAP_TAGGING_ENABLED()) {// If we're doing heap tagging then update the tag entry
|
|
NewExtraStuff->TagIndex = RtlpUpdateTagEntry(Heap,
|
|
NewExtraStuff->TagIndex,
|
|
OldAllocationIndex,
|
|
AllocationIndex,
|
|
ReAllocationAction);
|
|
}
|
|
|
|
BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
|
|
}
|
|
else {
|
|
if (IS_HEAP_TAGGING_ENABLED()) {// If we're doing heap tagging then update the tag entry
|
|
BusyBlock->SmallTagIndex = (UCHAR)RtlpUpdateTagEntry(Heap,
|
|
BusyBlock->SmallTagIndex,
|
|
BusyBlock->Size,
|
|
AllocationIndex,
|
|
ReAllocationAction);
|
|
}
|
|
|
|
BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
|
|
}
|
|
|
|
// Check if the block is getting bigger, then fill in the extra space.
|
|
|
|
// **** how can this happen if the allocation index is less than or equal to the old allocation index
|
|
if (Size > OldSize) {
|
|
if (Flags & HEAP_ZERO_MEMORY) {// See if we should zero the extra space
|
|
RtlZeroMemory((PCHAR)BaseAddress + OldSize, Size - OldSize);
|
|
}
|
|
else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {// Otherwise see if we should fill the extra space
|
|
SIZE_T PartialBytes, ExtraSize;
|
|
PartialBytes = OldSize & (sizeof(ULONG) - 1);
|
|
if (PartialBytes) {
|
|
PartialBytes = 4 - PartialBytes;
|
|
}
|
|
|
|
if (Size > (OldSize + PartialBytes)) {
|
|
ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof(ULONG) - 1);
|
|
if (ExtraSize != 0) {
|
|
RtlFillMemoryUlong((PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
|
|
ExtraSize,
|
|
ALLOC_HEAP_FILL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
|
|
RtlFillMemory((PCHAR)(BusyBlock + 1) + Size, CHECK_HEAP_TAIL_SIZE, CHECK_HEAP_TAIL_FILL);
|
|
}
|
|
|
|
// If amount of change is greater than the size of a free block, then need to free the extra space.
|
|
// Otherwise, nothing else to do.
|
|
if (AllocationIndex != OldAllocationIndex) {
|
|
FreeFlags = BusyBlock->Flags & ~HEAP_ENTRY_BUSY;
|
|
if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC) {
|
|
PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
|
|
VirtualAllocBlock = CONTAINING_RECORD(BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
|
|
if (IS_HEAP_TAGGING_ENABLED()) {
|
|
VirtualAllocBlock->ExtraStuff.TagIndex =
|
|
RtlpUpdateTagEntry(Heap,
|
|
VirtualAllocBlock->ExtraStuff.TagIndex,
|
|
OldAllocationIndex,
|
|
AllocationIndex,
|
|
VirtualReAllocationAction);
|
|
}
|
|
|
|
DeCommitAddress = (PCHAR)VirtualAllocBlock + AllocationSize;
|
|
DeCommitSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) - AllocationSize;
|
|
Status = ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&DeCommitAddress, &DeCommitSize, MEM_RELEASE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
HeapDebugPrint(("Unable to release memory at %p for %p bytes - Status == %x\n", DeCommitAddress, DeCommitSize, Status));
|
|
HeapDebugBreak(NULL);
|
|
}
|
|
else {
|
|
VirtualAllocBlock->CommitSize -= DeCommitSize;
|
|
}
|
|
}
|
|
else {// Otherwise, shrink size of this block to new size, and make extra space at end free.
|
|
SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
|
|
SplitBlock->Flags = FreeFlags;
|
|
SplitBlock->PreviousSize = (USHORT)AllocationIndex;
|
|
SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
|
|
|
|
FreeSize = BusyBlock->Size - AllocationIndex;
|
|
BusyBlock->Size = (USHORT)AllocationIndex;
|
|
BusyBlock->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
|
|
|
|
// If the following block is uncommitted then we only need to add this new entry to its free list
|
|
if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[SplitBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
|
|
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {
|
|
// Otherwise get the next block and check if it is busy. If it is in use then add this new entry to its free list
|
|
SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
|
|
if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {
|
|
// Otherwise the next block is not in use so we should be able to merge with it.
|
|
// Remove the second free block and if the combined size is
|
|
// still okay then merge the two blocks and add the single block back in.
|
|
// Otherwise call a routine that will actually break it apart before insertion.
|
|
SplitBlock->Flags = SplitBlock2->Flags;
|
|
RtlpRemoveFreeBlock(Heap, SplitBlock2);
|
|
Heap->TotalFreeSize -= SplitBlock2->Size;
|
|
FreeSize += SplitBlock2->Size;
|
|
if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
|
|
((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
|
|
}
|
|
else {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[SplitBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
|
|
}
|
|
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {
|
|
RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// At this point the new size is greater than the current size
|
|
|
|
// If the block is a big allocation or we're not able to grow the block in place then we have a lot of work to do
|
|
if ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
|
|
!RtlpGrowBlockInPlace(Heap, Flags, BusyBlock, Size, AllocationIndex))
|
|
{
|
|
// We're growing the block. Allocate a new block with the bigger
|
|
// size, copy the contents of the old block to the new block and then free the old block.
|
|
// Return the address of the new block.
|
|
if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) {
|
|
#if DBG
|
|
// HeapDebugPrint(( "Failing ReAlloc because cant do it inplace.\n" ));
|
|
#endif
|
|
BaseAddress = NULL;
|
|
}
|
|
else {
|
|
Flags &= ~HEAP_TAG_MASK;// Clear the tag bits from the flags
|
|
|
|
// If there is an extra struct present then get the tag
|
|
// index from the extra stuff and augment the flags with the tag index.
|
|
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
Flags &= ~HEAP_SETTABLE_USER_FLAGS;
|
|
Flags |= HEAP_SETTABLE_USER_VALUE | ((BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
|
|
OldExtraStuff = RtlpGetExtraStuffPointer(BusyBlock);
|
|
try {
|
|
if ((OldExtraStuff->TagIndex != 0) &&
|
|
!(OldExtraStuff->TagIndex & HEAP_PSEUDO_TAG_FLAG))
|
|
{
|
|
Flags |= OldExtraStuff->TagIndex << HEAP_TAG_SHIFT;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
BusyBlock->Flags &= ~HEAP_ENTRY_EXTRA_PRESENT;
|
|
}
|
|
}
|
|
else if (BusyBlock->SmallTagIndex != 0) {
|
|
// There is not an extra stuff struct, but block does have a small tag index so now add this small tag to the flags
|
|
Flags |= BusyBlock->SmallTagIndex << HEAP_TAG_SHIFT;
|
|
}
|
|
|
|
// Allocate from the heap space for the reallocation
|
|
NewBaseAddress = RtlAllocateHeap(HeapHandle, Flags & ~HEAP_ZERO_MEMORY, Size);
|
|
if (NewBaseAddress != NULL) {
|
|
// We were able to get the allocation so now back up
|
|
// to the heap block and if the block has an extra stuff struct then copy over the extra stuff
|
|
NewBusyBlock = (PHEAP_ENTRY)NewBaseAddress - 1;
|
|
if (NewBusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
NewExtraStuff = RtlpGetExtraStuffPointer(NewBusyBlock);
|
|
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
OldExtraStuff = RtlpGetExtraStuffPointer(BusyBlock);
|
|
NewExtraStuff->Settable = OldExtraStuff->Settable;
|
|
}
|
|
else {
|
|
RtlZeroMemory(NewExtraStuff, sizeof(*NewExtraStuff));
|
|
}
|
|
}
|
|
|
|
// Copy over the user's data area to the new block
|
|
RtlMoveMemory(NewBaseAddress, BaseAddress, Size < OldSize ? Size : OldSize);
|
|
|
|
// Check if we grew the block and we should zero the remaining part.
|
|
|
|
// **** is this first test always true because we're in the part that grows blocks
|
|
if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) {
|
|
RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize);
|
|
}
|
|
|
|
RtlFreeHeap(HeapHandle, Flags, BaseAddress);// Release the old block
|
|
}
|
|
|
|
BaseAddress = NewBaseAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((BaseAddress == NULL) && (Flags & HEAP_GENERATE_EXCEPTIONS)) {
|
|
// Construct an exception record.
|
|
ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
|
|
ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
|
|
ExceptionRecord.NumberParameters = 1;
|
|
ExceptionRecord.ExceptionFlags = 0;
|
|
ExceptionRecord.ExceptionInformation[0] = AllocationSize;
|
|
RtlRaiseException(&ExceptionRecord);
|
|
}
|
|
} except(GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER) {
|
|
SET_LAST_STATUS(GetExceptionCode());
|
|
BaseAddress = NULL;
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return BaseAddress;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
BOOLEAN RtlGetUserInfoHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
OUT PVOID *UserValue OPTIONAL,
|
|
OUT PULONG UserFlags OPTIONAL)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns to the user the set of user flags and user values for the specified heap entry.
|
|
The user value is set via a set call and the user flags is part of the
|
|
user settable flags used when communicating with the heap package and can also be set via a set call
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being queried
|
|
Flags - Supplies a set of flags to agument those already in the heap
|
|
BaseAddress - Supplies a pointer to the users heap entry being queried
|
|
UserValue - Optionally supplies a pointer to recieve the heap entry value
|
|
UserFlasg - Optionally supplies a pointer to recieve the heap flags
|
|
Return Value:
|
|
BOOLEAN - TRUE if the query is successful and FALSE otherwise
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PHEAP_ENTRY BusyBlock;
|
|
PHEAP_ENTRY_EXTRA ExtraStuff;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
BOOLEAN Result;
|
|
|
|
Flags |= Heap->ForceFlags;// Build up a set of real flags to use in this operation
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check if we should be going the debug route
|
|
return RtlDebugGetUserInfoHeap(HeapHandle, Flags, BaseAddress, UserValue, UserFlags);
|
|
}
|
|
|
|
Result = FALSE;
|
|
|
|
try {
|
|
try {
|
|
// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;// Backup the pointer to the heap entry
|
|
|
|
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {// If the entry is not in use then it is an error
|
|
SET_LAST_STATUS(STATUS_INVALID_PARAMETER);
|
|
}
|
|
else {// The heap entry is in use so now check if there is any extra information present
|
|
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
// Get a pointer to the extra information and if the user asked for user values then that field from the extra stuff
|
|
ExtraStuff = RtlpGetExtraStuffPointer(BusyBlock);
|
|
if (ARGUMENT_PRESENT(UserValue)) {
|
|
*UserValue = (PVOID)ExtraStuff->Settable;
|
|
}
|
|
}
|
|
|
|
// If the user asked for user flags then return the flags from the heap entry that are user setable
|
|
if (ARGUMENT_PRESENT(UserFlags)) {
|
|
*UserFlags = (BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
|
|
}
|
|
|
|
Result = TRUE;// Now that the assignments are done we can say that we were successful
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SET_LAST_STATUS(GetExceptionCode());
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Result;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
BOOLEAN RtlSetUserValueHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN PVOID UserValue)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to set the user settable value for a heap entry
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of flags needed to augment those already enforced by the heap
|
|
BaseAddress - Supplies a pointer to the heap entry allocation being modified
|
|
UserValue - Supplies the value to store in the extra stuff space of the heap entry
|
|
Return Value:
|
|
BOOLEAN - TRUE if the setting worked, and FALSE otherwise. It could be
|
|
FALSE if the base address is invalid, or if there is not room for the extra stuff
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PHEAP_ENTRY BusyBlock;
|
|
PHEAP_ENTRY_EXTRA ExtraStuff;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
BOOLEAN Result;
|
|
|
|
Flags |= Heap->ForceFlags;// Augment the set of flags
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check to see if we should be going the debug route
|
|
return RtlDebugSetUserValueHeap(HeapHandle, Flags, BaseAddress, UserValue);
|
|
}
|
|
|
|
Result = FALSE;
|
|
|
|
try {
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);// Lock the heap
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;// Get a pointer to the owning heap entry
|
|
|
|
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {// If the entry is not in use then its is an error
|
|
SET_LAST_STATUS(STATUS_INVALID_PARAMETER);
|
|
}
|
|
else if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {//Otherwise we only can set the value if the entry has space for the extra stuff
|
|
ExtraStuff = RtlpGetExtraStuffPointer(BusyBlock);
|
|
ExtraStuff->Settable = (ULONG_PTR)UserValue;
|
|
Result = TRUE;
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Result;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
BOOLEAN RtlSetUserFlagsHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN ULONG UserFlagsReset,
|
|
IN ULONG UserFlagsSet)
|
|
/*++
|
|
Routine Description:
|
|
HeapHandle - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of flags needed to augment those already enforced by the heap
|
|
BaseAddress - Supplies a pointer to the heap entry allocation being modified
|
|
UserFlagsReset - Supplies a mask of flags that the user wants cleared
|
|
UserFlagsSet- Supplies a mask of flags that the user wants set
|
|
Return Value:
|
|
BOOLEAN - TRUE if the operation is a success and FALSE otherwise
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PHEAP_ENTRY BusyBlock;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
BOOLEAN Result = FALSE;
|
|
|
|
Flags |= Heap->ForceFlags;// Augment the set of flags
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check to see if we should be going the debug route
|
|
return RtlDebugSetUserFlagsHeap(HeapHandle,
|
|
Flags,
|
|
BaseAddress,
|
|
UserFlagsReset,
|
|
UserFlagsSet);
|
|
}
|
|
|
|
try {
|
|
// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
try {
|
|
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;// Get a pointer to the owning heap entry
|
|
|
|
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {// If the entry is not in use then it is an error
|
|
SET_LAST_STATUS(STATUS_INVALID_PARAMETER);
|
|
}
|
|
else {// Otherwise modify the flags in the block
|
|
// **** this is terrible error prone if the user passes in flags that aren't 0x200 0x400 or 0x800 only.
|
|
BusyBlock->Flags &= ~(UserFlagsReset >> 4);
|
|
BusyBlock->Flags |= (UserFlagsSet >> 4);
|
|
Result = TRUE;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SET_LAST_STATUS(GetExceptionCode());
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
ULONG RtlCreateTagHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PWSTR TagPrefix OPTIONAL,
|
|
IN PWSTR TagNames)
|
|
/*++
|
|
Routine Description:
|
|
This routine create a tag heap for either the specified heap or for the global tag heap.
|
|
Arguments:
|
|
HeapHandle - Optionally supplies a pointer to the heap that we want modified. If null then the global tag heap is used
|
|
Flags - Supplies a list of flags to augment the flags already enforced by the heap
|
|
TagPrefix - Optionally supplies a null terminated wchar string of a prefix to add to each tag
|
|
TagNames - Supplies a list of tag names separated by null and terminated
|
|
by a double null. If the first name in the list start with
|
|
a "!" then it is interpreted as the heap name. The syntax for the tag name is
|
|
|
|
[!<heapname> nul ] {<tagname> nul}* nul
|
|
|
|
Return Value:
|
|
ULONG - returns the index of the last tag create shifted to the high order word.
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
ULONG TagIndex;
|
|
ULONG NumberOfTags, MaxTagNameLength, TagPrefixLength;
|
|
PWSTR s, s1, HeapName;
|
|
PHEAP_TAG_ENTRY TagEntry;
|
|
ULONG Result;
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {// Check if tagging is disable and so this call is a noop
|
|
return 0;
|
|
}
|
|
|
|
// If the processes global tag heap has not been created yet then allocate a global tag heap
|
|
if (RtlpGlobalTagHeap == NULL) {
|
|
RtlpGlobalTagHeap = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEAP));
|
|
if (RtlpGlobalTagHeap == NULL) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// If the user passed in a heap then we'll use the lock from that heap to synchronize our work.
|
|
// Otherwise we're unsynchronized
|
|
if (Heap != NULL) {
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, 0);// Tagging is not part of the guard page heap package
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check if we should be calling the debug version of the heap package
|
|
Result = RtlDebugCreateTagHeap(HeapHandle, Flags, TagPrefix, TagNames);
|
|
leave;
|
|
}
|
|
|
|
// Augment the flags and lock the specified heap
|
|
Flags |= Heap->ForceFlags;
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
}
|
|
|
|
// We start off with zero tags
|
|
TagIndex = 0;
|
|
NumberOfTags = 0;
|
|
|
|
// With tag names that start with "!" we assume what follows is a heap name.
|
|
if (*TagNames == L'!') {
|
|
HeapName = TagNames + 1;
|
|
|
|
// Move up to the following tag name after the heap name separated by a null
|
|
while (*TagNames++) { NOTHING; }
|
|
}
|
|
else {
|
|
HeapName = NULL;
|
|
}
|
|
|
|
s = TagNames;// Gobble up each tag name keeping count of how many we find
|
|
|
|
while (*s) {
|
|
while (*s++) { NOTHING; }
|
|
NumberOfTags += 1;
|
|
}
|
|
|
|
// Now we will only continue on if we were supplied tag names
|
|
if (NumberOfTags > 0) {
|
|
// Allocate heap entries for the number of tags we need and only proceed if this allocation succeeded.
|
|
// The following call also makes room for the heap name as tag index 0.
|
|
// Note that is heap is null then we assume we're using the global tag heap
|
|
|
|
TagEntry = RtlpAllocateTags(Heap, NumberOfTags);
|
|
if (TagEntry != NULL) {
|
|
MaxTagNameLength = (sizeof(TagEntry->TagName) / sizeof(WCHAR)) - 1;
|
|
TagIndex = TagEntry->TagIndex;
|
|
|
|
if (TagIndex == 0) {// If the first tag index is zero then we'll make this tag entry the heap name.
|
|
if (HeapName != NULL) {
|
|
// Copy over the heap name and pad it out with nulls up to the end of the name buffer
|
|
wcsncpy(TagEntry->TagName, HeapName, MaxTagNameLength);
|
|
}
|
|
|
|
// Whether we add a heap name or not we'll move on to the next tag entry and index
|
|
TagEntry += 1;
|
|
TagIndex = TagEntry->TagIndex;
|
|
|
|
// This isn't the first index for a specified heap, but see if it is the first index for the global heap.
|
|
// If so then put name of the global tags into the 0 index
|
|
}
|
|
else if (TagIndex == HEAP_GLOBAL_TAG) {
|
|
wcsncpy(TagEntry->TagName, L"GlobalTags", MaxTagNameLength);
|
|
TagEntry += 1;
|
|
TagIndex = TagEntry->TagIndex;
|
|
}
|
|
|
|
// Now we've taken case of the 0 index we'll go on to the rest of the tags.
|
|
// If there is tag prefix and it is not zero length then we'll use this tag prefix provided that is leaves us at
|
|
// least 4 characters for the tag name itself.
|
|
// Otherwise we'll ignore the tag prefix (by setting the variable to null).
|
|
if ((ARGUMENT_PRESENT(TagPrefix)) && (TagPrefixLength = wcslen(TagPrefix))) {
|
|
if (TagPrefixLength >= MaxTagNameLength - 4) {
|
|
TagPrefix = NULL;
|
|
}
|
|
else {
|
|
MaxTagNameLength -= TagPrefixLength;
|
|
}
|
|
}
|
|
else {
|
|
TagPrefix = NULL;
|
|
}
|
|
|
|
// For every tag name (note that this varable has already been
|
|
// advanced beyond the heap name) we'll put it in a tag entry
|
|
// by copying in the prefix and then appending on the tag itself
|
|
|
|
// s points to the current users supplied tag name
|
|
// s1 points to the tag name buffer in the current tag entry
|
|
|
|
s = TagNames;
|
|
while (*s) {
|
|
s1 = TagEntry->TagName;
|
|
|
|
// Copy in the optional tag prefix and update s1
|
|
if (ARGUMENT_PRESENT(TagPrefix)) {
|
|
wcscpy(s1, TagPrefix);
|
|
s1 += TagPrefixLength;
|
|
}
|
|
|
|
// Copy over the remaining tag name padding it with nulls
|
|
// up to the end of the name buffer
|
|
wcsncpy(s1, s, MaxTagNameLength);
|
|
|
|
// Skip to the next tag name
|
|
while (*s++)
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
TagEntry += 1;// Skip to the next tag entry
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = TagIndex << HEAP_TAG_SHIFT;
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
// And return to our caller. The answer we return is the last tag index
|
|
// stored in the high word of a ulong result
|
|
return Result;
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
PWSTR RtlQueryTagHeap(IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN USHORT TagIndex,
|
|
IN BOOLEAN ResetCounters,
|
|
OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the name and optional statistics for a given tag index.
|
|
Arguments:
|
|
**** note that some of the code looks like it can handle the
|
|
**** global tag heap but other places look rather wrong
|
|
|
|
HeapHandle - Specifies the heap being queried. If null then the global tag heap is used.
|
|
Flags - Supplies a set flags to augment those enforced by the heap
|
|
TagIndex - Specifies the tag index that we want to query
|
|
ResetCounter - Specifies if this routine should reset the counter for the tag after the query
|
|
TagInfo - Optionally supplies storage where the output tag information should be stored
|
|
Return Value:
|
|
PWSTR - Returns a pointer to the tag name or NULL if the index doesn't exist
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
PHEAP_TAG_ENTRY TagEntry;
|
|
PWSTR Result;
|
|
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, NULL);// Tagging is not part of the guard page heap package
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {// Check if tagging is disabled
|
|
return NULL;
|
|
}
|
|
|
|
try {
|
|
if (Heap != NULL) {// Check if the caller has given us a heap to query
|
|
if (DEBUG_HEAP(Flags)) {// Check if we should be using the debug version of the heap package
|
|
Result = RtlDebugQueryTagHeap(HeapHandle, Flags, TagIndex, ResetCounters, TagInfo);
|
|
leave;
|
|
}
|
|
|
|
Flags |= Heap->ForceFlags;// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
}
|
|
|
|
Result = NULL;
|
|
|
|
// **** note that the next test assumes that heap is not null
|
|
|
|
// Check that the specified tag index is valid and that the this heap does actually have some tag entries
|
|
if ((TagIndex < Heap->NextAvailableTagIndex) && (Heap->TagEntries != NULL)) {
|
|
// Stride over to the specific tag entry and
|
|
// if the caller gave us an output buffer then fill in the details
|
|
TagEntry = Heap->TagEntries + TagIndex;
|
|
|
|
if (ARGUMENT_PRESENT(TagInfo)) {
|
|
TagInfo->NumberOfAllocations = TagEntry->Allocs;
|
|
TagInfo->NumberOfFrees = TagEntry->Frees;
|
|
TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
|
|
}
|
|
|
|
if (ResetCounters) {// Check if we should reset the counters
|
|
TagEntry->Allocs = 0;
|
|
TagEntry->Frees = 0;
|
|
TagEntry->Size = 0;
|
|
}
|
|
|
|
Result = &TagEntry->TagName[0];// Point to the tag name
|
|
|
|
// If the tag index has the psuedo tag bit set then recalulate the
|
|
// tag index and if this heap has pseudo tags than that is what we'll return
|
|
}
|
|
else if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
|
|
TagIndex ^= HEAP_PSEUDO_TAG_FLAG;// Clear the bit
|
|
|
|
if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) && (Heap->PseudoTagEntries != NULL)) {
|
|
// Stride over to the specific pseudo tag entry and if the
|
|
// caller gave us an output buffer then fill in the details
|
|
TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
|
|
if (ARGUMENT_PRESENT(TagInfo)) {
|
|
TagInfo->NumberOfAllocations = TagEntry->Allocs;
|
|
TagInfo->NumberOfFrees = TagEntry->Frees;
|
|
TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
|
|
}
|
|
|
|
if (ResetCounters) {// Check if we should reset the counters
|
|
TagEntry->Allocs = 0;
|
|
TagEntry->Frees = 0;
|
|
TagEntry->Size = 0;
|
|
}
|
|
|
|
Result = L"";// Pseudo tags do not have names
|
|
}
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Result;// And return the tag name to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
NTSTATUS RtlExtendHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Base, IN SIZE_T Size)
|
|
/*++
|
|
Routine Description:
|
|
This routine grows the specified heap by adding a new segment to its storage.
|
|
The memory for the segment is supplied by the caller.
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of flags used to augment those already enforced by the heap
|
|
Base - Supplies the starting address for the new segment being added to the input heap
|
|
Size - Supplies the size, in bytes, of the new segment. Note that this
|
|
routine will actually use more memory than specified by this
|
|
variable. It will use whatever is committed and reserved provided the amount is greater than or equal to "Size"
|
|
Return Value:
|
|
NTSTATUS - An appropriate status value
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
NTSTATUS Status;
|
|
PHEAP_SEGMENT Segment;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
UCHAR SegmentIndex, EmptySegmentIndex;
|
|
SIZE_T CommitSize;
|
|
SIZE_T ReserveSize;
|
|
ULONG SegmentFlags;
|
|
PVOID CommittedBase;
|
|
PVOID UnCommittedBase;
|
|
MEMORY_BASIC_INFORMATION MemoryInformation;
|
|
|
|
// Check if the guard page version of heap can do the work
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, RtlpDebugPageHeapExtend(HeapHandle, Flags, Base, Size));
|
|
|
|
// See what Mm thinks about the base address we were passed in.
|
|
// The address must not be free.
|
|
Status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
Base,
|
|
MemoryBasicInformation,
|
|
&MemoryInformation,
|
|
sizeof(MemoryInformation),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (MemoryInformation.State == MEM_FREE) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// If what we were passed in as a base address is not on a page boundary then
|
|
// adjust the information supplied by MM to the page boundary right after the input base address
|
|
if (MemoryInformation.BaseAddress != Base) {
|
|
MemoryInformation.BaseAddress = (PCHAR)MemoryInformation.BaseAddress + PAGE_SIZE;
|
|
MemoryInformation.RegionSize -= PAGE_SIZE;
|
|
}
|
|
|
|
try {
|
|
// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
// Scan the heap's segment list for a free segment.
|
|
// And make sure the address of all the segment does not contain the input base address
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
EmptySegmentIndex = HEAP_MAXIMUM_SEGMENTS;
|
|
|
|
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++)
|
|
{
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
|
|
if (Segment) {
|
|
if (((ULONG_PTR)Base >= (ULONG_PTR)Segment) &&
|
|
((ULONG_PTR)Base < (ULONG_PTR)(Segment->LastValidEntry)))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
else if ((Segment == NULL) && (EmptySegmentIndex == HEAP_MAXIMUM_SEGMENTS)) {
|
|
EmptySegmentIndex = SegmentIndex;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// At this point if status is success then the empty segment index
|
|
// is available for us to use and base address doesn't overlap an existing segment.
|
|
if (NT_SUCCESS(Status)) {
|
|
// Indicate that this segment is user supplied
|
|
SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
|
|
CommittedBase = MemoryInformation.BaseAddress;
|
|
|
|
// If the start of the memory supplied by the use is already committed then check the state of the following uncommitted piece of memory to see if it is reserved
|
|
if (MemoryInformation.State == MEM_COMMIT) {
|
|
CommitSize = MemoryInformation.RegionSize;
|
|
UnCommittedBase = (PCHAR)CommittedBase + CommitSize;
|
|
Status = NtQueryVirtualMemory(NtCurrentProcess(),
|
|
UnCommittedBase,
|
|
MemoryBasicInformation,
|
|
&MemoryInformation,
|
|
sizeof(MemoryInformation),
|
|
NULL);
|
|
ReserveSize = CommitSize;
|
|
if ((NT_SUCCESS(Status)) && (MemoryInformation.State == MEM_RESERVE)) {
|
|
ReserveSize += MemoryInformation.RegionSize;
|
|
}
|
|
}
|
|
else {
|
|
// Otherwise the user hasn't committed anything in the
|
|
// the address they gave us and we know it is not free
|
|
// so it must be reserved.
|
|
|
|
UnCommittedBase = CommittedBase;
|
|
ReserveSize = MemoryInformation.RegionSize;
|
|
}
|
|
|
|
// Now if the reserved size is smaller than a page size or
|
|
// the user specified size is greater than the reserved size
|
|
// then the buffer we're given is too small to be a segment of heap
|
|
if ((ReserveSize < PAGE_SIZE) || (Size > ReserveSize)) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else {
|
|
// Otherwise the size is okay, now check if we need
|
|
// to do the commit of the base. If so we'll commit one page
|
|
if (UnCommittedBase == CommittedBase) {
|
|
CommitSize = PAGE_SIZE;
|
|
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&Segment,
|
|
0,
|
|
&CommitSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
}
|
|
}
|
|
|
|
// At this point the if status is good then memory is all set up
|
|
// with at least one page of committed memory to start with.
|
|
// So initialize the heap segment and we're done.
|
|
if (NT_SUCCESS(Status)) {
|
|
if (RtlpInitializeHeapSegment(Heap,
|
|
Segment,
|
|
EmptySegmentIndex,
|
|
0,
|
|
Segment,
|
|
(PCHAR)Segment + CommitSize,
|
|
(PCHAR)Segment + ReserveSize)) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Status;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
SIZE_T NTAPI RtlCompactHeap(IN PVOID HeapHandle, IN ULONG Flags)
|
|
/*++
|
|
Routine Description:
|
|
This routine compacts the specified heap by coalescing all the free block.
|
|
It also determines the size of the largest available free block and returns its, in bytes, back to the caller.
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of flags used to augment those already enforced by the heap
|
|
Return Value:
|
|
SIZE_T - Returns the size, in bytes, of the largest free block available in the heap
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PHEAP_FREE_ENTRY FreeBlock;
|
|
PHEAP_SEGMENT Segment;
|
|
UCHAR SegmentIndex;
|
|
SIZE_T LargestFreeSize;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
|
|
Flags |= Heap->ForceFlags;// Augment the heap flags
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check if this is a debug version of heap
|
|
return RtlDebugCompactHeap(HeapHandle, Flags);
|
|
}
|
|
|
|
try {
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {// Lock the heap
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
LargestFreeSize = 0;
|
|
|
|
try {
|
|
// Coalesce the heap into its largest free blocks possible and get the largest free block in the heap
|
|
FreeBlock = RtlpCoalesceHeap((PHEAP)HeapHandle);
|
|
|
|
if (FreeBlock != NULL) {// If there is a free block then compute its byte size
|
|
LargestFreeSize = FreeBlock->Size << HEAP_GRANULARITY_SHIFT;
|
|
}
|
|
|
|
// Scan every segment in the heap looking at its largest uncommitted range.
|
|
// Remember the largest range if its bigger than anything we've found so far
|
|
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
if (Segment && Segment->LargestUnCommittedRange > LargestFreeSize) {
|
|
LargestFreeSize = Segment->LargestUnCommittedRange;
|
|
}
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SET_LAST_STATUS(GetExceptionCode());
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return LargestFreeSize;// And return the largest free size to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
BOOLEAN RtlValidateHeap(PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress)
|
|
/*++
|
|
Routine Description:
|
|
This routine verifies the structure of a heap and/or heap block
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being queried
|
|
Flags - Supplies a set of flags used to augment those already enforced by the heap
|
|
BaseAddress - Optionally supplies a pointer to the heap block that should be individually validated
|
|
Return Value:
|
|
BOOLEAN - TRUE if the heap/block is okay and FALSE otherwise
|
|
*/
|
|
{
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
BOOLEAN Result;
|
|
|
|
try {
|
|
try {
|
|
// Check for the guard page version of heap
|
|
if (IS_DEBUG_PAGE_HEAP_HANDLE(HeapHandle)) {
|
|
Result = RtlpDebugPageHeapValidate(HeapHandle, Flags, BaseAddress);
|
|
}
|
|
else {
|
|
// If there is an active lookaside list then drain and remove it.
|
|
// By setting the lookaside field in the heap to null we guarantee that the call the free heap will not try and use the lookaside list logic.
|
|
|
|
// We'll actually capture the lookaside pointer from the heap and only use the captured pointer. This will take care of the
|
|
// condition where another walk or lock heap can cause us to check for a non null pointer and then have it become null when we read
|
|
// it again. If it is non null to start with then even if the user walks or locks the heap via another thread the pointer to
|
|
// still valid here so we can still try and do a lookaside list pop.
|
|
|
|
PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)Heap->Lookaside;
|
|
if (Lookaside != NULL) {
|
|
ULONG i;
|
|
PVOID Block;
|
|
|
|
Heap->Lookaside = NULL;
|
|
|
|
for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
|
|
while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
|
|
RtlFreeHeap(HeapHandle, 0, Block);
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = FALSE;
|
|
|
|
// Validate that HeapAddress points to a HEAP structure.
|
|
if (RtlpCheckHeapSignature(Heap, "RtlValidateHeap")) {
|
|
Flags |= Heap->ForceFlags;
|
|
|
|
// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
// If the user did not supply a base address then verify the complete heap otherwise just do a single heap entry
|
|
if (BaseAddress == NULL) {
|
|
Result = RtlpValidateHeap(Heap, TRUE);
|
|
}
|
|
else {
|
|
Result = RtlpValidateHeapEntry(Heap,
|
|
(PHEAP_ENTRY)BaseAddress - 1,
|
|
"RtlValidateHeap");
|
|
}
|
|
}
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SET_LAST_STATUS(GetExceptionCode());
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Result;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
BOOLEAN RtlValidateProcessHeaps(VOID)
|
|
/*++
|
|
Routine Description:
|
|
This routine cycles through all and validates each heap in the current process.
|
|
Return Value:
|
|
BOOLEAN - TRUE if all the heap verify okay and FALSE for any other reason.
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i, NumberOfHeaps;
|
|
PVOID HeapsArray[512];
|
|
PVOID *Heaps;
|
|
SIZE_T Size;
|
|
BOOLEAN Result;
|
|
|
|
Result = TRUE;
|
|
Heaps = &HeapsArray[0];
|
|
|
|
// By default we can handle 512 heaps per process any more than
|
|
// that and we'll need to allocate storage to do the processing
|
|
NumberOfHeaps = RtlGetProcessHeaps(512, Heaps);// So now determine how many heaps are in the current process
|
|
|
|
// **** this is bogus because the preceeding routine will never return more than 512. Either this routine
|
|
// **** needs to get the heap count from the peb itself or the called routine needs to return the actual
|
|
// **** number of heaps in the process, Then we have to know not to to beyond the heap array size
|
|
if (NumberOfHeaps > 512) {// The number of heaps is greater than 512
|
|
// so allocate extra memory to store the array of heap pointers
|
|
Heaps = NULL;
|
|
Size = NumberOfHeaps * sizeof(PVOID);
|
|
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
|
(PVOID *)&Heaps,
|
|
0,
|
|
&Size,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
// And retry getting the heaps
|
|
NumberOfHeaps = RtlGetProcessHeaps(512, Heaps);// **** this won't work again because it still uses 512
|
|
}
|
|
|
|
// Now for each heap in our heap array we'll validate that heap
|
|
for (i = 0; i < NumberOfHeaps; i++) {
|
|
if (!RtlValidateHeap(Heaps[i], 0, NULL)) {
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
// Check if we need to return the memory that we use for an enlarged heap array
|
|
if (Heaps != &HeapsArray[0]) {
|
|
ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&Heaps, &Size, MEM_RELEASE);
|
|
}
|
|
|
|
return Result;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
ULONG RtlGetProcessHeaps(ULONG NumberOfHeapsToReturn, PVOID *ProcessHeaps)
|
|
/*++
|
|
Routine Description:
|
|
This routine determines how many individual heaps there are in the current process and fills an array with pointers to each heap.
|
|
Arguments:
|
|
NumberOfHeapsToReturn - Indicates how many heaps the caller is willing to accept in the second parameter
|
|
ProcessHeaps - Supplies a pointer to an array of heap pointer to be filled in by this routine.
|
|
The maximum size of this array is specified by the first parameter
|
|
Return Value:
|
|
ULONG - Returns the smaller of the actual number of heaps in the the process or the size of the output buffer
|
|
*/
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
ULONG NumberOfHeapsToCopy;
|
|
ULONG TotalHeaps;
|
|
|
|
RtlAcquireLockRoutine(&RtlpProcessHeapsListLock.Lock);
|
|
try {
|
|
TotalHeaps = Peb->NumberOfHeaps;// Return no more than the number of heaps currently in use
|
|
if (TotalHeaps > NumberOfHeapsToReturn) {
|
|
NumberOfHeapsToCopy = NumberOfHeapsToReturn;
|
|
}
|
|
else {
|
|
NumberOfHeapsToCopy = TotalHeaps;
|
|
}
|
|
|
|
// Return the heap pointers to the caller
|
|
RtlMoveMemory(ProcessHeaps, Peb->ProcessHeaps, NumberOfHeapsToCopy * sizeof(*ProcessHeaps));
|
|
|
|
ProcessHeaps += NumberOfHeapsToCopy;
|
|
NumberOfHeapsToReturn -= NumberOfHeapsToCopy;
|
|
}
|
|
finally{
|
|
RtlReleaseLockRoutine(&RtlpProcessHeapsListLock.Lock);
|
|
}
|
|
|
|
#ifdef DEBUG_PAGE_HEAP
|
|
if (RtlpDebugPageHeap) {// If we have debugging page heaps, go return what we can from them
|
|
TotalHeaps += RtlpDebugPageHeapGetProcessHeaps(NumberOfHeapsToReturn, ProcessHeaps);
|
|
}
|
|
#endif
|
|
|
|
return TotalHeaps;
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
NTSTATUS RtlEnumProcessHeaps(PRTL_ENUM_HEAPS_ROUTINE EnumRoutine, PVOID Parameter)
|
|
/*++
|
|
Routine Description:
|
|
This routine cycles through all the heaps in a process and invokes the specified call back routine for that heap
|
|
Arguments:
|
|
EnumRoutine - Supplies the callback to invoke for each heap in the process
|
|
Parameter - Provides an additional parameter to pass to the callback routine
|
|
Return Value:
|
|
NTSTATUS - returns success or the first error status returned by the callback routine
|
|
*/
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
RtlAcquireLockRoutine(&RtlpProcessHeapsListLock.Lock);// Lock the heap
|
|
try {
|
|
// For each heap in the process invoke the callback routine and if the callback returns anything other than success
|
|
// then break out and return immediately to our caller
|
|
for (i = 0; i < Peb->NumberOfHeaps; i++) {
|
|
Status = (*EnumRoutine)((PHEAP)(Peb->ProcessHeaps[i]), Parameter);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
finally{
|
|
RtlReleaseLockRoutine(&RtlpProcessHeapsListLock.Lock);// Unlock the heap
|
|
}
|
|
|
|
return Status;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
NTSTATUS RtlUsageHeap(IN PVOID HeapHandle, IN ULONG Flags, IN OUT PRTL_HEAP_USAGE Usage)
|
|
/*++
|
|
Routine Description:
|
|
This is a rather bizzare routine.
|
|
It models heap usage in that it returns to the caller the various heap sizes, but it also return three lists.
|
|
One is a list of entries for each active allocation in the heap.
|
|
The next two are used for tracking difference between usage calls.
|
|
There is a list of what was added and a list of what was removed.
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being queried
|
|
Flags - Supplies a set of flags needed to augment those enforced by the heap.
|
|
HEAP_USAGE_ALLOCATED_BLOCKS - Denotes that the calls wants the list of allocated entries.
|
|
HEAP_USAGE_FREE_BUFFER - Denotes the last call to this procedure and that any temporary storage can now be freed
|
|
Usage - Receives the current usage statistics for the heap.
|
|
This variable is also used to store state information between calls to this routine.
|
|
Return Value:
|
|
NTSTATUS - An appropriate status value. STATUS_SUCCESS if the heap has
|
|
not changed at all between calls and STATUS_MORE_ENTRIES if thep changed between two calls.
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PRTL_HEAP_USAGE_INTERNAL Buffer;
|
|
PHEAP_SEGMENT Segment;
|
|
PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
|
|
PHEAP_ENTRY CurrentBlock;
|
|
PHEAP_ENTRY_EXTRA ExtraStuff;
|
|
PLIST_ENTRY Head, Next;
|
|
PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
|
|
SIZE_T BytesFree;
|
|
UCHAR SegmentIndex;
|
|
BOOLEAN LockAcquired = FALSE;
|
|
BOOLEAN VirtualAllocBlockSeen;
|
|
PRTL_HEAP_USAGE_ENTRY pOldEntries, pNewEntries, pNewEntry;
|
|
PRTL_HEAP_USAGE_ENTRY *ppEntries, *ppAddedEntries, *ppRemovedEntries, *pp;
|
|
PVOID DataAddress;
|
|
SIZE_T DataSize;
|
|
|
|
Flags |= Heap->ForceFlags;// Augment the heap flags
|
|
|
|
if (DEBUG_HEAP(Flags)) {// Check if we should be using the debug version of heap
|
|
return RtlDebugUsageHeap(HeapHandle, Flags, Usage);
|
|
}
|
|
|
|
// Make sure that the size of the input buffer is correct
|
|
if (Usage->Length != sizeof(RTL_HEAP_USAGE)) {
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
// Zero out the output fields
|
|
Usage->BytesAllocated = 0;
|
|
Usage->BytesCommitted = 0;
|
|
Usage->BytesReserved = 0;
|
|
Usage->BytesReservedMaximum = 0;
|
|
|
|
// Use the reserved area of the output buffer as an internal
|
|
// heap usage storage space between calls
|
|
Buffer = (PRTL_HEAP_USAGE_INTERNAL)&Usage->Reserved[0];
|
|
|
|
// Check if there is not a base buffer and we should allocate one then do so now
|
|
if ((Buffer->Base == NULL) && (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
|
|
Buffer->ReservedSize = 4 * 1024 * 1024;
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&Buffer->Base,
|
|
0,
|
|
&Buffer->ReservedSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Buffer->CommittedSize = 0;
|
|
Buffer->FreeList = NULL;
|
|
Buffer->LargeEntriesSentinal = NULL;
|
|
}
|
|
else if ((Buffer->Base != NULL) && (Flags & HEAP_USAGE_FREE_BUFFER)) {// Otherwise check if there already is a base buffer and we should free it now
|
|
Buffer->ReservedSize = 0;
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&Buffer->Base,
|
|
&Buffer->ReservedSize,
|
|
MEM_RELEASE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RtlZeroMemory(Buffer, sizeof(*Buffer));
|
|
}
|
|
|
|
// **** Augment the heap flags again
|
|
|
|
Flags |= Heap->ForceFlags;
|
|
|
|
try {
|
|
// Lock the heap
|
|
if (!(Flags & HEAP_NO_SERIALIZE)) {
|
|
RtlAcquireLockRoutine(Heap->LockVariable);
|
|
LockAcquired = TRUE;
|
|
}
|
|
|
|
// Scan through the heap segments and for every in-use segment
|
|
// we add it to the amount of committed and reserved bytes
|
|
// If the segment is not in use and the heap is growable then
|
|
// we just add it to the reserved maximum
|
|
|
|
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
if (Segment) {
|
|
Usage->BytesCommitted += (Segment->NumberOfPages - Segment->NumberOfUnCommittedPages) * PAGE_SIZE;
|
|
Usage->BytesReserved += Segment->NumberOfPages * PAGE_SIZE;
|
|
}
|
|
else if (Heap->Flags & HEAP_GROWABLE) {
|
|
Usage->BytesReservedMaximum += Heap->SegmentReserve;
|
|
}
|
|
}
|
|
|
|
Usage->BytesReservedMaximum += Usage->BytesReserved;
|
|
Usage->BytesAllocated = Usage->BytesCommitted - (Heap->TotalFreeSize << HEAP_GRANULARITY_SHIFT);
|
|
|
|
// Scan through the big allocations and add those amounts to the usage statistics
|
|
|
|
Head = &Heap->VirtualAllocdBlocks;
|
|
Next = Head->Flink;
|
|
|
|
while (Head != Next) {
|
|
VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
|
|
|
|
Usage->BytesAllocated += VirtualAllocBlock->CommitSize;
|
|
Usage->BytesCommitted += VirtualAllocBlock->CommitSize;
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Now check if we have a base buffer and we are suppose to account for allocated blocks
|
|
if ((Buffer->Base != NULL) && (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
|
|
// Setup a pointer to the old entries, added entries, and removed
|
|
// entries in the usage struct. Also drain the added entries and removed entries list
|
|
|
|
pOldEntries = Usage->Entries;
|
|
ppEntries = &Usage->Entries;
|
|
*ppEntries = NULL;
|
|
ppAddedEntries = &Usage->AddedEntries;
|
|
|
|
while (*ppAddedEntries = RtlpFreeHeapUsageEntry(Buffer, *ppAddedEntries))
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
ppRemovedEntries = &Usage->RemovedEntries;
|
|
|
|
while (*ppRemovedEntries = RtlpFreeHeapUsageEntry(Buffer, *ppRemovedEntries))
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
// The way the code works is that ppEntries, ppAddedEntries, and
|
|
// ppRemovedEntries point to the tail of their respective lists. If
|
|
// the list is empty then they point to the head.
|
|
|
|
// Process every segment in the heap
|
|
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
|
|
// Only deal with segments that are in use
|
|
if (Segment) {
|
|
// The current block is really the first block in current segment.
|
|
// We need to special case the computation to account for the first heap segment.
|
|
|
|
if (Segment->BaseAddress == Heap) {
|
|
CurrentBlock = &Heap->Entry;
|
|
}
|
|
else {
|
|
CurrentBlock = &Segment->Entry;
|
|
}
|
|
|
|
// Now for every busy block in the segment we'll check if
|
|
// we need to allocate a heap usage entry and put it in the the entries list
|
|
while (CurrentBlock < Segment->LastValidEntry) {
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
|
|
// Compute the users data address and size
|
|
DataAddress = (CurrentBlock + 1);
|
|
DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) - CurrentBlock->UnusedBytes;
|
|
|
|
keepLookingAtOldEntries:
|
|
// The first time through this routine will have
|
|
// both of these variables null so we'll start off
|
|
// by looking at new entries.
|
|
if (pOldEntries == Buffer->LargeEntriesSentinal) {
|
|
goto keepLookingAtNewEntries;
|
|
}
|
|
|
|
// Check if this entry hasn't changed.
|
|
|
|
// If the old entry is equal to this data block
|
|
// then move the old entry back to the entries
|
|
// list and go on to the next block.
|
|
if ((pOldEntries->Address == DataAddress) && (pOldEntries->Size == DataSize)) {
|
|
// Same block, keep in entries list
|
|
*ppEntries = pOldEntries;
|
|
pOldEntries = pOldEntries->Next;
|
|
ppEntries = &(*ppEntries)->Next;
|
|
|
|
*ppEntries = NULL;
|
|
|
|
// Check if an entry was removed
|
|
|
|
// If this entry is beyond the old entry then move
|
|
// the old entry to the removed entry list and keep
|
|
// looking at the old entry list without advancing the current data block
|
|
}
|
|
else if (pOldEntries->Address <= DataAddress) {
|
|
*ppRemovedEntries = pOldEntries;
|
|
pOldEntries = pOldEntries->Next;
|
|
ppRemovedEntries = &(*ppRemovedEntries)->Next;
|
|
*ppRemovedEntries = NULL;
|
|
goto keepLookingAtOldEntries;
|
|
}
|
|
else {// Otherwise the we want to process the current data block
|
|
keepLookingAtNewEntries:
|
|
pNewEntry = NULL;// Allocate a new heap usage entry
|
|
|
|
Status = RtlpAllocateHeapUsageEntry(Buffer, &pNewEntry);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
// And fill in the new entry
|
|
pNewEntry->Address = DataAddress;
|
|
pNewEntry->Size = DataSize;
|
|
|
|
// If there is an extra stuff struct then fill it in
|
|
// with the stack backtrace, and appropriate tag index
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
ExtraStuff = RtlpGetExtraStuffPointer(CurrentBlock);
|
|
|
|
#if i386
|
|
pNewEntry->AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
|
|
#endif // i386
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {
|
|
pNewEntry->TagIndex = 0;
|
|
}
|
|
else {
|
|
pNewEntry->TagIndex = ExtraStuff->TagIndex;
|
|
}
|
|
}
|
|
else {
|
|
// Otherwise there is no extra stuff so there is
|
|
// no backtrace and the tag is from the small index
|
|
|
|
#if i386
|
|
pNewEntry->AllocatorBackTraceIndex = 0;
|
|
#endif // i386
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {
|
|
pNewEntry->TagIndex = 0;
|
|
}
|
|
else {
|
|
pNewEntry->TagIndex = CurrentBlock->SmallTagIndex;
|
|
}
|
|
}
|
|
|
|
// Allocate another new heap usage entry as part of the added entry list
|
|
Status = RtlpAllocateHeapUsageEntry(Buffer, ppAddedEntries);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
**ppAddedEntries = *pNewEntry;// Copy over the contents of the new entry to the added entry
|
|
|
|
// Advance the added entry pointer to the next slot
|
|
ppAddedEntries = &((*ppAddedEntries)->Next);
|
|
*ppAddedEntries = NULL;
|
|
pNewEntry->Next = NULL;
|
|
|
|
// Add the new entry to the entries list
|
|
*ppEntries = pNewEntry;
|
|
ppEntries = &pNewEntry->Next;
|
|
}
|
|
}
|
|
|
|
// Now advance to the next block in the segment
|
|
|
|
// If the next block doesn't exist then zoom through the
|
|
// uncommitted ranges in the segment until we find a
|
|
// match and can recompute the next real block
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
|
|
CurrentBlock += CurrentBlock->Size;
|
|
if (CurrentBlock < Segment->LastValidEntry)
|
|
{
|
|
UnCommittedRange = Segment->UnCommittedRanges;
|
|
|
|
while ((UnCommittedRange != NULL) &&
|
|
(UnCommittedRange->Address != (ULONG_PTR)CurrentBlock))
|
|
{
|
|
UnCommittedRange = UnCommittedRange->Next;
|
|
}
|
|
|
|
if (UnCommittedRange == NULL) {
|
|
CurrentBlock = Segment->LastValidEntry;
|
|
}
|
|
else {
|
|
CurrentBlock = (PHEAP_ENTRY)(UnCommittedRange->Address + UnCommittedRange->Size);
|
|
}
|
|
}
|
|
}
|
|
else {// Otherwise the next block exists and so point directly at it
|
|
CurrentBlock += CurrentBlock->Size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point we've scanned through every segment in the heap
|
|
|
|
// The first time through we now have two lists one of entries and
|
|
// another of added entries. In each case Usage->Entries, and
|
|
// Usage->AddedEntries points to the start of the list and ppEntries,
|
|
// and ppAddedEntries points to the tail of the list. The first
|
|
// time through we has seem to have a one-to-one correspondence
|
|
// between Entries and AddedEntries, but the AddedEntries records do not contain anything useful
|
|
if (NT_SUCCESS(Status)) {
|
|
// Now we'll examine each big allocation, and for each big allocation
|
|
// we'll make a heap usage entry
|
|
|
|
Head = &Heap->VirtualAllocdBlocks;
|
|
Next = Head->Flink;
|
|
VirtualAllocBlockSeen = FALSE;
|
|
|
|
while (Head != Next) {
|
|
VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
|
|
|
|
// Allocate a new heap usage entry
|
|
pNewEntry = NULL;
|
|
Status = RtlpAllocateHeapUsageEntry(Buffer, &pNewEntry);
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
break;
|
|
}
|
|
|
|
VirtualAllocBlockSeen = TRUE;
|
|
|
|
// Fill in the new heap usage entry
|
|
pNewEntry->Address = (VirtualAllocBlock + 1);
|
|
pNewEntry->Size = VirtualAllocBlock->CommitSize - VirtualAllocBlock->BusyBlock.Size;
|
|
|
|
#if i386
|
|
pNewEntry->AllocatorBackTraceIndex = VirtualAllocBlock->ExtraStuff.AllocatorBackTraceIndex;
|
|
#endif // i386
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {
|
|
pNewEntry->TagIndex = 0;
|
|
}
|
|
else {
|
|
pNewEntry->TagIndex = VirtualAllocBlock->ExtraStuff.TagIndex;
|
|
}
|
|
|
|
// Search the heap usage entries list until we find the address
|
|
// that right after the new entry address and then insert this new entry.
|
|
// This will keep the entries list sorted in assending addresses
|
|
|
|
// The first time through this function ppEntries will point
|
|
// to the tail and so *pp should actually start off as null,
|
|
// which means that the big allocation simply get tacked on the end of the entries list.
|
|
// We do not augment the AddedEntries list for these big allocations.
|
|
|
|
pp = ppEntries;
|
|
while (*pp) {
|
|
if ((*pp)->Address >= pNewEntry->Address) {
|
|
break;
|
|
}
|
|
|
|
pp = &(*pp)->Next;
|
|
}
|
|
|
|
pNewEntry->Next = *pp;
|
|
*pp = pNewEntry;
|
|
|
|
Next = Next->Flink;// Get the next big allocation block
|
|
}
|
|
|
|
// At this point we've scanned through the heap segments and the big allocations.
|
|
|
|
// The first time through this procedure we have built two lists the Entries and the AddedEntries
|
|
if (NT_SUCCESS(Status)) {
|
|
pOldEntries = Buffer->LargeEntriesSentinal;
|
|
Buffer->LargeEntriesSentinal = *ppEntries;
|
|
|
|
// Now we'll process the previous large entries sentinal list
|
|
|
|
// This path is not taken the first time through this procedure
|
|
|
|
while (pOldEntries != NULL) {
|
|
// If we have new entries and the entry is equal to the
|
|
// entry in the previous large sentinal list then
|
|
// we move one down on the new list and remove the previous
|
|
// sentinal entry
|
|
|
|
if ((*ppEntries != NULL) &&
|
|
(pOldEntries->Address == (*ppEntries)->Address) &&
|
|
(pOldEntries->Size == (*ppEntries)->Size))
|
|
{
|
|
ppEntries = &(*ppEntries)->Next;
|
|
pOldEntries = RtlpFreeHeapUsageEntry(Buffer, pOldEntries);
|
|
|
|
// If we do now have any new entries or the previous
|
|
// sentinal entry is comes before this new entry then
|
|
// we'll add the sentinal entry to the remove list
|
|
}
|
|
else if ((*ppEntries == NULL) ||
|
|
(pOldEntries->Address < (*ppEntries)->Address))
|
|
{
|
|
*ppRemovedEntries = pOldEntries;
|
|
pOldEntries = pOldEntries->Next;
|
|
ppRemovedEntries = &(*ppRemovedEntries)->Next;
|
|
*ppRemovedEntries = NULL;
|
|
}
|
|
else {// Otherwise the old sentinal entry is put on the added entries list
|
|
*ppAddedEntries = pOldEntries;
|
|
pOldEntries = pOldEntries->Next;
|
|
**ppAddedEntries = **ppEntries;
|
|
ppAddedEntries = &(*ppAddedEntries)->Next;
|
|
*ppAddedEntries = NULL;
|
|
}
|
|
}
|
|
|
|
// This path is not taken the first time through this procedure
|
|
while (pNewEntry = *ppEntries) {
|
|
Status = RtlpAllocateHeapUsageEntry(Buffer, ppAddedEntries);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
**ppAddedEntries = *pNewEntry;
|
|
ppAddedEntries = &(*ppAddedEntries)->Next;
|
|
*ppAddedEntries = NULL;
|
|
ppEntries = &pNewEntry->Next;
|
|
}
|
|
|
|
// Tell the user that something has changed between the previous call and this one
|
|
if ((Usage->AddedEntries != NULL) || (Usage->RemovedEntries != NULL)) {
|
|
Status = STATUS_MORE_ENTRIES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally{
|
|
if (LockAcquired) {
|
|
RtlReleaseLockRoutine(Heap->LockVariable);// Unlock the heap
|
|
}
|
|
}
|
|
|
|
return Status;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in nturtl.h
|
|
|
|
|
|
NTSTATUS RtlWalkHeap(IN PVOID HeapHandle, IN OUT PRTL_HEAP_WALK_ENTRY Entry)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to enumerate all the entries within a heap.
|
|
For each call it returns a new information in entry.
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being queried
|
|
Entry - Supplies storage for the entry information.
|
|
If the DataAddress field is null then the enumeration starts over from the beginning otherwise it resumes from where it left off
|
|
Return Value:
|
|
NTSTATUS - An appropriate status value
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PHEAP Heap = (PHEAP)HeapHandle;
|
|
PHEAP_SEGMENT Segment;
|
|
UCHAR SegmentIndex;
|
|
PHEAP_ENTRY CurrentBlock;
|
|
PHEAP_ENTRY_EXTRA ExtraStuff;
|
|
PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
|
|
PLIST_ENTRY Next, Head;
|
|
PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
|
|
|
|
// Check if we should be using the guard page verion of heap
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, RtlpDebugPageHeapWalk(HeapHandle, Entry));
|
|
|
|
if (DEBUG_HEAP(Heap->Flags)) {// If this is the debug version of heap then validate the heap before we go on
|
|
if (!RtlDebugWalkHeap(HeapHandle, Entry)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// If there is an active lookaside list then drain and remove it.
|
|
// By setting the lookaside field in the heap to null we guarantee that the call the free heap will not try and use the lookaside list logic.
|
|
|
|
// We'll actually capture the lookaside pointer from the heap and only use the captured pointer.
|
|
// This will take care of the condition where another walk or
|
|
// lock heap can cause us to check for a non null pointer and then have it become null when we read it again.
|
|
// If it is non null to start with then even if the user walks or
|
|
// locks the heap via another thread the pointer to
|
|
// still valid here so we can still try and do a lookaside list pop.
|
|
{
|
|
PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)Heap->Lookaside;
|
|
if (Lookaside != NULL) {
|
|
ULONG i;
|
|
PVOID Block;
|
|
|
|
Heap->Lookaside = NULL;
|
|
|
|
for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
|
|
while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
|
|
RtlFreeHeap(HeapHandle, 0, Block);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Entry->DataAddress == NULL) {// Check if this is the first time we've been called to walk the heap
|
|
SegmentIndex = 0;// Start with the first segement in the heap
|
|
|
|
nextSegment:
|
|
CurrentBlock = NULL;
|
|
Segment = NULL;// Now find the next in use segment for the heap
|
|
|
|
while ((SegmentIndex < HEAP_MAXIMUM_SEGMENTS) &&
|
|
((Segment = Heap->Segments[SegmentIndex]) == NULL))
|
|
{
|
|
SegmentIndex += 1;
|
|
}
|
|
|
|
if (Segment == NULL) {// If there are no more valid segments then we'll try the big allocation
|
|
Head = &Heap->VirtualAllocdBlocks;
|
|
Next = Head->Flink;
|
|
if (Next == Head) {
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
else {
|
|
VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
|
|
CurrentBlock = &VirtualAllocBlock->BusyBlock;
|
|
}
|
|
|
|
// Otherwise we'll grab information about the segment.
|
|
// Note that the current block is still null so when we fall out of this
|
|
// block we'll return directly to our caller with this segment information
|
|
}
|
|
else {
|
|
Entry->DataAddress = Segment;
|
|
Entry->DataSize = 0;
|
|
Entry->OverheadBytes = sizeof(*Segment);
|
|
Entry->Flags = RTL_HEAP_SEGMENT;
|
|
Entry->SegmentIndex = SegmentIndex;
|
|
Entry->Segment.CommittedSize = (Segment->NumberOfPages - Segment->NumberOfUnCommittedPages) * PAGE_SIZE;
|
|
Entry->Segment.UnCommittedSize = Segment->NumberOfUnCommittedPages * PAGE_SIZE;
|
|
Entry->Segment.FirstEntry = (Segment->FirstEntry->Flags & HEAP_ENTRY_BUSY) ?
|
|
((PHEAP_ENTRY)Segment->FirstEntry + 1) :
|
|
(PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Segment->FirstEntry + 1);
|
|
Entry->Segment.LastEntry = Segment->LastValidEntry;
|
|
}
|
|
|
|
// This is not the first time through.
|
|
// Check if last time we gave back an heap segement or an uncommitted range
|
|
}
|
|
else if (Entry->Flags & (RTL_HEAP_SEGMENT | RTL_HEAP_UNCOMMITTED_RANGE)) {
|
|
if ((SegmentIndex = Entry->SegmentIndex) >= HEAP_MAXIMUM_SEGMENTS) {// Check that the segment index is still valid
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
CurrentBlock = NULL;
|
|
}
|
|
else {
|
|
// Check that the segment is still in use
|
|
Segment = Heap->Segments[SegmentIndex];
|
|
if (Segment == NULL) {
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
CurrentBlock = NULL;
|
|
|
|
// The segment is still in use if what we returned last time
|
|
// as the segment header then this time we'll return the segments first entry
|
|
}
|
|
else if (Entry->Flags & RTL_HEAP_SEGMENT) {
|
|
CurrentBlock = (PHEAP_ENTRY)Segment->FirstEntry;
|
|
}
|
|
else {// Otherwise what we returned last time as an uncommitted range so now we need to get the next block
|
|
CurrentBlock = (PHEAP_ENTRY)((PCHAR)Entry->DataAddress + Entry->DataSize);
|
|
if (CurrentBlock >= Segment->LastValidEntry) {// Check if we are beyond this segment and need to get the next one
|
|
SegmentIndex += 1;
|
|
goto nextSegment;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {// Otherwise this is not the first time through and last time we gave back a valid heap entry
|
|
if (Entry->Flags & HEAP_ENTRY_BUSY) {// Check if the last entry we gave back was in use
|
|
CurrentBlock = ((PHEAP_ENTRY)Entry->DataAddress - 1);// Get the last entry we returned
|
|
|
|
// If the last entry was for a big allocation then get the next big block if there is one otherwise
|
|
// say there are no more entries
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
|
|
Head = &Heap->VirtualAllocdBlocks;
|
|
VirtualAllocBlock = CONTAINING_RECORD(CurrentBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
|
|
Next = VirtualAllocBlock->Entry.Flink;
|
|
if (Next == Head) {
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
else {
|
|
VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
|
|
CurrentBlock = &VirtualAllocBlock->BusyBlock;
|
|
}
|
|
}
|
|
else {// Our previous result is a busy normal block
|
|
// Get the segment and make sure it it still valid and in use
|
|
|
|
// **** this should also check that segment index is not greater than HEAP MAXIMUM SEGMENTS
|
|
Segment = Heap->Segments[SegmentIndex = CurrentBlock->SegmentIndex];
|
|
if (Segment == NULL) {
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
CurrentBlock = NULL;
|
|
|
|
// The segment is still in use, check if what we returned previously was a last entry
|
|
}
|
|
else if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
|
|
findUncommittedRange:
|
|
// We are at a last entry so now if the segment is done then go get another segment
|
|
CurrentBlock += CurrentBlock->Size;
|
|
if (CurrentBlock >= Segment->LastValidEntry) {
|
|
SegmentIndex += 1;
|
|
goto nextSegment;
|
|
}
|
|
|
|
// Otherwise we will find the uncommitted range entry that immediately follows this last entry
|
|
pp = &Segment->UnCommittedRanges;
|
|
while ((UnCommittedRange = *pp) && UnCommittedRange->Address != (ULONG_PTR)CurrentBlock)
|
|
{
|
|
pp = &UnCommittedRange->Next;
|
|
}
|
|
|
|
if (UnCommittedRange == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else {// Now fill in the entry to denote that uncommitted range information
|
|
Entry->DataAddress = (PVOID)UnCommittedRange->Address;
|
|
Entry->DataSize = UnCommittedRange->Size;
|
|
Entry->OverheadBytes = 0;
|
|
Entry->SegmentIndex = SegmentIndex;
|
|
Entry->Flags = RTL_HEAP_UNCOMMITTED_RANGE;
|
|
}
|
|
|
|
CurrentBlock = NULL;// Null out the current block because we've just filled in the entry
|
|
}
|
|
else {// Otherwise the entry has a following entry so now advance to the next entry
|
|
CurrentBlock += CurrentBlock->Size;
|
|
}
|
|
}
|
|
}
|
|
else {// Otherwise the previous entry we returned is not in use
|
|
CurrentBlock = (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Entry->DataAddress - 1);// Get the last entry we returned
|
|
|
|
// Get the segment and make sure it it still valid and in use
|
|
|
|
// **** this should also check that segment index is not greater than HEAP MAXIMUM SEGMENTS
|
|
Segment = Heap->Segments[SegmentIndex = CurrentBlock->SegmentIndex];
|
|
if (Segment == NULL) {
|
|
Status = STATUS_INVALID_ADDRESS;
|
|
CurrentBlock = NULL;
|
|
}// If the block is the last entry then go find the next uncommitted range or segment
|
|
else if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
|
|
goto findUncommittedRange;
|
|
}
|
|
else {// Otherwise we'll just move on to the next entry
|
|
CurrentBlock += CurrentBlock->Size;
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point if current block is not null then we've found another entry to return.
|
|
// We could also have found a segment or uncommitted range but those are handled separately above and keep current block null
|
|
if (CurrentBlock != NULL) {
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {// Check if the block is in use
|
|
// Fill in the entry field for this block
|
|
Entry->DataAddress = (CurrentBlock + 1);
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
|
|
Entry->DataSize = RtlpGetSizeOfBigBlock(CurrentBlock);
|
|
Entry->OverheadBytes = (UCHAR)(sizeof(*VirtualAllocBlock) + CurrentBlock->Size);
|
|
Entry->SegmentIndex = HEAP_MAXIMUM_SEGMENTS;
|
|
Entry->Flags = RTL_HEAP_BUSY | HEAP_ENTRY_VIRTUAL_ALLOC;
|
|
}
|
|
else {
|
|
Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) - CurrentBlock->UnusedBytes;
|
|
Entry->OverheadBytes = CurrentBlock->UnusedBytes;
|
|
Entry->SegmentIndex = CurrentBlock->SegmentIndex;
|
|
Entry->Flags = RTL_HEAP_BUSY;
|
|
}
|
|
|
|
if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
ExtraStuff = RtlpGetExtraStuffPointer(CurrentBlock);
|
|
Entry->Block.Settable = ExtraStuff->Settable;
|
|
#if i386
|
|
Entry->Block.AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
|
|
#endif // i386
|
|
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {
|
|
Entry->Block.TagIndex = 0;
|
|
}
|
|
else {
|
|
Entry->Block.TagIndex = ExtraStuff->TagIndex;
|
|
}
|
|
|
|
Entry->Flags |= RTL_HEAP_SETTABLE_VALUE;
|
|
}
|
|
else {
|
|
if (!IS_HEAP_TAGGING_ENABLED()) {
|
|
Entry->Block.TagIndex = 0;
|
|
}
|
|
else {
|
|
Entry->Block.TagIndex = CurrentBlock->SmallTagIndex;
|
|
}
|
|
}
|
|
|
|
Entry->Flags |= CurrentBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS;
|
|
}
|
|
else {// Otherwise the block is not in use
|
|
Entry->DataAddress = ((PHEAP_FREE_ENTRY)CurrentBlock + 1);
|
|
Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) - sizeof(HEAP_FREE_ENTRY);
|
|
Entry->OverheadBytes = sizeof(HEAP_FREE_ENTRY);
|
|
Entry->SegmentIndex = CurrentBlock->SegmentIndex;
|
|
Entry->Flags = 0;
|
|
}
|
|
}
|
|
|
|
return Status;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
BOOLEAN RtlpCheckHeapSignature(IN PHEAP Heap, IN PCHAR Caller)
|
|
/*++
|
|
Routine Description:
|
|
This routine verifies that it is being called with a properly identified heap.
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being checked
|
|
Caller - Supplies a string that can be used to identify the caller
|
|
Return Value:
|
|
BOOLEAN - TRUE if the heap signature is present, and FALSE otherwise
|
|
*/
|
|
{
|
|
// If the heap signature matches then that is the only checking we do
|
|
if (Heap->Signature == HEAP_SIGNATURE) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
// We have a bad heap signature.
|
|
// Print out some information, break into the debugger, and then return false
|
|
HeapDebugPrint(("Invalid heap signature for heap at %x", Heap));
|
|
|
|
if (Caller != NULL) {
|
|
DbgPrint(", passed to %s", Caller);
|
|
}
|
|
|
|
DbgPrint("\n");
|
|
|
|
HeapDebugBreak(&Heap->Signature);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
PHEAP_FREE_ENTRY RtlpCoalesceHeap(IN PHEAP Heap)
|
|
/*++
|
|
Routine Description:
|
|
This routine scans through heap and coalesces its free blocks
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified
|
|
Return Value:
|
|
PHEAP_FREE_ENTRY - returns a pointer to the largest free block in the heap
|
|
*/
|
|
{
|
|
SIZE_T OldFreeSize;
|
|
SIZE_T FreeSize;
|
|
ULONG n;
|
|
PHEAP_FREE_ENTRY FreeBlock, LargestFreeBlock;
|
|
PLIST_ENTRY FreeListHead, Next;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
LargestFreeBlock = NULL;
|
|
|
|
// For every free list in the heap, going from smallest to
|
|
// largest and skipping the zero index one we will
|
|
// scan the free list coalesceing the free blocks
|
|
|
|
FreeListHead = &Heap->FreeLists[1];
|
|
n = HEAP_MAXIMUM_FREELISTS;
|
|
|
|
while (n--) {
|
|
Next = FreeListHead->Blink;// Scan the individual free list
|
|
|
|
while (FreeListHead != Next) {
|
|
// Get a pointer to the current free list entry, and remember its next and size
|
|
FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
|
|
|
|
Next = Next->Flink;
|
|
OldFreeSize = FreeSize = FreeBlock->Size;
|
|
|
|
FreeBlock = RtlpCoalesceFreeBlocks(Heap, FreeBlock, &FreeSize, TRUE);// Coalesce the block
|
|
|
|
// If the new free size is not equal to the old free size
|
|
// then we actually did some changes otherwise the coalesce
|
|
// calll was essentialy a noop
|
|
if (FreeSize != OldFreeSize) {
|
|
// Check if we should decommit this block because it is too
|
|
// large and it is either at the beginning or end of a committed run.
|
|
// Otherwise just insert the new sized block into its corresponding free list.
|
|
// We'll hit this block again when we visit larger free lists.
|
|
|
|
if (FreeBlock->Size >= (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT) &&
|
|
(FreeBlock->PreviousSize == 0 || (FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)))
|
|
{
|
|
RtlpDeCommitFreeBlock(Heap, FreeBlock, FreeSize);
|
|
}
|
|
else {
|
|
RtlpInsertFreeBlock(Heap, FreeBlock, FreeSize);
|
|
}
|
|
|
|
Next = FreeListHead->Blink;
|
|
}
|
|
else {
|
|
// Remember the largest free block we've found so far
|
|
if ((LargestFreeBlock == NULL) || (LargestFreeBlock->Size < FreeBlock->Size)) {
|
|
LargestFreeBlock = FreeBlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go to the next free list. When we hit the largest dedicated
|
|
// size free list we'll fall back to the [0] index list
|
|
if (n == 1) {
|
|
FreeListHead = &Heap->FreeLists[0];
|
|
}
|
|
else {
|
|
FreeListHead++;
|
|
}
|
|
}
|
|
|
|
return LargestFreeBlock;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
VOID RtlpAddHeapToProcessList(IN PHEAP Heap)
|
|
/*++
|
|
Routine Description:
|
|
This routine adds the specified heap to the heap list for the current process
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being added
|
|
*/
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
PHEAP *NewList;
|
|
|
|
RtlAcquireLockRoutine(&RtlpProcessHeapsListLock.Lock);// Lock the processes heap list
|
|
try {
|
|
// If the processes heap list is already full then we'll
|
|
// double the size of the heap list for the process
|
|
if (Peb->NumberOfHeaps == Peb->MaximumNumberOfHeaps) {
|
|
Peb->MaximumNumberOfHeaps *= 2;// Double the size
|
|
|
|
// Allocate space for the new list
|
|
NewList = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
Peb->MaximumNumberOfHeaps * sizeof(*NewList));
|
|
if (NewList == NULL) {
|
|
leave;
|
|
}
|
|
|
|
// Copy over the old buffer to the new buffer
|
|
RtlMoveMemory(NewList, Peb->ProcessHeaps, Peb->NumberOfHeaps * sizeof(*NewList));
|
|
|
|
// Check if we should free the previous heap list buffer
|
|
if (Peb->ProcessHeaps != RtlpProcessHeapsListBuffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Peb->ProcessHeaps);
|
|
}
|
|
|
|
Peb->ProcessHeaps = NewList; // Set the new list
|
|
}
|
|
|
|
// Add the input heap to the next free heap list slot, and note that
|
|
// the processes heap list index is really one beyond the actualy index used to get the processes heap
|
|
Peb->ProcessHeaps[Peb->NumberOfHeaps++] = Heap;
|
|
Heap->ProcessHeapsListIndex = (USHORT)Peb->NumberOfHeaps;
|
|
}
|
|
finally{
|
|
RtlReleaseLockRoutine(&RtlpProcessHeapsListLock.Lock);// Unlock the processes heap list
|
|
}
|
|
|
|
return;// And return to our caller
|
|
}
|
|
|
|
|
|
// Delcared in heappriv.h
|
|
|
|
|
|
VOID RtlpRemoveHeapFromProcessList(IN PHEAP Heap)
|
|
/*++
|
|
Routine Description:
|
|
This routine removes the specified heap to the heap list for the current process
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being removed
|
|
*/
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
PHEAP *p, *p1;
|
|
ULONG n;
|
|
|
|
// Lock the current processes heap list lock
|
|
RtlAcquireLockRoutine(&RtlpProcessHeapsListLock.Lock);
|
|
try {
|
|
// We only want to the the work if the current process actually has some
|
|
// heaps, the index stored in the heap is within the range for active
|
|
// heaps. Note that the heaps stored index is bias by one.
|
|
if ((Peb->NumberOfHeaps != 0) &&
|
|
(Heap->ProcessHeapsListIndex != 0) &&
|
|
(Heap->ProcessHeapsListIndex <= Peb->NumberOfHeaps))
|
|
{
|
|
// Establish a pointer into the array of process heaps at the current heap location and one beyond
|
|
p = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
|
|
|
|
p1 = p + 1;
|
|
|
|
// Calculate the number of heaps that exist beyond the current
|
|
// heap in the array including the current heap location
|
|
n = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
|
|
|
|
// For every heap beyond the current one that we are removing
|
|
// we'll move that heap down to the previous index.
|
|
while (--n) {
|
|
// Copy the heap process array entry of the next entry to the current entry,
|
|
// and move p1 to the next next entry
|
|
*p = *p1++;
|
|
|
|
RtlpUpdateHeapListIndex((*p)->ProcessHeapsListIndex, (USHORT)((*p)->ProcessHeapsListIndex - 1));//This is simply a debugging call
|
|
(*p)->ProcessHeapsListIndex -= 1;// Assign the moved heap its new heap index
|
|
p += 1;// Move on to the next heap entry
|
|
}
|
|
|
|
// Zero out the last process heap pointer, update the count, and
|
|
// make the heap we just removed realize it has been removed by zeroing out its process heap list index
|
|
Peb->ProcessHeaps[--Peb->NumberOfHeaps] = NULL;
|
|
Heap->ProcessHeapsListIndex = 0;
|
|
}
|
|
}
|
|
finally{
|
|
RtlReleaseLockRoutine(&RtlpProcessHeapsListLock.Lock);// Unlock the current processes heap list lock
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Local Support routine
|
|
|
|
|
|
BOOLEAN RtlpGrowBlockInPlace(IN PHEAP Heap,
|
|
IN ULONG Flags,
|
|
IN PHEAP_ENTRY BusyBlock,
|
|
IN SIZE_T Size,
|
|
IN SIZE_T AllocationIndex)
|
|
/*++
|
|
Routine Description:
|
|
This routine will try and grow a heap allocation block at its current location
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified
|
|
Flags - Supplies a set of flags to augment those already enforced by the heap
|
|
BusyBlock - Supplies a pointer to the block being resized
|
|
Size - Supplies the size, in bytes, needed by the resized block
|
|
AllocationIndex - Supplies the allocation index for the resized block
|
|
Note that the size variable has not been rounded up to the next granular block size,
|
|
but that allocation index has.
|
|
Return Value:
|
|
BOOLEAN - TRUE if the block has been resized and FALSE otherwise
|
|
*/
|
|
{
|
|
SIZE_T FreeSize;
|
|
SIZE_T OldSize;
|
|
UCHAR EntryFlags, FreeFlags;
|
|
PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
|
|
PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
|
|
|
|
// Check if the allocation index is too large for even the nondedicated
|
|
// free list (i.e., too large for list [0])
|
|
if (AllocationIndex > Heap->VirtualMemoryThreshold) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the flags for the current block and a pointer to the next
|
|
// block following the current block
|
|
EntryFlags = BusyBlock->Flags;
|
|
FreeBlock = (PHEAP_FREE_ENTRY)(BusyBlock + BusyBlock->Size);
|
|
|
|
// If the current block is the last entry before an uncommitted range
|
|
// we'll try and extend the uncommitted range to fit our new allocation
|
|
if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
|
|
// Calculate how must more we need beyond the current block size
|
|
FreeSize = (AllocationIndex - BusyBlock->Size) << HEAP_GRANULARITY_SHIFT;
|
|
FreeSize = ROUND_UP_TO_POWER2(FreeSize, PAGE_SIZE);
|
|
|
|
// Try and commit memory at the desired location
|
|
FreeBlock = RtlpFindAndCommitPages(Heap,
|
|
Heap->Segments[BusyBlock->SegmentIndex],
|
|
&FreeSize,
|
|
(PHEAP_ENTRY)FreeBlock);
|
|
if (FreeBlock == NULL) {// Check if the commit succeeded
|
|
return FALSE;
|
|
}
|
|
|
|
// New coalesce this newly committed space with whatever is free around it
|
|
FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
|
|
FreeBlock = RtlpCoalesceFreeBlocks(Heap, FreeBlock, &FreeSize, FALSE);
|
|
FreeFlags = FreeBlock->Flags;
|
|
|
|
// If the newly allocated space plus the current block size is still
|
|
// not big enough for our resize effort then put this newly
|
|
// allocated block into the appropriate free list and tell our caller that a resize wasn't possible
|
|
if ((FreeSize + BusyBlock->Size) < AllocationIndex) {
|
|
RtlpInsertFreeBlock(Heap, FreeBlock, FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
if (DEBUG_HEAP(Flags)) {
|
|
RtlpValidateHeapHeaders(Heap, TRUE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// We were able to generate enough space for the resize effort, so
|
|
// now free size will be the index for the current block plus the new free space
|
|
FreeSize += BusyBlock->Size;
|
|
}
|
|
else {
|
|
// The following block is present so grab its flags and see if
|
|
// it is free or busy. If busy then we cannot grow the current block
|
|
FreeFlags = FreeBlock->Flags;
|
|
if (FreeFlags & HEAP_ENTRY_BUSY) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Compute the index if we combine current block with its following
|
|
// free block and check if it is big enough
|
|
FreeSize = BusyBlock->Size + FreeBlock->Size;
|
|
if (FreeSize < AllocationIndex) {
|
|
return FALSE;
|
|
}
|
|
|
|
// The two blocks together are big enough so now remove the free
|
|
// block from its free list, and update the heap's total free size
|
|
RtlpRemoveFreeBlock(Heap, FreeBlock);
|
|
Heap->TotalFreeSize -= FreeBlock->Size;
|
|
}
|
|
|
|
// At this point we have a busy block followed by a free block that
|
|
// together have enough space for the resize. The free block has been
|
|
// removed from its list and free size is the index of the two combined blocks.
|
|
|
|
// Calculate the number of bytes in use in the old block
|
|
OldSize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) - BusyBlock->UnusedBytes;
|
|
|
|
// Calculate the index for whatever excess we'll have when we combine the two blocks
|
|
FreeSize -= AllocationIndex;
|
|
|
|
// If the excess is not too much then put it back in our allocation
|
|
// (i.e., we don't want small free pieces left over)
|
|
if (FreeSize <= 2) {
|
|
AllocationIndex += FreeSize;
|
|
FreeSize = 0;
|
|
}
|
|
|
|
// If the busy block has an extra stuff struct present then copy over the extra stuff
|
|
if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) {
|
|
OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
|
|
NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
|
|
*NewExtraStuff = *OldExtraStuff;
|
|
|
|
// If heap tagging is enabled then update the heap tag from the extra stuff struct
|
|
if (IS_HEAP_TAGGING_ENABLED()) {
|
|
NewExtraStuff->TagIndex = RtlpUpdateTagEntry(Heap,
|
|
NewExtraStuff->TagIndex,
|
|
BusyBlock->Size,
|
|
AllocationIndex,
|
|
ReAllocationAction);
|
|
}
|
|
|
|
// Otherwise extra stuff is not in use so see if heap tagging is enabled
|
|
// and if so then update small tag index
|
|
}
|
|
else if (IS_HEAP_TAGGING_ENABLED()) {
|
|
BusyBlock->SmallTagIndex = (UCHAR)RtlpUpdateTagEntry(Heap,
|
|
BusyBlock->SmallTagIndex,
|
|
BusyBlock->Size,
|
|
AllocationIndex,
|
|
ReAllocationAction);
|
|
}
|
|
|
|
// Check if we will have any free space to give back.
|
|
if (FreeSize == 0) {
|
|
// No following free space so update the flags, size and byte counts
|
|
// for the resized block. If the free block was a last entry
|
|
// then the busy block must also now be a last entry.
|
|
|
|
BusyBlock->Flags |= FreeFlags & HEAP_ENTRY_LAST_ENTRY;
|
|
BusyBlock->Size = (USHORT)AllocationIndex;
|
|
BusyBlock->UnusedBytes = (UCHAR)((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
|
|
|
|
// Update the previous size field of the following block if it exists
|
|
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
|
|
(BusyBlock + BusyBlock->Size)->PreviousSize = BusyBlock->Size;
|
|
}
|
|
else {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[BusyBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = BusyBlock;
|
|
}
|
|
}
|
|
else {// Otherwise there is some free space to return to the heap
|
|
// Update the size and byte counts for the resized block.
|
|
BusyBlock->Size = (USHORT)AllocationIndex;
|
|
BusyBlock->UnusedBytes = (UCHAR)((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
|
|
|
|
// Determine where the new free block starts and fill in its fields
|
|
SplitBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)BusyBlock + AllocationIndex);
|
|
SplitBlock->PreviousSize = (USHORT)AllocationIndex;
|
|
SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
|
|
|
|
// If this new free block will be the last entry then update its
|
|
// flags and size and put it into the appropriate free list
|
|
if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[SplitBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
|
|
|
|
SplitBlock->Flags = FreeFlags;
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {// The free block is followed by another valid block
|
|
// Point to the block following our new free block
|
|
SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
|
|
|
|
// If the block following the new free block is busy then
|
|
// update the flags and size for the new free block, update
|
|
// the following blocks previous size, and put the free block into the appropriate free list
|
|
if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
|
|
SplitBlock->Flags = FreeFlags & (~HEAP_ENTRY_LAST_ENTRY);
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
|
|
// **** note that this test must be true because we are
|
|
// **** already in the else clause of the
|
|
// **** if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) statement
|
|
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
|
|
((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
|
|
}
|
|
else {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[SplitBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
|
|
}
|
|
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {// Otherwise the following block is also free so we can combine these two blocks
|
|
FreeFlags = SplitBlock2->Flags;// Remember the new free flags from the following block
|
|
|
|
// Remove the following block from its free list
|
|
RtlpRemoveFreeBlock(Heap, SplitBlock2);
|
|
Heap->TotalFreeSize -= SplitBlock2->Size;
|
|
|
|
FreeSize += SplitBlock2->Size;// Calculate the size of the new combined free block
|
|
SplitBlock->Flags = FreeFlags;// Give the new the its new flags
|
|
|
|
// If the combited block is not too large for the dedicated
|
|
// free lists then that where we'll put it
|
|
if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
|
|
SplitBlock->Size = (USHORT)FreeSize;
|
|
|
|
// If present update the previous size for the following block
|
|
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
|
|
((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
|
|
}
|
|
else {
|
|
PHEAP_SEGMENT Segment;
|
|
|
|
Segment = Heap->Segments[SplitBlock->SegmentIndex];
|
|
Segment->LastEntryInSegment = (PHEAP_ENTRY)SplitBlock;
|
|
}
|
|
|
|
// Insert the new combined free block into the free list
|
|
RtlpInsertFreeBlockDirect(Heap, SplitBlock, (USHORT)FreeSize);
|
|
Heap->TotalFreeSize += FreeSize;
|
|
}
|
|
else {
|
|
// Otherwise the new free block is too large to go into
|
|
// a dedicated free list so put it in the general free list
|
|
// which might involve breaking it apart.
|
|
RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point the block has been resized and any extra space has been returned to the free list
|
|
|
|
if (Flags & HEAP_ZERO_MEMORY) {// Check if we should zero out the new space
|
|
// **** this test is sort of bogus because we're resizing and the new
|
|
// **** size by definition must be larger than the old size
|
|
if (Size > OldSize) {
|
|
RtlZeroMemory((PCHAR)(BusyBlock + 1) + OldSize, Size - OldSize);
|
|
}
|
|
|
|
// Check if we should be filling in heap after it as been freed,
|
|
// and if so then fill in the newly allocated space beyond the old bytes.
|
|
}
|
|
else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
|
|
SIZE_T PartialBytes, ExtraSize;
|
|
PartialBytes = OldSize & (sizeof(ULONG) - 1);
|
|
if (PartialBytes) {
|
|
PartialBytes = 4 - PartialBytes;
|
|
}
|
|
|
|
if (Size > (OldSize + PartialBytes)) {
|
|
ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof(ULONG) - 1);
|
|
if (ExtraSize != 0) {
|
|
RtlFillMemoryUlong((PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
|
|
ExtraSize,
|
|
ALLOC_HEAP_FILL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we are going tailing checking then fill in the space right beyond the new allocation
|
|
if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
|
|
RtlFillMemory((PCHAR)(BusyBlock + 1) + Size, CHECK_HEAP_TAIL_SIZE, CHECK_HEAP_TAIL_FILL);
|
|
}
|
|
|
|
// Give the resized block any user settable flags send in by the caller
|
|
BusyBlock->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
|
|
BusyBlock->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
|
|
|
|
return TRUE;// And return to our caller
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
PHEAP_TAG_ENTRY RtlpAllocateTags(PHEAP Heap, ULONG NumberOfTags)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to allocate space for additional tags within a heap
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified.
|
|
If not specified then the processes global tag heap is used
|
|
NumberOfTags - Supplies the number of tags that we want stored in the heap.
|
|
This is the number to grow the tag list by.
|
|
Return Value:
|
|
PHEAP_TAG_ENTRY - Returns a pointer to the next available tag entry in the heap
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG TagIndex;
|
|
SIZE_T ReserveSize;
|
|
SIZE_T CommitSize;
|
|
PHEAP_TAG_ENTRY TagEntry;
|
|
USHORT CreatorBackTraceIndex;
|
|
USHORT MaximumTagIndex;
|
|
USHORT TagIndexFlag;
|
|
|
|
// Check if the process has a global tag heap.
|
|
// If not then there is nothing for us to do
|
|
if (RtlpGlobalTagHeap == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// If the user didn't give us a heap then use the processes global tag heap
|
|
if (Heap == NULL) {
|
|
RtlpGlobalTagHeap->Signature = HEAP_SIGNATURE;
|
|
RtlpGlobalTagHeap->Flags = HEAP_NO_SERIALIZE;
|
|
TagIndexFlag = HEAP_GLOBAL_TAG;
|
|
Heap = RtlpGlobalTagHeap;
|
|
}
|
|
else {
|
|
TagIndexFlag = 0;
|
|
}
|
|
|
|
CreatorBackTraceIndex = 0;// Grab the stack backtrace if possible and if we should
|
|
|
|
#if i386
|
|
if (Heap->Flags & HEAP_CAPTURE_STACK_BACKTRACES) {
|
|
CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
|
|
}
|
|
#endif // i386
|
|
|
|
// If the heap does not already have tag entries then we'll reserve space for them
|
|
if (Heap->TagEntries == NULL) {
|
|
MaximumTagIndex = HEAP_MAXIMUM_TAG & ~HEAP_GLOBAL_TAG;
|
|
ReserveSize = MaximumTagIndex * sizeof(HEAP_TAG_ENTRY);
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&Heap->TagEntries,
|
|
0,
|
|
&ReserveSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
Heap->MaximumTagIndex = MaximumTagIndex;
|
|
Heap->NextAvailableTagIndex = 0;
|
|
|
|
NumberOfTags += 1;// Add one for zero tag, as that is always reserved for heap name
|
|
}
|
|
|
|
// At this point we have a space reserved for tag entries. If the number
|
|
// of tags that we need to grow is too large then tell the user we can't do it.
|
|
if (NumberOfTags > (ULONG)(Heap->MaximumTagIndex - Heap->NextAvailableTagIndex)) {
|
|
return NULL;
|
|
}
|
|
|
|
// Get a pointer to the next available tag entry, and for every tag entry that we want to grow by we'll commit
|
|
// the page containing the tag entry. We only need to do this for every page just once.
|
|
// We'll determine this by seeing when the tag entry crosses a page boundary
|
|
TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
|
|
for (TagIndex = Heap->NextAvailableTagIndex;
|
|
TagIndex < Heap->NextAvailableTagIndex + NumberOfTags;
|
|
TagIndex++)
|
|
{
|
|
if (((((ULONG_PTR)TagEntry + sizeof(*TagEntry)) & (PAGE_SIZE - 1)) <= sizeof(*TagEntry))) {
|
|
CommitSize = PAGE_SIZE;
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&TagEntry,
|
|
0,
|
|
&CommitSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
TagEntry->TagIndex = (USHORT)TagIndex | TagIndexFlag;// Bias the tag index if this is the global tag heap
|
|
TagEntry->CreatorBackTraceIndex = CreatorBackTraceIndex;// Set the stack back trace
|
|
TagEntry += 1;// Move on to the next tag entry
|
|
}
|
|
|
|
// At this point we've build the new tag list so now pop off the next available tag entry
|
|
TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
|
|
Heap->NextAvailableTagIndex += (USHORT)NumberOfTags;
|
|
|
|
return TagEntry;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
PWSTR RtlpGetTagName(PHEAP Heap, USHORT TagIndex)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the name of the tag denoted by the heap, tagindex tuple.
|
|
This routine is only called by heapdbg when doing a debug print to generate a tag name for printing
|
|
Arguments:
|
|
Heap - Supplies the tag being queried
|
|
TagIndex - Supplies the index for the tag being queried
|
|
Return Value:
|
|
PWSTR - returns the name of the indicated tag
|
|
*/
|
|
{
|
|
// If the processes global tag heap has not been initialized then not tag has a name
|
|
if (RtlpGlobalTagHeap == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// We only deal with non zero tag indices
|
|
if (TagIndex != 0) {
|
|
// If the tag index is for a pseudo tag then we clear the
|
|
// the psuedo bit and generate a pseudo tag name
|
|
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
|
|
TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
|
|
|
|
// Check that the tag index is valid and that the heap has some psuedo tag entries
|
|
if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) && (Heap->PseudoTagEntries != NULL)) {
|
|
// A pseudo tag index of zero denote objects
|
|
if (TagIndex == 0) {
|
|
swprintf(RtlpPseudoTagNameBuffer,
|
|
L"Objects>%4u",
|
|
HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT);
|
|
|
|
// A psuedo tag index less than the free list maximum
|
|
// denotes the dedicated free list
|
|
}
|
|
else if (TagIndex < HEAP_MAXIMUM_FREELISTS) {
|
|
swprintf(RtlpPseudoTagNameBuffer, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT);
|
|
}
|
|
else {// Otherwise the pseudo tag is for the big allocations
|
|
swprintf(RtlpPseudoTagNameBuffer, L"VirtualAlloc");
|
|
}
|
|
|
|
return RtlpPseudoTagNameBuffer;
|
|
}
|
|
|
|
// Otherwise if the tag index is for a global tag then we pull the name off of the global heap.
|
|
// Provided the index is valid and the heap does have some tag entries
|
|
}
|
|
else if (TagIndex & HEAP_GLOBAL_TAG) {
|
|
TagIndex &= ~HEAP_GLOBAL_TAG;
|
|
if ((TagIndex < RtlpGlobalTagHeap->NextAvailableTagIndex) &&
|
|
(RtlpGlobalTagHeap->TagEntries != NULL))
|
|
{
|
|
return RtlpGlobalTagHeap->TagEntries[TagIndex].TagName;
|
|
}
|
|
|
|
// Otherwise we'll pull the name off of the input heap
|
|
// provided the index is valid and the heap does have some tag entries
|
|
}
|
|
else if ((TagIndex < Heap->NextAvailableTagIndex) && (Heap->TagEntries != NULL)) {
|
|
return Heap->TagEntries[TagIndex].TagName;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
USHORT RtlpUpdateTagEntry(
|
|
PHEAP Heap,
|
|
USHORT TagIndex,
|
|
SIZE_T OldSize, // Only valid for ReAllocation and Free actions
|
|
SIZE_T NewSize, // Only valid for ReAllocation and Allocation actions
|
|
HEAP_TAG_ACTION Action
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to modify a tag entry
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified
|
|
TagIndex - Supplies the tag being modified
|
|
OldSize - Supplies the old allocation index of the block associated with the tag
|
|
NewSize - Supplies the new allocation index of the block associated with the tag
|
|
Action - Supplies the type of action being performed on the heap tag
|
|
Return Value:
|
|
USHORT - Returns a tag index for the newly updated tag
|
|
*/
|
|
{
|
|
PHEAP_TAG_ENTRY TagEntry;
|
|
|
|
// If the processes tag heap does not exist then we'll return a zero index right away
|
|
if (RtlpGlobalTagHeap == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
// If the action is greater than or equal to free action then it is either FreeAction,
|
|
// VirtualFreeAction, ReAllocationAction, or VirtualReAllocationAction.
|
|
// Which means we already should have a tag that is simply being modified
|
|
if (Action >= FreeAction) {
|
|
// If the tag index is zero then there is nothing for us to do
|
|
if (TagIndex == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// If this is a pseudo tag then make sure the rest of the tag index
|
|
// after we remove the psuedo bit is valid and that the heap is actually maintaining pseudo tags
|
|
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
|
|
TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
|
|
if ((TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) && (Heap->PseudoTagEntries != NULL)) {
|
|
TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
|
|
TagIndex |= HEAP_PSEUDO_TAG_FLAG;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
|
|
// Otherwise if this is a global tag then make sure the tag index
|
|
// after we remove the global bit is valid and that the global tag heap has some tag entries
|
|
}
|
|
else if (TagIndex & HEAP_GLOBAL_TAG) {
|
|
TagIndex &= ~HEAP_GLOBAL_TAG;
|
|
if ((TagIndex < RtlpGlobalTagHeap->NextAvailableTagIndex) &&
|
|
(RtlpGlobalTagHeap->TagEntries != NULL))
|
|
{
|
|
TagEntry = &RtlpGlobalTagHeap->TagEntries[TagIndex];
|
|
TagIndex |= HEAP_GLOBAL_TAG;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
|
|
// Otherwise we have a regular tag index that we need to make sure
|
|
// is a valid value and that the heap has some tag entries
|
|
}
|
|
else if ((TagIndex < Heap->NextAvailableTagIndex) && (Heap->TagEntries != NULL)) {
|
|
TagEntry = &Heap->TagEntries[TagIndex];
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
|
|
// At this point we have a tag entry and tag index. Increment the
|
|
// number of frees we've done on the tag, and decrement the size by the number of bytes we've just freed
|
|
TagEntry->Frees += 1;
|
|
TagEntry->Size -= OldSize;
|
|
|
|
// Now if the action is either ReAllocationAction or VirtualReAllocationAction.
|
|
// Then we get to add back in the new size and the allocation count
|
|
if (Action >= ReAllocationAction) {
|
|
// If the this is a pseudo tag then we tag entry goes off the pseudo tag list
|
|
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
|
|
TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
|
|
NewSize :
|
|
(Action == VirtualReAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0));
|
|
TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
|
|
TagIndex |= HEAP_PSEUDO_TAG_FLAG;
|
|
}
|
|
|
|
TagEntry->Allocs += 1;
|
|
TagEntry->Size += NewSize;
|
|
}
|
|
}
|
|
else {// The action is either AllocationAction or VirtualAllocationAction
|
|
// Check if the supplied tag index is a regular tag and that it is valid for the tags in this heap
|
|
if ((TagIndex != 0) &&
|
|
(TagIndex < Heap->NextAvailableTagIndex) &&
|
|
(Heap->TagEntries != NULL))
|
|
{
|
|
TagEntry = &Heap->TagEntries[TagIndex];
|
|
}
|
|
else if (TagIndex & HEAP_GLOBAL_TAG) {// Otherwise if this is a global tag then make sure that it is a valid global index
|
|
TagIndex &= ~HEAP_GLOBAL_TAG;
|
|
Heap = RtlpGlobalTagHeap;
|
|
if ((TagIndex < Heap->NextAvailableTagIndex) && (Heap->TagEntries != NULL)) {
|
|
TagEntry = &Heap->TagEntries[TagIndex];
|
|
TagIndex |= HEAP_GLOBAL_TAG;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
|
|
// Otherwise if this is a pseudo tag then build a valid tag index
|
|
// based on the new size of the allocation
|
|
}
|
|
else if (Heap->PseudoTagEntries != NULL) {
|
|
TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
|
|
NewSize :
|
|
(Action == VirtualAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0));
|
|
TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
|
|
TagIndex |= HEAP_PSEUDO_TAG_FLAG;
|
|
}
|
|
else {// Otherwise the user didn't call us with a valid tag
|
|
return 0;
|
|
}
|
|
|
|
// At this point we have a valid tag entry and tag index, so
|
|
// update the tag entry state to reflect this new allocation
|
|
TagEntry->Allocs += 1;
|
|
TagEntry->Size += NewSize;
|
|
}
|
|
|
|
return TagIndex;// And return to our caller with the new tag index
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
VOID RtlpResetTags(PHEAP Heap)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to reset all the tag entries in a heap
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified
|
|
*/
|
|
{
|
|
PHEAP_TAG_ENTRY TagEntry;
|
|
PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntry;
|
|
ULONG i;
|
|
|
|
// We only have work to do if the heap has any allocated tag entries
|
|
TagEntry = Heap->TagEntries;
|
|
|
|
if (TagEntry != NULL) {
|
|
// For every tag entry in the heap we will zero out its counters
|
|
for (i = 0; i < Heap->NextAvailableTagIndex; i++) {
|
|
TagEntry->Allocs = 0;
|
|
TagEntry->Frees = 0;
|
|
TagEntry->Size = 0;
|
|
|
|
TagEntry += 1;// Advance to the next tag entry
|
|
}
|
|
}
|
|
|
|
// We will only reset the pseudo tags if they exist
|
|
PseudoTagEntry = Heap->PseudoTagEntries;
|
|
if (PseudoTagEntry != NULL) {
|
|
// For every pseudo tag entry in the heap we will zero out its counters
|
|
for (i = 0; i < HEAP_NUMBER_OF_PSEUDO_TAG; i++) {
|
|
PseudoTagEntry->Allocs = 0;
|
|
PseudoTagEntry->Frees = 0;
|
|
PseudoTagEntry->Size = 0;
|
|
|
|
PseudoTagEntry += 1;// Advance to the next pseudo tag entry
|
|
}
|
|
}
|
|
|
|
return;// And return to our caller
|
|
}
|
|
|
|
|
|
// Declared in heappriv.h
|
|
|
|
|
|
VOID RtlpDestroyTags(PHEAP Heap)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to completely remove all the normal tag entries in use by a heap
|
|
Arguments:
|
|
Heap - Supplies a pointer to the heap being modified
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T RegionSize;
|
|
|
|
if (Heap->TagEntries != NULL) {// We will only do the action if the heap has some tag entries
|
|
RegionSize = 0;// Release all the memory used by the tag entries
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&Heap->TagEntries,
|
|
&RegionSize,
|
|
MEM_RELEASE);
|
|
if (NT_SUCCESS(Status)) {
|
|
Heap->TagEntries = NULL;
|
|
}
|
|
}
|
|
|
|
return;// And return to our caller
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
NTSTATUS RtlpAllocateHeapUsageEntry(PRTL_HEAP_USAGE_INTERNAL Buffer, PRTL_HEAP_USAGE_ENTRY *pp)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to allocate an new heap usage entry from the internal heap usage buffer
|
|
Arguments:
|
|
Buffer - Supplies a pointer to the internal heap usage buffer from which to allocate an entry
|
|
pp - Receives a pointer to the newly allocated heap usage entry.
|
|
If pp is already pointing to an existing
|
|
heap usage entry then on return we'll have this old
|
|
entry point to the new entry, but still return the new entry.
|
|
Return Value:
|
|
NTSTATUS - An appropriate status value
|
|
*/
|
|
{
|
|
NTSTATUS Status;
|
|
PRTL_HEAP_USAGE_ENTRY p;
|
|
PVOID CommitAddress;
|
|
SIZE_T PageSize;
|
|
|
|
// Check if the free list is empty and then we have to allocate more memory for the free list
|
|
if (Buffer->FreeList == NULL) {
|
|
// We cannot grow the buffer any larger than the reserved size
|
|
if (Buffer->CommittedSize >= Buffer->ReservedSize) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// Try and add one page of committed memory to the buffer
|
|
// starting right after the currently committed space
|
|
PageSize = PAGE_SIZE;
|
|
CommitAddress = (PCHAR)Buffer->Base + Buffer->CommittedSize;
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&CommitAddress,
|
|
0,
|
|
&PageSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Buffer->CommittedSize += PageSize;// Update the committed buffer size
|
|
|
|
// Add the newly allocated space to the free list and build up the free list
|
|
Buffer->FreeList = CommitAddress;
|
|
p = Buffer->FreeList;
|
|
while (PageSize != 0) {
|
|
p->Next = (p + 1);
|
|
p += 1;
|
|
PageSize -= sizeof(*p);
|
|
}
|
|
|
|
// Null terminate the next pointer in the last free entry
|
|
p -= 1;
|
|
p->Next = NULL;
|
|
}
|
|
|
|
// At this point the free list contains at least one entry
|
|
// so simply pop the entry.
|
|
p = Buffer->FreeList;
|
|
Buffer->FreeList = p->Next;
|
|
p->Next = NULL;
|
|
|
|
// Now if the caller supplied an existing heap entry then
|
|
// we'll make the old heap entry point to this new entry
|
|
if (*pp) {
|
|
(*pp)->Next = p;
|
|
}
|
|
|
|
// And then return the new entry to our caller
|
|
*pp = p;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Local support routine
|
|
|
|
|
|
PRTL_HEAP_USAGE_ENTRY RtlpFreeHeapUsageEntry(PRTL_HEAP_USAGE_INTERNAL Buffer,
|
|
PRTL_HEAP_USAGE_ENTRY p)
|
|
/*++
|
|
Routine Description:
|
|
This routine moves a heap usage entry from its current
|
|
list onto the free list and returns a pointer to the
|
|
next heap usage entry in the list. It is like doing a pop
|
|
of the list denoted by "p"
|
|
Arguments:
|
|
Buffer - Supplies a pointer to the internal heap usage buffer being modified
|
|
p - Supplies a pointer to the entry being moved. Okay if it's null
|
|
Return Value:
|
|
PRTL_HEAP_USAGE_ENTRY - Returns a pointer to the next heap usage entry
|
|
*/
|
|
{
|
|
PRTL_HEAP_USAGE_ENTRY pTmp;
|
|
|
|
// Check if we have a non null heap entry and if so then add
|
|
// the entry to the front of the free list and return the next entry in the list
|
|
if (p != NULL) {
|
|
pTmp = p->Next;
|
|
p->Next = Buffer->FreeList;
|
|
Buffer->FreeList = p;
|
|
}
|
|
else {
|
|
pTmp = NULL;
|
|
}
|
|
|
|
return pTmp;
|
|
}
|
|
|
|
|
|
// Declared in heap.h
|
|
|
|
|
|
BOOLEAN RtlpHeapIsLocked(IN PVOID HeapHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to determine if a heap is locked
|
|
Arguments:
|
|
HeapHandle - Supplies a pointer to the heap being queried
|
|
Return Value:
|
|
BOOLEAN - TRUE if the heap is locked and FALSE otherwise
|
|
*/
|
|
{
|
|
PHEAP Heap;
|
|
|
|
// Check if this is guard page version of heap
|
|
IF_DEBUG_PAGE_HEAP_THEN_RETURN(HeapHandle, RtlpDebugPageHeapIsLocked(HeapHandle));
|
|
|
|
Heap = (PHEAP)HeapHandle;
|
|
|
|
// The heap is locked if there is a lock variable,
|
|
// and it has an owning thread or the lockcount is not -1
|
|
return ((Heap->LockVariable != NULL) &&
|
|
(Heap->LockVariable->Lock.CriticalSection.OwningThread ||
|
|
Heap->LockVariable->Lock.CriticalSection.LockCount != -1));
|
|
}
|