diff options
Diffstat (limited to 'private/ntos/rtl/heapdbg.c')
-rw-r--r-- | private/ntos/rtl/heapdbg.c | 1883 |
1 files changed, 1883 insertions, 0 deletions
diff --git a/private/ntos/rtl/heapdbg.c b/private/ntos/rtl/heapdbg.c new file mode 100644 index 000000000..26bdce151 --- /dev/null +++ b/private/ntos/rtl/heapdbg.c @@ -0,0 +1,1883 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + heapdbg.c + +Abstract: + + This module implements a debugging layer on top of heap allocator. + +Author: + + Steve Wood (stevewo) 20-Sep-1994 + +Revision History: + +--*/ + +#include "ntrtlp.h" +#include "heap.h" +#include "heappriv.h" + +BOOLEAN RtlpValidateHeapHdrsEnable = FALSE; // Set to TRUE if headers are being corrupted +BOOLEAN RtlpValidateHeapTagsEnable; // Set to TRUE if tag counts are off and you want to know why + +HEAP_STOP_ON_VALUES RtlpHeapStopOn; + +VOID +RtlpUpdateHeapListIndex( + USHORT OldIndex, + USHORT NewIndex + ) +{ + if (RtlpHeapStopOn.AllocTag.HeapIndex == OldIndex) { + RtlpHeapStopOn.AllocTag.HeapIndex = NewIndex; + } + + if (RtlpHeapStopOn.ReAllocTag.HeapIndex == OldIndex) { + RtlpHeapStopOn.ReAllocTag.HeapIndex = NewIndex; + } + + if (RtlpHeapStopOn.FreeTag.HeapIndex == OldIndex) { + RtlpHeapStopOn.FreeTag.HeapIndex = NewIndex; + } + + return; +} + +struct { + ULONG Offset; + LPSTR Description; +} RtlpHeapHeaderFieldOffsets[] = { + FIELD_OFFSET( HEAP, Entry ), "Entry", + FIELD_OFFSET( HEAP, Signature ), "Signature", + FIELD_OFFSET( HEAP, Flags ), "Flags", + FIELD_OFFSET( HEAP, ForceFlags ), "ForceFlags", + FIELD_OFFSET( HEAP, VirtualMemoryThreshold ), "VirtualMemoryThreshold", + FIELD_OFFSET( HEAP, SegmentReserve ), "SegmentReserve", + FIELD_OFFSET( HEAP, SegmentCommit ), "SegmentCommit", + FIELD_OFFSET( HEAP, DeCommitFreeBlockThreshold ), "DeCommitFreeBlockThreshold", + FIELD_OFFSET( HEAP, DeCommitTotalFreeThreshold ), "DeCommitTotalFreeThreshold", + FIELD_OFFSET( HEAP, TotalFreeSize ), "TotalFreeSize", + FIELD_OFFSET( HEAP, MaximumAllocationSize ), "MaximumAllocationSize", + FIELD_OFFSET( HEAP, ProcessHeapsListIndex ), "ProcessHeapsListIndex", + FIELD_OFFSET( HEAP, HeaderValidateLength ), "HeaderValidateLength", + FIELD_OFFSET( HEAP, HeaderValidateCopy ), "HeaderValidateCopy", + FIELD_OFFSET( HEAP, NextAvailableTagIndex ), "NextAvailableTagIndex", + FIELD_OFFSET( HEAP, MaximumTagIndex ), "MaximumTagIndex", + FIELD_OFFSET( HEAP, TagEntries ), "TagEntries", + FIELD_OFFSET( HEAP, UCRSegments ), "UCRSegments", + FIELD_OFFSET( HEAP, UnusedUnCommittedRanges ), "UnusedUnCommittedRanges", + FIELD_OFFSET( HEAP, AlignRound ), "AlignRound", + FIELD_OFFSET( HEAP, AlignMask ), "AlignMask", + FIELD_OFFSET( HEAP, VirtualAllocdBlocks ), "VirtualAllocdBlocks", + FIELD_OFFSET( HEAP, Segments ), "Segments", + FIELD_OFFSET( HEAP, u ), "FreeListsInUse", + FIELD_OFFSET( HEAP, FreeListsInUseTerminate ), "FreeListsInUseTerminate", + FIELD_OFFSET( HEAP, AllocatorBackTraceIndex ), "AllocatorBackTraceIndex", + FIELD_OFFSET( HEAP, TraceBuffer ), "TraceBuffer", + FIELD_OFFSET( HEAP, EventLogMask ), "EventLogMask", + FIELD_OFFSET( HEAP, PseudoTagEntries ), "PseudoTagEntries", + FIELD_OFFSET( HEAP, FreeLists ), "FreeLists", + FIELD_OFFSET( HEAP, LockVariable ), "LockVariable", + FIELD_OFFSET( HEAP, Reserved ), "Reserved", + sizeof( HEAP ), "Uncommitted Ranges", + 0xFFFF, NULL +}; + +BOOLEAN +RtlpValidateHeapHeaders( + IN PHEAP Heap, + IN BOOLEAN Recompute + ) +{ + ULONG i, n, nEqual; + NTSTATUS Status; + + if (!RtlpValidateHeapHdrsEnable) { + return TRUE; + } + + if (Heap->HeaderValidateCopy == NULL) { + n = Heap->HeaderValidateLength; + Status = NtAllocateVirtualMemory( NtCurrentProcess(), + &Heap->HeaderValidateCopy, + 0, + &n, + MEM_COMMIT, + PAGE_READWRITE + ); + if (!NT_SUCCESS( Status )) { + return TRUE; + } + + Recompute = TRUE; + } + + n = Heap->HeaderValidateLength; + if (!Recompute) { + nEqual = RtlCompareMemory( Heap, + Heap->HeaderValidateCopy, + n + ); + } + else { + RtlMoveMemory( Heap->HeaderValidateCopy, + Heap, + n + ); + nEqual = n; + } + + if (n != nEqual) { + HeapDebugPrint(( "Heap %x - headers modified (%x is %x instead of %x)\n", + Heap, + (PCHAR)Heap + nEqual, + *(PULONG)((PCHAR)Heap + nEqual), + *(PULONG)((PCHAR)Heap->HeaderValidateCopy + nEqual) + )); + for (i=0; RtlpHeapHeaderFieldOffsets[ i ].Description != NULL; i++) { + if (nEqual >= RtlpHeapHeaderFieldOffsets[ i ].Offset && + nEqual < RtlpHeapHeaderFieldOffsets[ i+1 ].Offset + ) { + DbgPrint( " This is located in the %s field of the heap header.\n", + RtlpHeapHeaderFieldOffsets[ i ].Description + ); + break; + } + } + + return FALSE; + } + else { + return TRUE; + } +} + + +PRTL_TRACE_BUFFER +RtlpHeapCreateTraceBuffer( + IN PHEAP Heap + ) +{ + if (Heap->Flags & HEAP_CREATE_ENABLE_TRACING) { + if (Heap->TraceBuffer == NULL) { + Heap->TraceBuffer = RtlCreateTraceBuffer( 0x10000, HEAP_TRACE_MAX_EVENT ); + if (Heap->TraceBuffer != NULL) { + DbgPrint( "Created Trace buffer (%x) for heap (%x)\n", Heap->TraceBuffer, Heap ); + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_ALLOC ] = "Alloc - Result: %08x Flags: %04x Size: %08x "; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_REALLOC ] = "ReAlloc - Block: %08x Flags: %04x Size: %08x Result: %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_FREE ] = "Free - Block: %08x Flags: %04x Size: %08x Result: %02x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SIZE ] = "Size - Block: %08x Flags: %04x Result: %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_GET_INFO ] = "GetInfo - Block: %08x Flags: %04x Value: %08x UserFlags: %08x Result: %u"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SET_VALUE ] = "SetValue - Block: %08x Flags: %04x Value: %08x Result: %u"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SET_FLAGS ] = "SetFlags - Block: %08x Flags: %04x Reset: %02x Set: %02x Result: %u"; +#if DBG + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_MEMORY ] = " Commit VA - %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_INSERT ] = " Commit Insert - %08x %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_NEW_ENTRY ] = " Commit NewEntry - %08x %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_INSERT_FREE_BLOCK ] = " Insert Free - %08x %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_UNLINK_FREE_BLOCK ] = " Unlink Free - %08x %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COALESCE_FREE_BLOCKS ] = " Coalesce Free - %08x %08x %08x %08x %08x %08x %08x"; + Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_EXTEND_HEAP ] = " Extended Heap - Size: %08x Pages: %08x Commit: %08x"; +#endif // DBG + RtlpValidateHeapHeaders( Heap, TRUE ); + } + } + } + + return Heap->TraceBuffer; +} + +PVOID +RtlDebugCreateHeap( + IN ULONG Flags, + IN PVOID HeapBase OPTIONAL, + IN ULONG ReserveSize OPTIONAL, + IN ULONG CommitSize OPTIONAL, + IN PVOID Lock OPTIONAL, + IN PRTL_HEAP_PARAMETERS Parameters + ) +{ + PHEAP Heap; + NTSTATUS Status; + MEMORY_BASIC_INFORMATION MemoryInformation; + + if (ReserveSize <= sizeof( HEAP_ENTRY )) { + HeapDebugPrint(( "Invalid ReserveSize parameter - %lx\n", ReserveSize )); + HeapDebugBreak( NULL ); + return NULL; + } + + if (ReserveSize < CommitSize) { + HeapDebugPrint(( "Invalid CommitSize parameter - %lx\n", CommitSize )); + HeapDebugBreak( NULL ); + return NULL; + } + + if ((Flags & HEAP_NO_SERIALIZE) && ARGUMENT_PRESENT( Lock )) { + HeapDebugPrint(( "May not specify Lock parameter with HEAP_NO_SERIALIZE\n" )); + HeapDebugBreak( NULL ); + return NULL; + } + + if (ARGUMENT_PRESENT( HeapBase )) { + Status = NtQueryVirtualMemory( NtCurrentProcess(), + HeapBase, + MemoryBasicInformation, + &MemoryInformation, + sizeof( MemoryInformation ), + NULL + ); + if (!NT_SUCCESS( Status )) { + HeapDebugPrint(( "Specified HeapBase (%lx) invalid, Status = %lx\n", + HeapBase, + Status + )); + HeapDebugBreak( NULL ); + return NULL; + } + + if (MemoryInformation.BaseAddress != HeapBase) { + HeapDebugPrint(( "Specified HeapBase (%lx) != to BaseAddress (%lx)\n", + HeapBase, + MemoryInformation.BaseAddress + )); + HeapDebugBreak( NULL ); + return NULL; + } + + if (MemoryInformation.State == MEM_FREE) { + HeapDebugPrint(( "Specified HeapBase (%lx) is free or not writable\n", + MemoryInformation.BaseAddress + )); + HeapDebugBreak( NULL ); + return NULL; + } + } + + Heap = RtlCreateHeap( Flags | + HEAP_SKIP_VALIDATION_CHECKS | + HEAP_TAIL_CHECKING_ENABLED | + HEAP_FREE_CHECKING_ENABLED, + HeapBase, + ReserveSize, + CommitSize, + Lock, + Parameters + ); + if (Heap != NULL) { +#if i386 + if ((NtGlobalFlag & FLG_USER_STACK_TRACE_DB)) { + Heap->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace(); + } +#endif // i386 + + RtlpValidateHeapHeaders( Heap, TRUE ); + } + + return Heap; +} + + + +BOOLEAN +RtlpSerializeHeap( + IN PVOID HeapHandle + ) +{ + NTSTATUS Status; + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_LOCK Lock; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapSerialize( HeapHandle ) + ); + + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlpSerializeHeap" )) { + return FALSE; + } + + // + // Lock the heap. + // + + if (Heap->Flags & HEAP_NO_SERIALIZE) { + Lock = RtlAllocateHeap( HeapHandle, HEAP_NO_SERIALIZE, sizeof( *Lock ) ); + Status = RtlInitializeLockRoutine( Lock ); + if (!NT_SUCCESS( Status )) { + RtlFreeHeap( HeapHandle, HEAP_NO_SERIALIZE, Lock ); + return FALSE; + } + + Heap->LockVariable = Lock; + Heap->Flags &= ~HEAP_NO_SERIALIZE; + Heap->ForceFlags &= ~HEAP_NO_SERIALIZE; + RtlpValidateHeapHeaders( Heap, TRUE ); + } + + return TRUE; +} + + + +BOOLEAN +RtlDebugDestroyHeap( + IN PVOID HeapHandle + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + LIST_ENTRY ListEntry; + ULONG n; + + if (HeapHandle == NtCurrentPeb()->ProcessHeap) { + HeapDebugPrint(( "May not destroy the process heap at %x\n", HeapHandle )); + return FALSE; + } + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlDestroyHeap" )) { + return FALSE; + } + + if (!RtlpValidateHeap( Heap, FALSE )) { + return FALSE; + } + + // + // Now mark the heap as invalid by zeroing the signature field. + // + + Heap->Signature = 0; + + if (Heap->HeaderValidateCopy != NULL) { + n = 0; + NtFreeVirtualMemory( NtCurrentProcess(), + &Heap->HeaderValidateCopy, + &n, + MEM_RELEASE + ); + } + + return TRUE; +} + + +PVOID +RtlDebugAllocateHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN ULONG Size + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN LockAcquired; + PVOID ReturnValue; + ULONG AllocationSize; + USHORT TagIndex; + PHEAP_ENTRY BusyBlock; + PHEAP_ENTRY_EXTRA ExtraStuff; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapAllocate( HeapHandle, Flags, Size ) + ); + + LockAcquired = FALSE; + + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlAllocateHeap" )) { + return NULL; + } + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Verify that the size did not wrap or exceed the limit for this heap. + // + + AllocationSize = (((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask) + + sizeof( HEAP_ENTRY_EXTRA ); + if (AllocationSize < Size || AllocationSize > Heap->MaximumAllocationSize) { + HeapDebugPrint(( "Invalid allocation size - %lx (exceeded %x)\n", + Size, + Heap->MaximumAllocationSize + )); + HeapDebugBreak( NULL ); + return NULL; + } + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + + ReturnValue = RtlAllocateHeapSlowly( HeapHandle, Flags, Size ); + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_ALLOC, + 3, + ReturnValue, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + Size + ) + ); + + RtlpValidateHeapHeaders( Heap, TRUE ); + + if (ReturnValue != NULL) { + BusyBlock = (PHEAP_ENTRY)ReturnValue - 1; + + if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { + ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock ); +#if i386 + if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) { + ExtraStuff->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace(); + } + else { + ExtraStuff->AllocatorBackTraceIndex = 0; + } +#endif // i386 + TagIndex = ExtraStuff->TagIndex; + } + else { + TagIndex = BusyBlock->SmallTagIndex; + } + + if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED) { + RtlpValidateHeap( Heap, FALSE ); + } + } + + if (LockAcquired) { + LockAcquired = FALSE; + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + if (ReturnValue != NULL) { + if ((ULONG)ReturnValue == RtlpHeapStopOn.AllocAddress) { + HeapDebugPrint(( "Just allocated block at %lx for 0x%x bytes\n", + RtlpHeapStopOn.AllocAddress, + Size + )); + HeapDebugBreak( NULL ); + } + else + if (IS_HEAP_TAGGING_ENABLED() && TagIndex != 0 && + TagIndex == RtlpHeapStopOn.AllocTag.TagIndex && + Heap->ProcessHeapsListIndex == RtlpHeapStopOn.AllocTag.HeapIndex + ) { + HeapDebugPrint(( "Just allocated block at %lx for 0x%x bytes with tag %ws\n", + ReturnValue, + Size, + RtlpGetTagName( Heap, TagIndex ) + )); + HeapDebugBreak( NULL ); + } + } + } + except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH : + EXCEPTION_EXECUTE_HANDLER + ) { + SET_LAST_STATUS( GetExceptionCode() ); + ReturnValue = NULL; + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return ReturnValue; +} + + +PVOID +RtlDebugReAllocateHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress, + IN ULONG Size + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + ULONG AllocationSize; + PHEAP_ENTRY BusyBlock; + PHEAP_ENTRY_EXTRA ExtraStuff; + BOOLEAN LockAcquired; + PVOID ReturnValue; + USHORT TagIndex; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapReAllocate( HeapHandle, Flags, BaseAddress, Size ) + ); + + ReturnValue = NULL; + LockAcquired = FALSE; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlReAllocateHeap" )) { + return NULL; + } + Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Verify that the size did not wrap or exceed the limit for this heap. + // + + AllocationSize = (((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask) + + sizeof( HEAP_ENTRY_EXTRA ); + if (AllocationSize < Size || AllocationSize > Heap->MaximumAllocationSize) { + HeapDebugPrint(( "Invalid allocation size - %lx (exceeded %x)\n", + Size, + Heap->MaximumAllocationSize + )); + HeapDebugBreak( NULL ); + return NULL; + } + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlReAllocateHeap" )) { + if ((ULONG)BaseAddress == RtlpHeapStopOn.ReAllocAddress) { + HeapDebugPrint(( "About to reallocate block at %lx to 0x%x bytes\n", + RtlpHeapStopOn.ReAllocAddress, + Size + )); + HeapDebugBreak( NULL ); + } + else + if (IS_HEAP_TAGGING_ENABLED() && RtlpHeapStopOn.ReAllocTag.HeapAndTagIndex != 0) { + if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { + ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock ); + TagIndex = ExtraStuff->TagIndex; + } + else { + TagIndex = BusyBlock->SmallTagIndex; + } + + if (TagIndex != 0 && + TagIndex == RtlpHeapStopOn.ReAllocTag.TagIndex && + Heap->ProcessHeapsListIndex == RtlpHeapStopOn.ReAllocTag.HeapIndex + ) { + HeapDebugPrint(( "About to rellocate block at %lx to 0x%x bytes with tag %ws\n", + BaseAddress, + Size, + RtlpGetTagName( Heap, TagIndex ) + )); + HeapDebugBreak( NULL ); + } + } + + ReturnValue = RtlReAllocateHeap( HeapHandle, Flags, BaseAddress, Size ); + if (ReturnValue != NULL) { + BusyBlock = (PHEAP_ENTRY)ReturnValue - 1; + if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { + ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock ); +#if i386 + if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) { + ExtraStuff->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace(); + } + else { + ExtraStuff->AllocatorBackTraceIndex = 0; + } +#endif // i386 + TagIndex = ExtraStuff->TagIndex; + } + else { + TagIndex = BusyBlock->SmallTagIndex; + } + } + + RtlpValidateHeapHeaders( Heap, TRUE ); + RtlpValidateHeap( Heap, FALSE ); + } + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_REALLOC, + 4, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + Size, + ReturnValue + ) + ); + + if (ReturnValue != NULL) { + if ((ULONG)ReturnValue == RtlpHeapStopOn.ReAllocAddress) { + HeapDebugPrint(( "Just reallocated block at %lx to 0x%x bytes\n", + RtlpHeapStopOn.ReAllocAddress, + Size + )); + HeapDebugBreak( NULL ); + } + else + if (IS_HEAP_TAGGING_ENABLED() && + TagIndex == RtlpHeapStopOn.ReAllocTag.TagIndex && + Heap->ProcessHeapsListIndex == RtlpHeapStopOn.ReAllocTag.HeapIndex + + ) { + HeapDebugPrint(( "Just reallocated block at %lx to 0x%x bytes with tag %ws\n", + ReturnValue, + Size, + RtlpGetTagName( Heap, TagIndex ) + )); + HeapDebugBreak( NULL ); + } + } + } + except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH : + EXCEPTION_EXECUTE_HANDLER + ) { + SET_LAST_STATUS( GetExceptionCode() ); + ReturnValue = NULL; + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return ReturnValue; +} + + +BOOLEAN +RtlDebugFreeHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_ENTRY BusyBlock; + PHEAP_ENTRY_EXTRA ExtraStuff; + ULONG Size; + BOOLEAN Result, LockAcquired; + USHORT TagIndex; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapFree( HeapHandle, Flags, BaseAddress ) + ); + + LockAcquired = FALSE; + Result = FALSE; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlFreeHeap" )) { + return FALSE; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + Size = BusyBlock->Size << HEAP_GRANULARITY_SHIFT; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlFreeHeap" )) { + if ((ULONG)BaseAddress == RtlpHeapStopOn.FreeAddress) { + HeapDebugPrint(( "About to free block at %lx\n", + RtlpHeapStopOn.FreeAddress + )); + HeapDebugBreak( NULL ); + } + else + if (IS_HEAP_TAGGING_ENABLED() && RtlpHeapStopOn.FreeTag.HeapAndTagIndex != 0) { + if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { + ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock ); + TagIndex = ExtraStuff->TagIndex; + } + else { + TagIndex = BusyBlock->SmallTagIndex; + } + + if (TagIndex != 0 && + TagIndex == RtlpHeapStopOn.FreeTag.TagIndex && + Heap->ProcessHeapsListIndex == RtlpHeapStopOn.FreeTag.HeapIndex + ) { + HeapDebugPrint(( "About to free block at %lx with tag %ws\n", + BaseAddress, + RtlpGetTagName( Heap, TagIndex ) + )); + HeapDebugBreak( NULL ); + } + } + + Result = RtlFreeHeapSlowly( HeapHandle, Flags, BaseAddress ); + + RtlpValidateHeapHeaders( Heap, TRUE ); + RtlpValidateHeap( Heap, FALSE ); + } + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_FREE, + 4, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + Size, + (ULONG)Result + ) + ); + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + Result = FALSE; + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Result; +} + + +BOOLEAN +RtlDebugGetUserInfoHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress, + OUT PVOID *UserValue OPTIONAL, + OUT PULONG UserFlags OPTIONAL + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_ENTRY BusyBlock; + BOOLEAN Result, LockAcquired; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapGetUserInfo( HeapHandle, Flags, BaseAddress, UserValue, UserFlags ) + ); + + LockAcquired = FALSE; + Result = FALSE; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlGetUserInfoHeap" )) { + return FALSE; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlGetUserInfoHeap" )) { + Result = RtlGetUserInfoHeap( HeapHandle, Flags, BaseAddress, UserValue, UserFlags ); + } + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_GET_INFO, + 5, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + ARGUMENT_PRESENT( UserValue ) ? *UserValue : 0, + ARGUMENT_PRESENT( UserFlags ) ? *UserFlags : 0, + (ULONG)Result + ) + ); + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Result; +} + + +BOOLEAN +RtlDebugSetUserValueHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress, + IN PVOID UserValue + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_ENTRY BusyBlock; + BOOLEAN Result, LockAcquired; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapSetUserValue( HeapHandle, Flags, BaseAddress, UserValue ) + ); + + LockAcquired = FALSE; + Result = FALSE; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSetUserValueHeap" )) { + return FALSE; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_SET_VALUE, + 4, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + UserValue, + (ULONG)Result + ) + ); + + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSetUserValueHeap" )) { + Result = RtlSetUserValueHeap( HeapHandle, Flags, BaseAddress, UserValue ); + + RtlpValidateHeap( Heap, FALSE ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Result; +} + + +BOOLEAN +RtlDebugSetUserFlagsHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress, + IN ULONG UserFlagsReset, + IN ULONG UserFlagsSet + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_ENTRY BusyBlock; + BOOLEAN Result, LockAcquired; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapSetUserFlags( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet ) + ); + + if (UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS || + UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS + ) { + return FALSE; + } + + LockAcquired = FALSE; + Result = FALSE; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSetUserFlagsHeap" )) { + return FALSE; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_SET_FLAGS, + 5, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + UserFlagsReset, + UserFlagsSet, + (ULONG)TRUE + ) + ); + + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSetUserFlagsHeap" )) { + Result = RtlSetUserFlagsHeap( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet ); + + RtlpValidateHeap( Heap, FALSE ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Result; +} + +ULONG +RtlDebugSizeHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PVOID BaseAddress + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + PHEAP_ENTRY BusyBlock; + BOOLEAN LockAcquired; + ULONG BusySize; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapSize( HeapHandle, Flags, BaseAddress ) + ); + + LockAcquired = FALSE; + BusySize = 0xFFFFFFFF; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSizeHeap" )) { + return FALSE; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + BusyBlock = (PHEAP_ENTRY)BaseAddress - 1; + if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSizeHeap" )) { + BusySize = RtlSizeHeap( HeapHandle, Flags, BaseAddress ); + } + + HeapTrace( Heap, (Heap->TraceBuffer, + HEAP_TRACE_SIZE, + 3, + BaseAddress, + Flags & ~HEAP_SKIP_VALIDATION_CHECKS, + BusySize + ) + ); + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return BusySize; +} + +ULONG +RtlDebugCompactHeap( + IN PVOID HeapHandle, + IN ULONG Flags + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN LockAcquired; + ULONG LargestFreeSize; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapCompact( HeapHandle, Flags ) + ); + + LockAcquired = FALSE; + LargestFreeSize = 0; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlCompactHeap" )) { + return 0; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + RtlpValidateHeap( Heap, FALSE ); + + LargestFreeSize = RtlCompactHeap( HeapHandle, Flags ); + RtlpValidateHeapHeaders( Heap, TRUE ); + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return LargestFreeSize; + + +} + +NTSTATUS +RtlDebugZeroHeap( + IN PVOID HeapHandle, + IN ULONG Flags + ) +{ + NTSTATUS Status; + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN LockAcquired; + ULONG LargestFreeSize; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapZero( HeapHandle, Flags ) + ); + + Status = STATUS_SUCCESS; + LockAcquired = FALSE; + LargestFreeSize = 0; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlZeroHeap" )) { + return STATUS_INVALID_PARAMETER; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + if (!RtlpValidateHeap( Heap, FALSE )) { + Status = STATUS_INVALID_PARAMETER; + } + else { + Status = RtlZeroHeap( HeapHandle, Flags ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + Status = GetExceptionCode(); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Status; +} + +NTSTATUS +RtlDebugCreateTagHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN PWSTR TagPrefix OPTIONAL, + IN PWSTR TagNames + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN LockAcquired; + ULONG TagIndex; + + LockAcquired = FALSE; + TagIndex = 0; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlCreateTagHeap" )) { + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + if (RtlpValidateHeap( Heap, FALSE )) { + TagIndex = RtlCreateTagHeap( HeapHandle, Flags, TagPrefix, TagNames ); + } + + RtlpValidateHeapHeaders( Heap, TRUE ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return TagIndex; +} + + + +NTSYSAPI +PWSTR +NTAPI +RtlDebugQueryTagHeap( + 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; + PWSTR Result; + + LockAcquired = FALSE; + Result = NULL; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlQueryTagHeap" )) { + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + if (RtlpValidateHeap( Heap, FALSE )) { + Result = RtlQueryTagHeap( HeapHandle, Flags, TagIndex, ResetCounters, TagInfo ); + } + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Result; +} + + + +NTSTATUS +RtlDebugUsageHeap( + IN PVOID HeapHandle, + IN ULONG Flags, + IN OUT PRTL_HEAP_USAGE Usage + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + NTSTATUS Status; + BOOLEAN LockAcquired; + + IF_DEBUG_PAGE_HEAP_THEN_RETURN( + HeapHandle, + RtlpDebugPageHeapUsage( HeapHandle, Flags, Usage ) + ); + + LockAcquired = FALSE; + Status = STATUS_SUCCESS; + try { + // + // Validate that HeapAddress points to a HEAP structure. + // + + if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlUsageHeap" )) { + return STATUS_INVALID_PARAMETER; + } + Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS; + + // + // Lock the heap + // + + if (!(Flags & HEAP_NO_SERIALIZE)) { + RtlAcquireLockRoutine( Heap->LockVariable ); + Flags |= HEAP_NO_SERIALIZE; + LockAcquired = TRUE; + } + + if (!RtlpValidateHeap( Heap, FALSE )) { + Status = STATUS_INVALID_PARAMETER; + } + else { + Status = RtlUsageHeap( HeapHandle, Flags, Usage ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + Status = GetExceptionCode(); + } + + if (LockAcquired) { + RtlReleaseLockRoutine( Heap->LockVariable ); + } + + return Status; +} + +BOOLEAN +RtlDebugWalkHeap( + IN PVOID HeapHandle, + IN OUT PRTL_HEAP_WALK_ENTRY Entry + ) +{ + PHEAP Heap = (PHEAP)HeapHandle; + BOOLEAN Result; + + // + // Assumed the caller has serialized via RtlLockHeap or their own locking mechanism. + // + + Result = FALSE; + try { + if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlWalkHeap" )) { + Result = RtlpValidateHeap( Heap, FALSE ); + } + } + except( EXCEPTION_EXECUTE_HANDLER ) { + SET_LAST_STATUS( GetExceptionCode() ); + } + + return Result; +} + +BOOLEAN +RtlpValidateHeapEntry( + IN PHEAP Heap, + IN PHEAP_ENTRY BusyBlock, + IN PCHAR Reason + ) +{ + PHEAP_SEGMENT Segment; + UCHAR SegmentIndex; + BOOLEAN Result; + + if ((BusyBlock == NULL) || + ((ULONG)BusyBlock & (HEAP_GRANULARITY-1)) || + ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) && + (ULONG)BusyBlock & (PAGE_SIZE-1) != FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock ) + ) || + (!(BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) && + ((BusyBlock->SegmentIndex >= HEAP_MAXIMUM_SEGMENTS) || + !(Segment = Heap->Segments[ BusyBlock->SegmentIndex ]) || + (BusyBlock < Segment->FirstEntry) || + (BusyBlock >= Segment->LastValidEntry) + ) + ) || + !(BusyBlock->Flags & HEAP_ENTRY_BUSY) || + (BusyBlock->Flags & HEAP_ENTRY_FILL_PATTERN && !RtlpCheckBusyBlockTail( BusyBlock )) + ) { +InvalidBlock: + HeapDebugPrint(( "Invalid Address specified to %s( %lx, %lx )\n", + Reason, + Heap, + BusyBlock + 1 + )); + HeapDebugBreak( BusyBlock ); + return FALSE; + } + else { + + if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { + Result = TRUE; + } + else + for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) { + Segment = Heap->Segments[ SegmentIndex ]; + if (Segment) { + if (BusyBlock >= Segment->FirstEntry && + BusyBlock < Segment->LastValidEntry + ) { + Result = TRUE; + break; + } + } + } + + if (!Result) { + goto InvalidBlock; + } + + return TRUE; + } +} + +BOOLEAN +RtlpValidateHeapSegment( + IN PHEAP Heap, + IN PHEAP_SEGMENT Segment, + IN UCHAR SegmentIndex, + IN OUT PULONG CountOfFreeBlocks, + IN OUT PULONG TotalFreeSize, + OUT PVOID *BadAddress, + IN OUT PULONG ComputedTagEntries, + IN OUT PULONG ComputedPseudoTagEntries + ) +{ + PHEAP_ENTRY CurrentBlock, PreviousBlock; + ULONG Size; + USHORT PreviousSize, TagIndex; + PHEAP_UNCOMMMTTED_RANGE UnCommittedRange; + PHEAP_ENTRY_EXTRA ExtraStuff; + ULONG NumberOfUnCommittedPages; + ULONG NumberOfUnCommittedRanges; + + RTL_PAGED_CODE(); + + NumberOfUnCommittedPages = 0; + NumberOfUnCommittedRanges = 0; + UnCommittedRange = Segment->UnCommittedRanges; + if (Segment->BaseAddress == Heap) { + CurrentBlock = &Heap->Entry; + } + else { + CurrentBlock = &Segment->Entry; + } + + while (CurrentBlock < Segment->LastValidEntry) { + *BadAddress = CurrentBlock; + if (UnCommittedRange != NULL && + (ULONG)CurrentBlock >= UnCommittedRange->Address + ) { + HeapDebugPrint(( "Heap entry %lx is beyond uncommited range [%x .. %x)\n", + CurrentBlock, + UnCommittedRange->Address, + (PCHAR)UnCommittedRange->Address + UnCommittedRange->Size + )); + return FALSE; + } + + PreviousSize = 0; + while (CurrentBlock < Segment->LastValidEntry) { + *BadAddress = CurrentBlock; + if (PreviousSize != CurrentBlock->PreviousSize) { + HeapDebugPrint(( "Heap entry %lx has incorrect PreviousSize field (%04x instead of %04x)\n", + CurrentBlock, CurrentBlock->PreviousSize, PreviousSize + )); + return FALSE; + } + + PreviousSize = CurrentBlock->Size; + Size = (ULONG)CurrentBlock->Size << HEAP_GRANULARITY_SHIFT; + if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) { + if (ComputedTagEntries != NULL) { + if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { + ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock ); + TagIndex = ExtraStuff->TagIndex; + } + else { + TagIndex = CurrentBlock->SmallTagIndex; + } + + if (TagIndex != 0) { + if (TagIndex & HEAP_PSEUDO_TAG_FLAG) { + TagIndex &= ~HEAP_PSEUDO_TAG_FLAG; + if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) { + ComputedPseudoTagEntries[ TagIndex ] += CurrentBlock->Size; + } + } + else + if (TagIndex & HEAP_GLOBAL_TAG) { + // + // Ignore these since they are global across more than + // one heap. + // + } + else + if (TagIndex < Heap->NextAvailableTagIndex) { + ComputedTagEntries[ TagIndex ] += CurrentBlock->Size; + } + } + } + + if (CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN) { + if (!RtlpCheckBusyBlockTail( CurrentBlock )) { + return FALSE; + } + } + } + else { + *CountOfFreeBlocks += 1; + *TotalFreeSize += CurrentBlock->Size; + + if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED && + CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN + ) { + ULONG cb, cbEqual; + + cb = Size - sizeof( HEAP_FREE_ENTRY ); + if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT && + cb > sizeof( HEAP_FREE_ENTRY_EXTRA ) + ) { + cb -= sizeof( HEAP_FREE_ENTRY_EXTRA ); + } + cbEqual = RtlCompareMemoryUlong( (PCHAR)((PHEAP_FREE_ENTRY)CurrentBlock + 1), cb, FREE_HEAP_FILL ); + if (cbEqual != cb) { + HeapDebugPrint(( "Free Heap block %lx modified at %lx after it was freed\n", + CurrentBlock, + (PCHAR)(CurrentBlock + 1) + cbEqual + )); + return FALSE; + } + } + } + + if (CurrentBlock->SegmentIndex != SegmentIndex) { + HeapDebugPrint(( "Heap block at %lx has incorrect segment index (%x)\n", + CurrentBlock, + SegmentIndex + )); + return FALSE; + } + + if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) { + CurrentBlock = (PHEAP_ENTRY)((PCHAR)CurrentBlock + Size); + if (UnCommittedRange == NULL) { + if (CurrentBlock != Segment->LastValidEntry) { + HeapDebugPrint(( "Heap block at %lx is not last block in segment (%x)\n", + CurrentBlock, + Segment->LastValidEntry + )); + return FALSE; + } + } + else + if ((ULONG)CurrentBlock != UnCommittedRange->Address) { + HeapDebugPrint(( "Heap block at %lx does not match address of next uncommitted address (%x)\n", + CurrentBlock, + UnCommittedRange->Address + )); + return FALSE; + } + else { + NumberOfUnCommittedPages += UnCommittedRange->Size / PAGE_SIZE; + NumberOfUnCommittedRanges += 1; + CurrentBlock = (PHEAP_ENTRY) + ((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size); + UnCommittedRange = UnCommittedRange->Next; + } + + break; + } + + + + CurrentBlock = (PHEAP_ENTRY)((PCHAR)CurrentBlock + Size); + } + } + + *BadAddress = Segment; + if (Segment->NumberOfUnCommittedPages != NumberOfUnCommittedPages) { + HeapDebugPrint(( "Heap Segment at %lx contains invalid NumberOfUnCommittedPages (%x != %x)\n", + Segment, + Segment->NumberOfUnCommittedPages, + NumberOfUnCommittedPages + )); + return FALSE; + } + + if (Segment->NumberOfUnCommittedRanges != NumberOfUnCommittedRanges) { + HeapDebugPrint(( "Heap Segment at %lx contains invalid NumberOfUnCommittedRanges (%x != %x)\n", + Segment, + Segment->NumberOfUnCommittedRanges, + NumberOfUnCommittedRanges + )); + return FALSE; + } + + return TRUE; +} + +BOOLEAN +RtlpValidateHeap( + IN PHEAP Heap, + IN BOOLEAN AlwaysValidate + ) +{ + NTSTATUS Status; + PHEAP_SEGMENT Segment; + PLIST_ENTRY Head, Next; + PHEAP_FREE_ENTRY FreeBlock; + BOOLEAN EmptyFreeList; + ULONG NumberOfFreeListEntries; + ULONG CountOfFreeBlocks; + ULONG TotalFreeSize; + ULONG Size; + USHORT PreviousSize; + UCHAR SegmentIndex; + PVOID BadAddress; + PULONG ComputedTagEntries; + PULONG ComputedPseudoTagEntries; + PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock; + USHORT TagIndex; + + RTL_PAGED_CODE(); + + BadAddress = Heap; + if (!RtlpValidateHeapHeaders( Heap, FALSE )) { + goto errorExit; + } + + if (!AlwaysValidate && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)) { + goto exit; + } + + NumberOfFreeListEntries = 0; + Head = &Heap->FreeLists[ 0 ]; + for (Size = 0; Size < HEAP_MAXIMUM_FREELISTS; Size++) { + if (Size != 0) { + EmptyFreeList = (BOOLEAN)(IsListEmpty( Head )); + BadAddress = &Heap->u.FreeListsInUseBytes[ Size / 8 ]; + if (Heap->u.FreeListsInUseBytes[ Size / 8 ] & (1 << (Size & 7)) ) { + if (EmptyFreeList) { + HeapDebugPrint(( "dedicated (%04x) free list empty but marked as non-empty\n", + Size + )); + goto errorExit; + } + } + else { + if (!EmptyFreeList) { + HeapDebugPrint(( "dedicated (%04x) free list non-empty but marked as empty\n", + Size + )); + goto errorExit; + } + } + } + + Next = Head->Flink; + PreviousSize = 0; + while (Head != Next) { + FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList ); + Next = Next->Flink; + BadAddress = FreeBlock; + if (FreeBlock->Flags & HEAP_ENTRY_BUSY) { + HeapDebugPrint(( "dedicated (%04x) free list element %lx is marked busy\n", + Size, + FreeBlock + )); + goto errorExit; + } + + if (Size != 0 && FreeBlock->Size != Size) { + HeapDebugPrint(( "Dedicated (%04x) free list element %lx is wrong size (%04x)\n", + Size, + FreeBlock, + FreeBlock->Size + )); + goto errorExit; + } + else + if (Size == 0 && FreeBlock->Size < HEAP_MAXIMUM_FREELISTS) { + HeapDebugPrint(( "Non-Dedicated free list element %lx with too small size (%04x)\n", + FreeBlock, + FreeBlock->Size + )); + goto errorExit; + } + else + if (Size == 0 && FreeBlock->Size < PreviousSize) { + HeapDebugPrint(( "Non-Dedicated free list element %lx is out of order\n", + FreeBlock + )); + goto errorExit; + } + else { + PreviousSize = FreeBlock->Size; + } + + NumberOfFreeListEntries++; + } + + Head++; + } + + Size = (HEAP_NUMBER_OF_PSEUDO_TAG + + Heap->NextAvailableTagIndex + + 1 + ) * sizeof( ULONG ); + ComputedTagEntries = NULL; + ComputedPseudoTagEntries = NULL; + if (RtlpValidateHeapTagsEnable && Heap->PseudoTagEntries != NULL) { + Status = NtAllocateVirtualMemory( NtCurrentProcess(), + &ComputedPseudoTagEntries, + 0, + &Size, + MEM_COMMIT, + PAGE_READWRITE + ); + if (NT_SUCCESS( Status )) { + ComputedTagEntries = ComputedPseudoTagEntries + HEAP_NUMBER_OF_PSEUDO_TAG; + } + } + + Head = &Heap->VirtualAllocdBlocks; + Next = Head->Flink; + while (Head != Next) { + VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry ); + if (ComputedTagEntries != NULL) { + TagIndex = VirtualAllocBlock->ExtraStuff.TagIndex; + if (TagIndex != 0) { + if (TagIndex & HEAP_PSEUDO_TAG_FLAG) { + TagIndex &= ~HEAP_PSEUDO_TAG_FLAG; + if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) { + ComputedPseudoTagEntries[ TagIndex ] += + VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT; + } + } + else + if (TagIndex & HEAP_GLOBAL_TAG) { + // + // Ignore these since they are global across more than + // one heap. + // + } + else + if (TagIndex < Heap->NextAvailableTagIndex) { + ComputedTagEntries[ TagIndex ] += + VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT; + } + } + } + + if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN) { + if (!RtlpCheckBusyBlockTail( &VirtualAllocBlock->BusyBlock )) { + return FALSE; + } + } + + Next = Next->Flink; + } + + CountOfFreeBlocks = 0; + TotalFreeSize = 0; + for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) { + Segment = Heap->Segments[ SegmentIndex ]; + if (Segment) { + if (!RtlpValidateHeapSegment( Heap, + Segment, + SegmentIndex, + &CountOfFreeBlocks, + &TotalFreeSize, + &BadAddress, + ComputedTagEntries, + ComputedPseudoTagEntries + ) + ) { + goto errorExit; + } + } + } + + BadAddress = Heap; + if (NumberOfFreeListEntries != CountOfFreeBlocks) { + HeapDebugPrint(( "Number of free blocks in arena (%ld) does not match number in the free lists (%ld)\n", + CountOfFreeBlocks, + NumberOfFreeListEntries + )); + goto errorExit; + } + + if (Heap->TotalFreeSize != TotalFreeSize) { + HeapDebugPrint(( "Total size of free blocks in arena (%ld) does not match number total in heap header (%ld)\n", + TotalFreeSize, + Heap->TotalFreeSize + )); + goto errorExit; + } + + if (ComputedPseudoTagEntries != NULL) { + PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries; + PHEAP_TAG_ENTRY TagEntries; + USHORT TagIndex; + + PseudoTagEntries = Heap->PseudoTagEntries; + if (PseudoTagEntries != NULL) { + for (TagIndex=1; TagIndex<HEAP_NUMBER_OF_PSEUDO_TAG; TagIndex++) { + PseudoTagEntries += 1; + if (ComputedPseudoTagEntries[ TagIndex ] != PseudoTagEntries->Size) { + HeapDebugPrint(( "Pseudo Tag %04x size incorrect (%x != %x) %x\n", + TagIndex, + PseudoTagEntries->Size, + ComputedPseudoTagEntries[ TagIndex ] + &ComputedPseudoTagEntries[ TagIndex ] + )); + goto errorExit; + } + } + } + TagEntries = Heap->TagEntries; + if (TagEntries != NULL) { + for (TagIndex=1; TagIndex<Heap->NextAvailableTagIndex; TagIndex++) { + TagEntries += 1; + if (ComputedTagEntries[ TagIndex ] != TagEntries->Size) { + HeapDebugPrint(( "Tag %04x (%ws) size incorrect (%x != %x) %x\n", + TagIndex, + TagEntries->TagName, + TagEntries->Size, + ComputedTagEntries[ TagIndex ], + &ComputedTagEntries[ TagIndex ] + )); + goto errorExit; + } + } + } + + Size = 0; + NtFreeVirtualMemory( NtCurrentProcess(), + &ComputedPseudoTagEntries, + &Size, + MEM_RELEASE + ); + } + +exit: + return TRUE; + +errorExit: + HeapDebugBreak( BadAddress ); + + if (ComputedPseudoTagEntries != NULL) { + Size = 0; + NtFreeVirtualMemory( NtCurrentProcess(), + &ComputedPseudoTagEntries, + &Size, + MEM_RELEASE + ); + } + return FALSE; + +} // RtlpValidateHeap + + +BOOLEAN RtlpHeapInvalidBreakPoint; +PVOID RtlpHeapInvalidBadAddress; + +VOID +RtlpBreakPointHeap( + IN PVOID BadAddress + ) +{ + if (NtCurrentPeb()->BeingDebugged) { + *(BOOLEAN volatile *)&RtlpHeapInvalidBreakPoint = TRUE; + RtlpHeapInvalidBadAddress = BadAddress; + DbgBreakPoint(); + *(BOOLEAN volatile *)&RtlpHeapInvalidBreakPoint = FALSE; + } +} |