/*++
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
Revision History:
--*/
#include "ntrtlp.h"
#include "heap.h"
#include "heappriv.h"
BOOLEAN
RtlpGrowBlockInPlace(
IN PHEAP Heap,
IN ULONG Flags,
IN PHEAP_ENTRY BusyBlock,
IN ULONG Size,
IN ULONG AllocationIndex
);
PVOID
RtlDebugReAllocateHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
IN ULONG 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
);
ULONG
RtlDebugSizeHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress
);
ULONG
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
);
HEAP_LOCK RtlpProcessHeapsListLock;
#define RTLP_STATIC_HEAP_LIST_SIZE 16
PHEAP RtlpProcessHeapsListBuffer[ RTLP_STATIC_HEAP_LIST_SIZE ];
NTSTATUS
RtlInitializeHeapManager( VOID )
{
PPEB Peb = NtCurrentPeb();
#if DBG
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 // DBG
Peb->NumberOfHeaps = 0;
Peb->MaximumNumberOfHeaps = RTLP_STATIC_HEAP_LIST_SIZE;
Peb->ProcessHeaps = RtlpProcessHeapsListBuffer;
return RtlInitializeLockRoutine( &RtlpProcessHeapsListLock.Lock );
}
BOOLEAN
RtlpCheckHeapSignature(
IN PHEAP Heap,
IN PCHAR Caller
)
{
if (Heap->Signature == HEAP_SIGNATURE) {
return TRUE;
}
else {
HeapDebugPrint(( "Invalid heap signature for heap at %x", Heap ));
if (Caller != NULL) {
DbgPrint( ", passed to %s", Caller );
}
DbgPrint( "\n" );
HeapDebugBreak( &Heap->Signature );
return FALSE;
}
}
PHEAP_FREE_ENTRY
RtlpCoalesceHeap(
IN PHEAP Heap
)
{
ULONG OldFreeSize, FreeSize, n;
PHEAP_FREE_ENTRY FreeBlock, LargestFreeBlock;
PLIST_ENTRY FreeListHead, Next;
RTL_PAGED_CODE();
LargestFreeBlock = NULL;
FreeListHead = &Heap->FreeLists[ 1 ];
n = HEAP_MAXIMUM_FREELISTS;
while (n--) {
Next = FreeListHead->Blink;
while (FreeListHead != Next) {
FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
Next = Next->Flink;
OldFreeSize = FreeSize = FreeBlock->Size;
FreeBlock = RtlpCoalesceFreeBlocks( Heap,
FreeBlock,
&FreeSize,
TRUE
);
if (FreeSize != OldFreeSize) {
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 {
if (LargestFreeBlock == NULL ||
LargestFreeBlock->Size < FreeBlock->Size
) {
LargestFreeBlock = FreeBlock;
}
}
}
if (n == 1) {
FreeListHead = &Heap->FreeLists[ 0 ];
}
else {
FreeListHead++;
}
}
return LargestFreeBlock;
}
VOID
RtlpAddHeapToProcessList(
IN PHEAP Heap
)
{
PPEB Peb = NtCurrentPeb();
PHEAP *NewList;
RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
try {
if (Peb->NumberOfHeaps == Peb->MaximumNumberOfHeaps) {
Peb->MaximumNumberOfHeaps *= 2;
NewList = RtlAllocateHeap( RtlProcessHeap(),
0,
Peb->MaximumNumberOfHeaps * sizeof( *NewList )
);
if (NewList == NULL) {
leave;
}
RtlMoveMemory( NewList,
Peb->ProcessHeaps,
Peb->NumberOfHeaps * sizeof( *NewList )
);
if (Peb->ProcessHeaps != RtlpProcessHeapsListBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, Peb->ProcessHeaps );
}
Peb->ProcessHeaps = NewList;
}
Peb->ProcessHeaps[ Peb->NumberOfHeaps++ ] = Heap;
Heap->ProcessHeapsListIndex = (USHORT)Peb->NumberOfHeaps;
}
finally {
RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
}
return;
}
VOID
RtlpRemoveHeapFromProcessList(
IN PHEAP Heap
)
{
PPEB Peb = NtCurrentPeb();
PHEAP *p, *p1;
ULONG n;
RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
if (Peb->NumberOfHeaps != 0 &&
Heap->ProcessHeapsListIndex != 0 &&
Heap->ProcessHeapsListIndex <= Peb->NumberOfHeaps
) {
p = (PHEAP *)&Peb->ProcessHeaps[ Heap->ProcessHeapsListIndex - 1 ];
p1 = p + 1;
n = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
while (--n) {
*p = *p1++;
RtlpUpdateHeapListIndex( (*p)->ProcessHeapsListIndex,
(USHORT)((*p)->ProcessHeapsListIndex - 1)
);
(*p)->ProcessHeapsListIndex -= 1;
p += 1;
}
Peb->ProcessHeaps[ --Peb->NumberOfHeaps ] = NULL;
Heap->ProcessHeapsListIndex = 0;
}
RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
return;
}
ULONG
RtlGetProcessHeaps(
ULONG NumberOfHeaps,
PVOID *ProcessHeaps
)
{
PPEB Peb = NtCurrentPeb();
ULONG ActualNumberOfHeaps;
ActualNumberOfHeaps = 0;
RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
try {
ActualNumberOfHeaps = Peb->NumberOfHeaps;
if (ActualNumberOfHeaps <= NumberOfHeaps) {
RtlMoveMemory( ProcessHeaps,
Peb->ProcessHeaps,
ActualNumberOfHeaps * sizeof( *ProcessHeaps )
);
}
}
finally {
RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
}
#ifdef DEBUG_PAGE_HEAP
if ( RtlpDebugPageHeap ) {
ULONG RemainingHeaps = ( NumberOfHeaps > ActualNumberOfHeaps ) ?
( NumberOfHeaps - ActualNumberOfHeaps ) :
( 0 );
ActualNumberOfHeaps += RtlpDebugPageHeapGetProcessHeaps(
RemainingHeaps,
ProcessHeaps + ActualNumberOfHeaps );
}
#endif
return ActualNumberOfHeaps;
}
NTSTATUS
RtlEnumProcessHeaps(
PRTL_ENUM_HEAPS_ROUTINE EnumRoutine,
PVOID Parameter
)
{
PPEB Peb = NtCurrentPeb();
NTSTATUS Status;
ULONG i;
Status = STATUS_SUCCESS;
RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
try {
for (i=0; i<Peb->NumberOfHeaps; i++) {
Status = (*EnumRoutine)( (PHEAP)(Peb->ProcessHeaps[ i ]), Parameter );
if (!NT_SUCCESS( Status )) {
break;
}
}
}
finally {
RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
}
return Status;
}
BOOLEAN
RtlLockHeap(
IN PVOID HeapHandle
)
{
PHEAP Heap = (PHEAP)HeapHandle;
RTL_PAGED_CODE();
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapLock( HeapHandle )
);
//
// Validate that HeapAddress points to a HEAP structure.
//
if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlLockHeap" )) {
return FALSE;
}
//
// Lock the heap.
//
if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
}
return TRUE;
}
BOOLEAN
RtlUnlockHeap(
IN PVOID HeapHandle
)
{
PHEAP Heap = (PHEAP)HeapHandle;
RTL_PAGED_CODE();
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapUnlock( HeapHandle )
);
//
// Validate that HeapAddress points to a HEAP structure.
//
if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlUnlockHeap" )) {
return FALSE;
}
//
// Unlock the heap.
//
if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return TRUE;
}
BOOLEAN
RtlpGrowBlockInPlace(
IN PHEAP Heap,
IN ULONG Flags,
IN PHEAP_ENTRY BusyBlock,
IN ULONG Size,
IN ULONG AllocationIndex
)
{
ULONG FreeSize, OldSize;
UCHAR EntryFlags, FreeFlags;
PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
if (AllocationIndex > Heap->VirtualMemoryThreshold) {
return FALSE;
}
EntryFlags = BusyBlock->Flags;
FreeBlock = (PHEAP_FREE_ENTRY)(BusyBlock + BusyBlock->Size);
if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
FreeSize = (AllocationIndex - BusyBlock->Size) << HEAP_GRANULARITY_SHIFT;
FreeSize = ROUND_UP_TO_POWER2( FreeSize, PAGE_SIZE );
FreeBlock = RtlpFindAndCommitPages( Heap,
Heap->Segments[ BusyBlock->SegmentIndex ],
&FreeSize,
(PHEAP_ENTRY)FreeBlock
);
if (FreeBlock == NULL) {
return FALSE;
}
FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
FreeBlock = RtlpCoalesceFreeBlocks( Heap, FreeBlock, &FreeSize, FALSE );
FreeFlags = FreeBlock->Flags;
if ((FreeSize + BusyBlock->Size) < AllocationIndex) {
RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
Heap->TotalFreeSize += FreeSize;
if (DEBUG_HEAP(Flags)) {
RtlpValidateHeapHeaders( Heap, TRUE );
}
return FALSE;
}
FreeSize += BusyBlock->Size;
}
else {
FreeFlags = FreeBlock->Flags;
if (FreeFlags & HEAP_ENTRY_BUSY) {
return FALSE;
}
FreeSize = BusyBlock->Size + FreeBlock->Size;
if (FreeSize < AllocationIndex) {
return FALSE;
}
RtlpRemoveFreeBlock( Heap, FreeBlock );
Heap->TotalFreeSize -= FreeBlock->Size;
}
OldSize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) -
BusyBlock->UnusedBytes;
FreeSize -= AllocationIndex;
if (FreeSize <= 2) {
AllocationIndex += FreeSize;
FreeSize = 0;
}
if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) {
OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
*NewExtraStuff = *OldExtraStuff;
if (IS_HEAP_TAGGING_ENABLED()) {
NewExtraStuff->TagIndex =
RtlpUpdateTagEntry( Heap,
NewExtraStuff->TagIndex,
BusyBlock->Size,
AllocationIndex,
ReAllocationAction
);
}
}
else
if (IS_HEAP_TAGGING_ENABLED()) {
BusyBlock->SmallTagIndex = (UCHAR)
RtlpUpdateTagEntry( Heap,
BusyBlock->SmallTagIndex,
BusyBlock->Size,
AllocationIndex,
ReAllocationAction
);
}
if (FreeSize == 0) {
BusyBlock->Flags |= FreeFlags & HEAP_ENTRY_LAST_ENTRY;
BusyBlock->Size = (USHORT)AllocationIndex;
BusyBlock->UnusedBytes = (UCHAR)
((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
(BusyBlock + BusyBlock->Size)->PreviousSize = BusyBlock->Size;
}
}
else {
BusyBlock->Size = (USHORT)AllocationIndex;
BusyBlock->UnusedBytes = (UCHAR)
((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
SplitBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)BusyBlock + AllocationIndex);
SplitBlock->PreviousSize = (USHORT)AllocationIndex;
SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
SplitBlock->Flags = FreeFlags;
SplitBlock->Size = (USHORT)FreeSize;
RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
Heap->TotalFreeSize += FreeSize;
}
else {
SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
SplitBlock->Flags = FreeFlags & (~HEAP_ENTRY_LAST_ENTRY);
SplitBlock->Size = (USHORT)FreeSize;
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
}
RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
Heap->TotalFreeSize += FreeSize;
}
else {
FreeFlags = SplitBlock2->Flags;
RtlpRemoveFreeBlock( Heap, SplitBlock2 );
Heap->TotalFreeSize -= SplitBlock2->Size;
FreeSize += SplitBlock2->Size;
SplitBlock->Flags = FreeFlags;
if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
SplitBlock->Size = (USHORT)FreeSize;
if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
}
RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
Heap->TotalFreeSize += FreeSize;
}
else {
RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
}
}
}
}
if (Flags & HEAP_ZERO_MEMORY) {
if (Size > OldSize) {
RtlZeroMemory( (PCHAR)(BusyBlock + 1) + OldSize,
Size - OldSize
);
}
}
else
if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
ULONG 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
);
}
BusyBlock->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
BusyBlock->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
return TRUE;
}
PVOID
RtlReAllocateHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
IN ULONG Size
)
{
PHEAP Heap = (PHEAP)HeapHandle;
ULONG AllocationSize;
PHEAP_ENTRY BusyBlock, NewBusyBlock;
PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
ULONG FreeSize;
BOOLEAN LockAcquired;
PVOID NewBaseAddress;
PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
ULONG OldSize;
ULONG AllocationIndex;
ULONG OldAllocationIndex;
UCHAR FreeFlags;
NTSTATUS Status;
PVOID DeCommitAddress;
ULONG DeCommitSize;
EXCEPTION_RECORD ExceptionRecord;
#if ENABLE_HEAP_EVENT_LOGGING
PVOID OldBaseAddress = BaseAddress;
#endif // ENABLE_HEAP_EVENT_LOGGING
if (BaseAddress == NULL) {
SET_LAST_STATUS( STATUS_SUCCESS );
return NULL;
}
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags)) {
return RtlDebugReAllocateHeap( HeapHandle, Flags, BaseAddress, Size );
}
if (Size > 0x7fffffff) {
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) {
AllocationSize += sizeof( HEAP_ENTRY_EXTRA );
}
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
Flags ^= HEAP_NO_SERIALIZE;
}
else {
LockAcquired = FALSE;
}
try { try {
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
//
// Bail if not a busy block.
//
leave;
}
else
if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
OldSize = RtlpGetSizeOfBigBlock( BusyBlock );
OldAllocationIndex = (OldSize + BusyBlock->Size) >> HEAP_GRANULARITY_SHIFT;
AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
AllocationSize = ROUND_UP_TO_POWER2( AllocationSize, PAGE_SIZE );
}
else {
OldAllocationIndex = BusyBlock->Size;
OldSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) -
BusyBlock->UnusedBytes;
}
AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
//
// See if new size less than or equal to the current size.
//
if (AllocationIndex <= OldAllocationIndex) {
if (AllocationIndex + 1 == OldAllocationIndex) {
AllocationIndex += 1;
AllocationSize += sizeof( HEAP_ENTRY );
}
//
// Then shrinking block. Calculate new residual amount and fill
// in the tail padding if enabled.
//
if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
BusyBlock->Size = (USHORT)(AllocationSize - Size);
}
else
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
*NewExtraStuff = *OldExtraStuff;
if (IS_HEAP_TAGGING_ENABLED()) {
NewExtraStuff->TagIndex =
RtlpUpdateTagEntry( Heap,
NewExtraStuff->TagIndex,
OldAllocationIndex,
AllocationIndex,
ReAllocationAction
);
}
BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
}
else {
if (IS_HEAP_TAGGING_ENABLED()) {
BusyBlock->SmallTagIndex = (UCHAR)
RtlpUpdateTagEntry( Heap,
BusyBlock->SmallTagIndex,
BusyBlock->Size,
AllocationIndex,
ReAllocationAction
);
}
BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
}
//
// If block is getting bigger, then fill in the extra
// space.
//
if (Size > OldSize) {
if (Flags & HEAP_ZERO_MEMORY) {
RtlZeroMemory( (PCHAR)BaseAddress + OldSize,
Size - OldSize
);
}
else
if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
ULONG 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 %x for %x 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 (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
SplitBlock->Size = (USHORT)FreeSize;
RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
Heap->TotalFreeSize += FreeSize;
}
else {
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 {
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;
}
RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
Heap->TotalFreeSize += FreeSize;
}
else {
RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
}
}
}
}
}
}
else {
if ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
!RtlpGrowBlockInPlace( Heap, Flags, BusyBlock, Size, AllocationIndex )
) {
//
// Otherwise growing block, so 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;
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) {
Flags |= BusyBlock->SmallTagIndex << HEAP_TAG_SHIFT;
}
NewBaseAddress = RtlAllocateHeap( HeapHandle,
Flags & ~HEAP_ZERO_MEMORY,
Size
);
if (NewBaseAddress != NULL) {
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 {
NewExtraStuff->ZeroInit = 0;
}
}
RtlMoveMemory( NewBaseAddress, BaseAddress, OldSize );
if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) {
RtlZeroMemory( (PCHAR)NewBaseAddress + OldSize,
Size - OldSize
);
}
RtlFreeHeap( HeapHandle,
Flags,
BaseAddress
);
}
BaseAddress = NewBaseAddress;
}
}
}
#if ENABLE_HEAP_EVENT_LOGGING
if (RtlAreLogging( Heap->EventLogMask )) {
RtlLogEvent( RtlpReAllocHeapEventId,
Heap->EventLogMask,
Heap,
Flags,
OldBaseAddress,
OldSize,
Size,
BaseAddress
);
}
#endif // ENABLE_HEAP_EVENT_LOGGING
//
// Unlock the heap
//
if (LockAcquired) {
LockAcquired = FALSE;
RtlReleaseLockRoutine( Heap->LockVariable );
}
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 {
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
}
return BaseAddress;
}
BOOLEAN
RtlValidateProcessHeaps( VOID )
{
ULONG i, NumberOfHeaps;
PVOID Heaps[ 128 ];
BOOLEAN Result;
Result = TRUE;
NumberOfHeaps = RtlGetProcessHeaps( 128, Heaps );
for (i=0; i<NumberOfHeaps; i++) {
if (!RtlValidateHeap( Heaps[i], 0, NULL )) {
Result = FALSE;
}
}
return Result;
}
BOOLEAN
RtlValidateHeap(
PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress
)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN LockAcquired;
BOOLEAN Result;
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapValidate( HeapHandle, Flags, BaseAddress )
);
LockAcquired = FALSE;
Result = FALSE;
try {
//
// Validate that HeapAddress points to a HEAP structure.
//
if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlValidateHeap" )) {
Flags |= Heap->ForceFlags;
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
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;
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Result;
}
BOOLEAN
RtlSetUserValueHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
IN PVOID UserValue
)
{
PHEAP Heap = (PHEAP)HeapHandle;
PHEAP_ENTRY BusyBlock;
PHEAP_ENTRY_EXTRA ExtraStuff;
BOOLEAN LockAcquired;
BOOLEAN Result;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugSetUserValueHeap( HeapHandle, Flags, BaseAddress, UserValue );
}
Result = FALSE;
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
}
else
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
ExtraStuff->Settable = (ULONG)UserValue;
Result = TRUE;
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Result;
}
BOOLEAN
RtlGetUserInfoHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
OUT PVOID *UserValue OPTIONAL,
OUT PULONG UserFlags OPTIONAL
)
{
PHEAP Heap = (PHEAP)HeapHandle;
PHEAP_ENTRY BusyBlock;
PHEAP_ENTRY_EXTRA ExtraStuff;
BOOLEAN LockAcquired;
BOOLEAN Result;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugGetUserInfoHeap( HeapHandle, Flags, BaseAddress, UserValue, UserFlags );
}
Result = FALSE;
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
try {
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
}
else {
if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
if (ARGUMENT_PRESENT( UserValue )) {
*UserValue = (PVOID)ExtraStuff->Settable;
}
}
if (ARGUMENT_PRESENT( UserFlags )) {
*UserFlags = (BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
}
Result = TRUE;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
SET_LAST_STATUS( GetExceptionCode() );
Result = FALSE;
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Result;
}
BOOLEAN
RtlSetUserFlagsHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
IN ULONG UserFlagsReset,
IN ULONG UserFlagsSet
)
{
PHEAP Heap = (PHEAP)HeapHandle;
PHEAP_ENTRY BusyBlock;
BOOLEAN LockAcquired, Result;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugSetUserFlagsHeap( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet );
}
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
try {
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
}
else {
BusyBlock->Flags &= ~(UserFlagsReset >> 4);
BusyBlock->Flags |= (UserFlagsSet >> 4);
Result = TRUE;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
SET_LAST_STATUS( GetExceptionCode() );
Result = FALSE;
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Result;
}
ULONG
RtlSizeHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress
)
{
PHEAP Heap = (PHEAP)HeapHandle;
PHEAP_ENTRY BusyBlock;
ULONG BusySize;
BOOLEAN LockAcquired;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugSizeHeap( HeapHandle, Flags, BaseAddress );
}
//
// No lock is required since nothing is modified and nothing
// outside the busy block is read.
//
BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
BusySize = (ULONG)-1;
SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
} else if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
BusySize = RtlpGetSizeOfBigBlock( BusyBlock );
} else {
BusySize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) -
BusyBlock->UnusedBytes;
}
return BusySize;
}
NTSTATUS
RtlExtendHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID Base,
IN ULONG Size
)
{
PHEAP Heap = (PHEAP)HeapHandle;
NTSTATUS Status;
PHEAP_SEGMENT Segment;
BOOLEAN LockAcquired;
UCHAR SegmentIndex, EmptySegmentIndex;
ULONG CommitSize;
ULONG ReserveSize;
ULONG SegmentFlags;
PVOID CommittedBase;
PVOID UnCommittedBase;
MEMORY_BASIC_INFORMATION MemoryInformation;
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapExtend( HeapHandle, Flags, Base, Size )
);
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 (MemoryInformation.BaseAddress != Base) {
MemoryInformation.BaseAddress = (PCHAR)MemoryInformation.BaseAddress + PAGE_SIZE;
MemoryInformation.RegionSize -= PAGE_SIZE;
}
//
// Lock the free list.
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
Status = STATUS_INSUFFICIENT_RESOURCES;
EmptySegmentIndex = HEAP_MAXIMUM_SEGMENTS;
for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
Segment = Heap->Segments[ SegmentIndex ];
if (Segment) {
if ((ULONG)Base >= (ULONG)Segment &&
(ULONG)Base < (ULONG)(Segment->LastValidEntry)
) {
Status = STATUS_INVALID_PARAMETER;
break;
}
}
else
if (Segment == NULL && EmptySegmentIndex == HEAP_MAXIMUM_SEGMENTS) {
EmptySegmentIndex = SegmentIndex;
Status = STATUS_SUCCESS;
}
}
if (NT_SUCCESS( Status )) {
SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
CommittedBase = MemoryInformation.BaseAddress;
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 {
UnCommittedBase = CommittedBase;
ReserveSize = MemoryInformation.RegionSize;
}
if (ReserveSize < PAGE_SIZE ||
Size > ReserveSize
) {
Status = STATUS_BUFFER_TOO_SMALL;
}
else {
if (UnCommittedBase == CommittedBase) {
CommitSize = PAGE_SIZE;
Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
(PVOID *)&Segment,
0,
&CommitSize,
MEM_COMMIT,
PAGE_READWRITE
);
}
}
if (NT_SUCCESS( Status )) {
if (RtlpInitializeHeapSegment( Heap,
Segment,
EmptySegmentIndex,
0,
Segment,
(PCHAR)Segment + CommitSize,
(PCHAR)Segment + ReserveSize
)
) {
Status = STATUS_NO_MEMORY;
}
}
}
if (LockAcquired) {
LockAcquired = FALSE;
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Status;
}
ULONG
RtlCompactHeap(
IN PVOID HeapHandle,
IN ULONG Flags
)
{
PHEAP Heap = (PHEAP)HeapHandle;
PHEAP_FREE_ENTRY FreeBlock;
PHEAP_SEGMENT Segment;
UCHAR SegmentIndex;
ULONG LargestFreeSize;
BOOLEAN LockAcquired;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugCompactHeap( HeapHandle, Flags );
}
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
LargestFreeSize = 0;
try {
FreeBlock = RtlpCoalesceHeap( (PHEAP)HeapHandle );
if (FreeBlock != NULL) {
LargestFreeSize = FreeBlock->Size << HEAP_GRANULARITY_SHIFT;
}
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() );
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return LargestFreeSize;
}
HEAP RtlpGlobalTagHeap;
PHEAP_TAG_ENTRY
RtlpAllocateTags(
PHEAP Heap,
ULONG NumberOfTags
)
{
NTSTATUS Status;
ULONG TagIndex, ReserveSize, CommitSize;
PHEAP_TAG_ENTRY TagEntry;
USHORT CreatorBackTraceIndex;
USHORT MaximumTagIndex;
USHORT TagIndexFlag;
if (Heap == NULL) {
RtlpGlobalTagHeap.Signature = HEAP_SIGNATURE;
TagIndexFlag = HEAP_GLOBAL_TAG;
Heap = &RtlpGlobalTagHeap;
}
else {
TagIndexFlag = 0;
}
#if i386
if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
}
else
#endif // i386
CreatorBackTraceIndex = 0;
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
}
if (NumberOfTags > (ULONG)(Heap->MaximumTagIndex - Heap->NextAvailableTagIndex)) {
return NULL;
}
TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
for (TagIndex = Heap->NextAvailableTagIndex;
TagIndex < Heap->NextAvailableTagIndex + NumberOfTags;
TagIndex++
) {
if (((ULONG)TagEntry & (PAGE_SIZE-1)) == 0) {
CommitSize = PAGE_SIZE;
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&TagEntry,
0,
&CommitSize,
MEM_COMMIT,
PAGE_READWRITE
);
if (!NT_SUCCESS( Status )) {
return NULL;
}
}
TagEntry->TagIndex = (USHORT)TagIndex | TagIndexFlag;
TagEntry->CreatorBackTraceIndex = CreatorBackTraceIndex;
TagEntry += 1;
}
TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
Heap->NextAvailableTagIndex += (USHORT)NumberOfTags;
return TagEntry;
}
static WCHAR RtlpPseudoTagNameBuffer[ 24 ];
PWSTR
RtlpGetTagName(
PHEAP Heap,
USHORT TagIndex
)
{
if (TagIndex != 0) {
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG &&
Heap->PseudoTagEntries != NULL
) {
if (TagIndex == 0) {
swprintf( RtlpPseudoTagNameBuffer, L"Objects>%4u",
HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT
);
}
else
if (TagIndex < HEAP_MAXIMUM_FREELISTS) {
swprintf( RtlpPseudoTagNameBuffer, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT );
}
else {
swprintf( RtlpPseudoTagNameBuffer, L"VirtualAlloc" );
}
return RtlpPseudoTagNameBuffer;
}
}
else
if (TagIndex & HEAP_GLOBAL_TAG) {
TagIndex &= ~HEAP_GLOBAL_TAG;
if (TagIndex < RtlpGlobalTagHeap.NextAvailableTagIndex &&
RtlpGlobalTagHeap.TagEntries != NULL
) {
return RtlpGlobalTagHeap.TagEntries[ TagIndex ].TagName;
}
}
else
if (TagIndex < Heap->NextAvailableTagIndex &&
Heap->TagEntries != NULL
) {
return Heap->TagEntries[ TagIndex ].TagName;
}
}
return NULL;
}
USHORT
RtlpUpdateTagEntry(
PHEAP Heap,
USHORT TagIndex,
ULONG OldSize, // Only valid for ReAllocation and Free actions
ULONG NewSize, // Only valid for ReAllocation and Allocation actions
HEAP_TAG_ACTION Action
)
{
PHEAP_TAG_ENTRY TagEntry;
if (Action >= FreeAction) {
if (TagIndex == 0) {
return 0;
}
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;
}
}
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;
}
}
else
if (TagIndex < Heap->NextAvailableTagIndex &&
Heap->TagEntries != NULL
) {
TagEntry = &Heap->TagEntries[ TagIndex ];
}
else {
return 0;
}
TagEntry->Frees += 1;
TagEntry->Size -= OldSize;
if (Action >= ReAllocationAction) {
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 {
if (TagIndex != 0 &&
TagIndex < Heap->NextAvailableTagIndex &&
Heap->TagEntries != NULL
) {
TagEntry = &Heap->TagEntries[ TagIndex ];
}
else
if (TagIndex & HEAP_GLOBAL_TAG) {
TagIndex &= ~HEAP_GLOBAL_TAG;
Heap = &RtlpGlobalTagHeap;
if (TagIndex < Heap->NextAvailableTagIndex &&
Heap->TagEntries != NULL
) {
TagEntry = &Heap->TagEntries[ TagIndex ];
TagIndex |= HEAP_GLOBAL_TAG;
}
else {
return 0;
}
}
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 {
return 0;
}
TagEntry->Allocs += 1;
TagEntry->Size += NewSize;
}
return TagIndex;
}
VOID
RtlpDestroyTags(
PHEAP Heap
)
{
NTSTATUS Status;
ULONG RegionSize;
if (Heap->TagEntries != NULL) {
RegionSize = 0;
Status = NtFreeVirtualMemory( NtCurrentProcess(),
&Heap->TagEntries,
&RegionSize,
MEM_RELEASE
);
if (NT_SUCCESS( Status )) {
Heap->TagEntries = NULL;
}
}
}
ULONG
RtlCreateTagHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN PWSTR TagPrefix OPTIONAL,
IN PWSTR TagNames
)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN LockAcquired;
ULONG TagIndex;
ULONG NumberOfTags, MaxTagNameLength, TagPrefixLength;
PWSTR s, s1, HeapName;
PHEAP_TAG_ENTRY TagEntry;
if (!IS_HEAP_TAGGING_ENABLED()) {
return 0;
}
LockAcquired = FALSE;
if (Heap != NULL) {
IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, 0 );
//
// Validate that HeapAddress points to a HEAP structure.
//
if (DEBUG_HEAP( Flags )) {
return RtlDebugCreateTagHeap( HeapHandle, Flags, TagPrefix, TagNames );
}
//
// Lock the heap
//
Flags |= Heap->ForceFlags;
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
}
TagIndex = 0;
NumberOfTags = 0;
if (*TagNames == L'!') {
HeapName = TagNames + 1;
while (*TagNames++) {
}
}
else {
HeapName = NULL;
}
s = TagNames;
while (*s) {
while (*s++) {
}
NumberOfTags += 1;
}
if (NumberOfTags > 0) {
TagEntry = RtlpAllocateTags( Heap, NumberOfTags );
if (TagEntry != NULL) {
MaxTagNameLength = (sizeof( TagEntry->TagName ) / sizeof( WCHAR )) - 1;
TagIndex = TagEntry->TagIndex;
if (TagIndex == 0) {
if (HeapName != NULL ) {
wcsncpy( TagEntry->TagName, HeapName, MaxTagNameLength );
}
TagEntry += 1;
TagIndex = TagEntry->TagIndex;
}
else
if (TagIndex == HEAP_GLOBAL_TAG) {
wcsncpy( TagEntry->TagName, L"GlobalTags", MaxTagNameLength );
TagEntry += 1;
TagIndex = TagEntry->TagIndex;
}
if (ARGUMENT_PRESENT( TagPrefix ) && (TagPrefixLength = wcslen( TagPrefix ))) {
if (TagPrefixLength >= MaxTagNameLength-4) {
TagPrefix = NULL;
}
else {
MaxTagNameLength -= TagPrefixLength;
}
}
else {
TagPrefix = NULL;
}
s = TagNames;
while (*s) {
s1 = TagEntry->TagName;
if (ARGUMENT_PRESENT( TagPrefix )) {
wcscpy( s1, TagPrefix );
s1 += TagPrefixLength;
}
wcsncpy( s1, s, MaxTagNameLength );
while (*s++) {
}
TagEntry += 1;
}
}
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return TagIndex << HEAP_TAG_SHIFT;
}
PWSTR
RtlQueryTagHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN USHORT TagIndex,
IN BOOLEAN ResetCounters,
OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN LockAcquired;
PHEAP_TAG_ENTRY TagEntry;
PWSTR Result;
IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, NULL );
if (!IS_HEAP_TAGGING_ENABLED()) {
return NULL;
}
LockAcquired = FALSE;
if (Heap != NULL) {
//
// Validate that HeapAddress points to a HEAP structure.
//
if (DEBUG_HEAP( Flags )) {
return RtlDebugQueryTagHeap( HeapHandle, Flags, TagIndex, ResetCounters, TagInfo );
}
//
// Lock the heap
//
Flags |= Heap->ForceFlags;
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
}
Result = NULL;
if (TagIndex < Heap->NextAvailableTagIndex && Heap->TagEntries != NULL) {
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) {
TagEntry->Allocs = 0;
TagEntry->Frees = 0;
TagEntry->Size = 0;
}
Result = &TagEntry->TagName[ 0 ];
}
else
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);
if (ARGUMENT_PRESENT( TagInfo )) {
TagInfo->NumberOfAllocations = TagEntry->Allocs;
TagInfo->NumberOfFrees = TagEntry->Frees;
TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
}
if (ResetCounters) {
TagEntry->Allocs = 0;
TagEntry->Frees = 0;
TagEntry->Size = 0;
}
Result = L"";
}
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Result;
}
typedef struct _RTL_HEAP_USAGE_INTERNAL {
PVOID Base;
ULONG ReservedSize;
ULONG CommittedSize;
PRTL_HEAP_USAGE_ENTRY FreeList;
PRTL_HEAP_USAGE_ENTRY LargeEntriesSentinal;
ULONG Reserved;
} RTL_HEAP_USAGE_INTERNAL, *PRTL_HEAP_USAGE_INTERNAL;
NTSTATUS
RtlpAllocateHeapUsageEntry(
PRTL_HEAP_USAGE_INTERNAL Buffer,
PRTL_HEAP_USAGE_ENTRY *pp
)
{
NTSTATUS Status;
PRTL_HEAP_USAGE_ENTRY p;
PVOID CommitAddress;
ULONG PageSize;
if (Buffer->FreeList == NULL) {
if (Buffer->CommittedSize >= Buffer->ReservedSize) {
return STATUS_NO_MEMORY;
}
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;
Buffer->FreeList = CommitAddress;
p = Buffer->FreeList;
while (PageSize != 0) {
p->Next = (p+1);
p += 1;
PageSize -= sizeof( *p );
}
p -= 1;
p->Next = NULL;
}
p = Buffer->FreeList;
Buffer->FreeList = p->Next;
p->Next = NULL;
if (*pp) {
(*pp)->Next = p;
}
*pp = p;
return STATUS_SUCCESS;
}
PRTL_HEAP_USAGE_ENTRY
RtlpFreeHeapUsageEntry(
PRTL_HEAP_USAGE_INTERNAL Buffer,
PRTL_HEAP_USAGE_ENTRY p
)
{
PRTL_HEAP_USAGE_ENTRY pTmp;
if (p != NULL) {
pTmp = p->Next;
p->Next = Buffer->FreeList;
Buffer->FreeList = p;
}
else {
pTmp = NULL;
}
return pTmp;
}
NTSTATUS
RtlUsageHeap(
IN PVOID HeapHandle,
IN ULONG Flags,
IN OUT PRTL_HEAP_USAGE Usage
)
{
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;
ULONG BytesFree;
UCHAR SegmentIndex;
BOOLEAN LockAcquired;
BOOLEAN VirtualAllocBlockSeen;
PRTL_HEAP_USAGE_ENTRY pOldEntries, pNewEntries, pNewEntry;
PRTL_HEAP_USAGE_ENTRY *ppEntries, *ppAddedEntries, *ppRemovedEntries, *pp;
PVOID DataAddress;
ULONG DataSize;
Flags |= Heap->ForceFlags;
if (DEBUG_HEAP( Flags )) {
return RtlDebugUsageHeap( HeapHandle, Flags, Usage );
}
if (Usage->Length != sizeof( RTL_HEAP_USAGE )) {
return STATUS_INFO_LENGTH_MISMATCH;
}
Usage->BytesAllocated = 0;
Usage->BytesCommitted = 0;
Usage->BytesReserved = 0;
Usage->BytesReservedMaximum = 0;
Buffer = (PRTL_HEAP_USAGE_INTERNAL)&Usage->Reserved[ 0 ];
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)) {
Buffer->ReservedSize = 0;
Status = NtFreeVirtualMemory( NtCurrentProcess(),
&Buffer->Base,
&Buffer->ReservedSize,
MEM_RELEASE
);
if (!NT_SUCCESS( Status )) {
return Status;
}
RtlZeroMemory( Buffer, sizeof( *Buffer ) );
}
Flags |= Heap->ForceFlags;
//
// Lock the heap
//
if (!(Flags & HEAP_NO_SERIALIZE)) {
RtlAcquireLockRoutine( Heap->LockVariable );
LockAcquired = TRUE;
}
else {
LockAcquired = FALSE;
}
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);
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;
if (Buffer->Base != NULL && (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
pOldEntries = Usage->Entries;
ppEntries = &Usage->Entries;
*ppEntries = NULL;
ppAddedEntries = &Usage->AddedEntries;
while (*ppAddedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppAddedEntries )) {
}
ppRemovedEntries = &Usage->RemovedEntries;
while (*ppRemovedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppRemovedEntries )) {
}
for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
Segment = Heap->Segments[ SegmentIndex ];
if (Segment) {
if (Segment->BaseAddress == Heap) {
CurrentBlock = &Heap->Entry;
}
else {
CurrentBlock = &Segment->Entry;
}
while (CurrentBlock < Segment->LastValidEntry) {
if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
DataAddress = (CurrentBlock+1);
DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
CurrentBlock->UnusedBytes;
keepLookingAtOldEntries:
if (pOldEntries == Buffer->LargeEntriesSentinal) {
goto keepLookingAtNewEntries;
}
if (pOldEntries->Address == DataAddress &&
pOldEntries->Size == DataSize
) {
//
// Same block, keep in entries list
//
*ppEntries = pOldEntries;
pOldEntries = pOldEntries->Next;
ppEntries = &(*ppEntries)->Next;
*ppEntries = NULL;
}
else
if (pOldEntries->Address <= DataAddress) {
*ppRemovedEntries = pOldEntries;
pOldEntries = pOldEntries->Next;
ppRemovedEntries = &(*ppRemovedEntries)->Next;
*ppRemovedEntries = NULL;
goto keepLookingAtOldEntries;
}
else {
keepLookingAtNewEntries:
pNewEntry = NULL;
Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
if (!NT_SUCCESS( Status )) {
break;
}
pNewEntry->Address = DataAddress;
pNewEntry->Size = DataSize;
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 {
#if i386
pNewEntry->AllocatorBackTraceIndex = 0;
#endif // i386
if (!IS_HEAP_TAGGING_ENABLED()) {
pNewEntry->TagIndex = 0;
}
else {
pNewEntry->TagIndex = CurrentBlock->SmallTagIndex;
}
}
Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
if (!NT_SUCCESS( Status )) {
break;
}
**ppAddedEntries = *pNewEntry;
ppAddedEntries = &(*ppAddedEntries)->Next;
*ppAddedEntries = NULL;
pNewEntry->Next = NULL;
*ppEntries = pNewEntry;
ppEntries = &pNewEntry->Next;
}
}
if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
CurrentBlock += CurrentBlock->Size;
if (CurrentBlock < Segment->LastValidEntry) {
UnCommittedRange = Segment->UnCommittedRanges;
while (UnCommittedRange != NULL && UnCommittedRange->Address != (ULONG)CurrentBlock ) {
UnCommittedRange = UnCommittedRange->Next;
}
if (UnCommittedRange == NULL) {
CurrentBlock = Segment->LastValidEntry;
}
else {
CurrentBlock = (PHEAP_ENTRY)(UnCommittedRange->Address +
UnCommittedRange->Size
);
}
}
}
else {
CurrentBlock += CurrentBlock->Size;
}
}
}
}
if (NT_SUCCESS( Status )) {
Head = &Heap->VirtualAllocdBlocks;
Next = Head->Flink;
VirtualAllocBlockSeen = FALSE;
while (Head != Next) {
VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
pNewEntry = NULL;
Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
if (!NT_SUCCESS( Status )) {
break;
}
VirtualAllocBlockSeen = TRUE;
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;
}
pp = ppEntries;
while (*pp) {
if ((*pp)->Address >= pNewEntry->Address) {
break;
}
pp = &(*pp)->Next;
}
pNewEntry->Next = *pp;
*pp = pNewEntry;
Next = Next->Flink;
}
if (NT_SUCCESS( Status )) {
pOldEntries = Buffer->LargeEntriesSentinal;
Buffer->LargeEntriesSentinal = *ppEntries;
while (pOldEntries != NULL) {
if (*ppEntries != NULL &&
pOldEntries->Address == (*ppEntries)->Address &&
pOldEntries->Size == (*ppEntries)->Size
) {
ppEntries = &(*ppEntries)->Next;
pOldEntries = RtlpFreeHeapUsageEntry( Buffer, pOldEntries );
}
else
if (*ppEntries == NULL ||
pOldEntries->Address < (*ppEntries)->Address
) {
*ppRemovedEntries = pOldEntries;
pOldEntries = pOldEntries->Next;
ppRemovedEntries = &(*ppRemovedEntries)->Next;
*ppRemovedEntries = NULL;
}
else {
*ppAddedEntries = pOldEntries;
pOldEntries = pOldEntries->Next;
**ppAddedEntries = **ppEntries;
ppAddedEntries = &(*ppAddedEntries)->Next;
*ppAddedEntries = NULL;
}
}
while (pNewEntry = *ppEntries) {
Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
if (!NT_SUCCESS( Status )) {
break;
}
**ppAddedEntries = *pNewEntry;
ppAddedEntries = &(*ppAddedEntries)->Next;
*ppAddedEntries = NULL;
ppEntries = &pNewEntry->Next;
}
if (Usage->AddedEntries != NULL || Usage->RemovedEntries != NULL) {
Status = STATUS_MORE_ENTRIES;
}
}
}
}
//
// Unlock the heap
//
if (LockAcquired) {
RtlReleaseLockRoutine( Heap->LockVariable );
}
return Status;
} // RtlUsageHeap
NTSTATUS
RtlWalkHeap(
IN PVOID HeapHandle,
IN OUT PRTL_HEAP_WALK_ENTRY Entry
)
{
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;
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapWalk( HeapHandle, Entry )
);
if (DEBUG_HEAP( Heap->Flags )) {
if (!RtlDebugWalkHeap( HeapHandle, Entry )) {
return STATUS_INVALID_PARAMETER;
}
}
Status = STATUS_SUCCESS;
if (Entry->DataAddress == NULL) {
SegmentIndex = 0;
nextSegment:
CurrentBlock = NULL;
Segment = NULL;
while (SegmentIndex < HEAP_MAXIMUM_SEGMENTS &&
(Segment = Heap->Segments[ SegmentIndex ]) == NULL
) {
SegmentIndex += 1;
}
if (Segment == NULL) {
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;
}
}
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;
}
}
else
if (Entry->Flags & (RTL_HEAP_SEGMENT | RTL_HEAP_UNCOMMITTED_RANGE)) {
if ((SegmentIndex = Entry->SegmentIndex) >= HEAP_MAXIMUM_SEGMENTS) {
Status = STATUS_INVALID_ADDRESS;
CurrentBlock = NULL;
}
else {
Segment = Heap->Segments[ SegmentIndex ];
if (Segment == NULL) {
Status = STATUS_INVALID_ADDRESS;
CurrentBlock = NULL;
}
else
if (Entry->Flags & RTL_HEAP_SEGMENT) {
CurrentBlock = (PHEAP_ENTRY)Segment->FirstEntry;
}
else {
CurrentBlock = (PHEAP_ENTRY)((PCHAR)Entry->DataAddress + Entry->DataSize);
if (CurrentBlock >= Segment->LastValidEntry) {
SegmentIndex += 1;
goto nextSegment;
}
}
}
}
else {
if (Entry->Flags & HEAP_ENTRY_BUSY) {
CurrentBlock = ((PHEAP_ENTRY)Entry->DataAddress - 1);
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 {
Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
if (Segment == NULL) {
Status = STATUS_INVALID_ADDRESS;
CurrentBlock = NULL;
}
else
if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
findUncommittedRange:
CurrentBlock += CurrentBlock->Size;
if (CurrentBlock >= Segment->LastValidEntry) {
SegmentIndex += 1;
goto nextSegment;
}
pp = &Segment->UnCommittedRanges;
while ((UnCommittedRange = *pp) && UnCommittedRange->Address != (ULONG)CurrentBlock ) {
pp = &UnCommittedRange->Next;
}
if (UnCommittedRange == NULL) {
Status = STATUS_INVALID_PARAMETER;
}
else {
Entry->DataAddress = (PVOID)UnCommittedRange->Address;
Entry->DataSize = UnCommittedRange->Size;
Entry->OverheadBytes = 0;
Entry->SegmentIndex = SegmentIndex;
Entry->Flags = RTL_HEAP_UNCOMMITTED_RANGE;
}
CurrentBlock = NULL;
}
else {
CurrentBlock += CurrentBlock->Size;
}
}
}
else {
CurrentBlock = (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Entry->DataAddress - 1);
Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
if (Segment == NULL) {
Status = STATUS_INVALID_ADDRESS;
CurrentBlock = NULL;
}
else
if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
goto findUncommittedRange;
}
else {
CurrentBlock += CurrentBlock->Size;
}
}
}
if (CurrentBlock != NULL) {
if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
Entry->DataAddress = (CurrentBlock+1);
if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
Entry->DataSize = RtlpGetSizeOfBigBlock( CurrentBlock );
Entry->OverheadBytes = 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 {
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;
}
VOID
RtlProtectHeap(
IN PVOID HeapHandle,
IN BOOLEAN MakeReadOnly
)
{
PHEAP Heap;
UCHAR SegmentIndex;
PHEAP_SEGMENT Segment;
MEMORY_BASIC_INFORMATION VaInfo;
NTSTATUS Status;
PVOID Address;
PVOID ProtectAddress;
ULONG Size;
ULONG OldProtect;
ULONG NewProtect;
Heap = (PHEAP)HeapHandle;
for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
Segment = Heap->Segments[ SegmentIndex ];
if ( Segment ) {
Address = Segment->BaseAddress;
while ((ULONG)Address < (ULONG)(Segment->LastValidEntry)) {
Status = ZwQueryVirtualMemory( NtCurrentProcess(),
Address,
MemoryBasicInformation,
&VaInfo,
sizeof(VaInfo),
NULL
);
if (!NT_SUCCESS( Status )) {
HeapDebugPrint(( "VirtualQuery Failed 0x%08x %x\n", Address, Status ));
return;
}
//
// Found a commited block. No set it's 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)((ULONG)Address + VaInfo.RegionSize);
}
}
}
return;
}
BOOLEAN
RtlpHeapIsLocked(
IN PVOID HeapHandle
)
{
PHEAP Heap;
IF_DEBUG_PAGE_HEAP_THEN_RETURN(
HeapHandle,
RtlpDebugPageHeapIsLocked( HeapHandle )
);
Heap = (PHEAP)HeapHandle;
return (( Heap->LockVariable != NULL ) &&
( Heap->LockVariable->Lock.CriticalSection.OwningThread ||
Heap->LockVariable->Lock.CriticalSection.LockCount != -1 ));
}