diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl/heapdll.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/rtl/heapdll.c')
-rw-r--r-- | private/ntos/rtl/heapdll.c | 2732 |
1 files changed, 2732 insertions, 0 deletions
diff --git a/private/ntos/rtl/heapdll.c b/private/ntos/rtl/heapdll.c new file mode 100644 index 000000000..9b0a1f7f6 --- /dev/null +++ b/private/ntos/rtl/heapdll.c @@ -0,0 +1,2732 @@ +/*++ + +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 )); + } |