mirror of https://github.com/lianthony/NT4.0
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.
2732 lines
84 KiB
2732 lines
84 KiB
/*++
|
|
|
|
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 ));
|
|
}
|