Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1883 lines
59 KiB

/*++
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;
}
}