mirror of https://github.com/tongzx/nt5src
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.
1301 lines
40 KiB
1301 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
heappriv.h
|
|
|
|
Abstract:
|
|
|
|
Private include file used by heap allocator (heap.c, heapdll.c and
|
|
heapdbg.c)
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 25-Oct-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#ifndef _RTL_HEAP_PRIVATE_
|
|
#define _RTL_HEAP_PRIVATE_
|
|
|
|
#include "heappage.h"
|
|
|
|
//
|
|
// In private builds (PRERELEASE = 1) we allow using the new low fragmentation heap
|
|
// for processes that set the DisableLookaside registry key. The main purpose is to
|
|
// allow testing the new heap API.
|
|
//
|
|
|
|
#ifndef PRERELEASE
|
|
|
|
#define DISABLE_REGISTRY_TEST_HOOKS
|
|
|
|
#endif
|
|
|
|
//
|
|
// Disable FPO optimization so even retail builds get somewhat reasonable
|
|
// stack backtraces
|
|
//
|
|
|
|
#if i386
|
|
// #pragma optimize("y",off)
|
|
#endif
|
|
|
|
#if DBG
|
|
#define HEAPASSERT(exp) if (!(exp)) RtlAssert( #exp, __FILE__, __LINE__, NULL )
|
|
#else
|
|
#define HEAPASSERT(exp)
|
|
#endif
|
|
|
|
//
|
|
// Define Minimum lookaside list depth.
|
|
//
|
|
|
|
#define MINIMUM_LOOKASIDE_DEPTH 4
|
|
|
|
//
|
|
// This variable contains the fill pattern used for heap tail checking
|
|
//
|
|
|
|
extern const UCHAR CheckHeapFillPattern[ CHECK_HEAP_TAIL_SIZE ];
|
|
|
|
|
|
//
|
|
// Here are the locking routines for the heap (kernel and user)
|
|
//
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Kernel mode heap uses the kernel resource package for locking
|
|
//
|
|
|
|
#define RtlInitializeLockRoutine(L) ExInitializeResourceLite((PERESOURCE)(L))
|
|
#define RtlAcquireLockRoutine(L) ExAcquireResourceExclusiveLite((PERESOURCE)(L),TRUE)
|
|
#define RtlReleaseLockRoutine(L) ExReleaseResourceLite((PERESOURCE)(L))
|
|
#define RtlDeleteLockRoutine(L) ExDeleteResourceLite((PERESOURCE)(L))
|
|
#define RtlOkayToLockRoutine(L) ExOkayToLockRoutineLite((PERESOURCE)(L))
|
|
|
|
#else // #ifdef NTOS_KERNEL_ROUTINE
|
|
|
|
//
|
|
// User mode heap uses the critical section package for locking
|
|
//
|
|
|
|
#ifndef PREALLOCATE_EVENT_MASK
|
|
|
|
#define PREALLOCATE_EVENT_MASK 0x80000000 // Defined only in dll\resource.c
|
|
|
|
#endif // PREALLOCATE_EVENT_MASK
|
|
|
|
#define RtlInitializeLockRoutine(L) RtlInitializeCriticalSectionAndSpinCount((PRTL_CRITICAL_SECTION)(L),(PREALLOCATE_EVENT_MASK | 4000))
|
|
#define RtlAcquireLockRoutine(L) RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)(L))
|
|
#define RtlReleaseLockRoutine(L) RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)(L))
|
|
#define RtlDeleteLockRoutine(L) RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)(L))
|
|
#define RtlOkayToLockRoutine(L) NtdllOkayToLockRoutine((PVOID)(L))
|
|
|
|
#endif // #ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
|
|
//
|
|
// Here are some debugging macros for the heap
|
|
//
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
#define HEAP_DEBUG_FLAGS 0
|
|
#define DEBUG_HEAP(F) FALSE
|
|
#define SET_LAST_STATUS(S) NOTHING;
|
|
|
|
#else // #ifdef NTOS_KERNEL_ROUTINE
|
|
|
|
#define HEAP_DEBUG_FLAGS (HEAP_VALIDATE_PARAMETERS_ENABLED | \
|
|
HEAP_VALIDATE_ALL_ENABLED | \
|
|
HEAP_CAPTURE_STACK_BACKTRACES | \
|
|
HEAP_CREATE_ENABLE_TRACING | \
|
|
HEAP_FLAG_PAGE_ALLOCS)
|
|
#define DEBUG_HEAP(F) ((F & HEAP_DEBUG_FLAGS) && !(F & HEAP_SKIP_VALIDATION_CHECKS))
|
|
#define SET_LAST_STATUS(S) {NtCurrentTeb()->LastErrorValue = RtlNtStatusToDosError( NtCurrentTeb()->LastStatusValue = (ULONG)(S) );}
|
|
|
|
#endif // #ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
|
|
//
|
|
// Here are the macros used for debug printing and breakpoints
|
|
//
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
#define HeapDebugPrint( _x_ ) {DbgPrint _x_;}
|
|
|
|
#define HeapDebugBreak( _x_ ) {if (KdDebuggerEnabled) DbgBreakPoint();}
|
|
|
|
#else // #ifdef NTOS_KERNEL_ROUTINE
|
|
|
|
#define HeapDebugPrint( _x_ ) \
|
|
{ \
|
|
PLIST_ENTRY _Module; \
|
|
PLDR_DATA_TABLE_ENTRY _Entry; \
|
|
\
|
|
_Module = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink; \
|
|
_Entry = CONTAINING_RECORD( _Module, \
|
|
LDR_DATA_TABLE_ENTRY, \
|
|
InLoadOrderLinks); \
|
|
DbgPrint("HEAP[%wZ]: ", &_Entry->BaseDllName); \
|
|
DbgPrint _x_; \
|
|
}
|
|
|
|
#define HeapDebugBreak( _x_ ) \
|
|
{ \
|
|
VOID RtlpBreakPointHeap( PVOID BadAddress ); \
|
|
\
|
|
RtlpBreakPointHeap( (_x_) ); \
|
|
}
|
|
|
|
#endif // #ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Virtual memory hook for virtual alloc functions
|
|
//
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
|
|
#define RtlpHeapFreeVirtualMemory(P,A,S,F) \
|
|
ZwFreeVirtualMemory(P,A,S,F)
|
|
|
|
#else // NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// The user mode call needs to call the secmem virtual free
|
|
// as well to update the memory counters per heap
|
|
//
|
|
|
|
#define RtlpHeapFreeVirtualMemory(P,A,S,F) \
|
|
RtlpSecMemFreeVirtualMemory(P,A,S,F)
|
|
|
|
#endif // NTOS_KERNEL_RUNTIME
|
|
|
|
|
|
|
|
//
|
|
// Implemented in heap.c
|
|
//
|
|
|
|
BOOLEAN
|
|
RtlpInitializeHeapSegment (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_SEGMENT Segment,
|
|
IN UCHAR SegmentIndex,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress,
|
|
IN PVOID UnCommittedAddress,
|
|
IN PVOID CommitLimitAddress
|
|
);
|
|
|
|
PHEAP_FREE_ENTRY
|
|
RtlpCoalesceFreeBlocks (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_FREE_ENTRY FreeBlock,
|
|
IN OUT PSIZE_T FreeSize,
|
|
IN BOOLEAN RemoveFromFreeList
|
|
);
|
|
|
|
VOID
|
|
RtlpDeCommitFreeBlock (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_FREE_ENTRY FreeBlock,
|
|
IN SIZE_T FreeSize
|
|
);
|
|
|
|
VOID
|
|
RtlpInsertFreeBlock (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_FREE_ENTRY FreeBlock,
|
|
IN SIZE_T FreeSize
|
|
);
|
|
|
|
PHEAP_FREE_ENTRY
|
|
RtlpFindAndCommitPages (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_SEGMENT Segment,
|
|
IN OUT PSIZE_T Size,
|
|
IN PVOID AddressWanted OPTIONAL
|
|
);
|
|
|
|
PVOID
|
|
RtlAllocateHeapSlowly (
|
|
IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN SIZE_T Size
|
|
);
|
|
|
|
BOOLEAN
|
|
RtlFreeHeapSlowly (
|
|
IN PVOID HeapHandle,
|
|
IN ULONG Flags,
|
|
IN PVOID BaseAddress
|
|
);
|
|
|
|
SIZE_T
|
|
RtlpGetSizeOfBigBlock (
|
|
IN PHEAP_ENTRY BusyBlock
|
|
);
|
|
|
|
PHEAP_ENTRY_EXTRA
|
|
RtlpGetExtraStuffPointer (
|
|
PHEAP_ENTRY BusyBlock
|
|
);
|
|
|
|
BOOLEAN
|
|
RtlpCheckBusyBlockTail (
|
|
IN PHEAP_ENTRY BusyBlock
|
|
);
|
|
|
|
|
|
//
|
|
// Implemented in heapdll.c
|
|
//
|
|
|
|
VOID
|
|
RtlpAddHeapToProcessList (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
VOID
|
|
RtlpRemoveHeapFromProcessList (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
PHEAP_FREE_ENTRY
|
|
RtlpCoalesceHeap (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
BOOLEAN
|
|
RtlpCheckHeapSignature (
|
|
IN PHEAP Heap,
|
|
IN PCHAR Caller
|
|
);
|
|
|
|
VOID
|
|
RtlDetectHeapLeaks();
|
|
|
|
|
|
//
|
|
// Implemented in heapdbg.c
|
|
//
|
|
|
|
BOOLEAN
|
|
RtlpValidateHeapEntry (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_ENTRY BusyBlock,
|
|
IN PCHAR Reason
|
|
);
|
|
|
|
BOOLEAN
|
|
RtlpValidateHeap (
|
|
IN PHEAP Heap,
|
|
IN BOOLEAN AlwaysValidate
|
|
);
|
|
|
|
VOID
|
|
RtlpUpdateHeapListIndex (
|
|
USHORT OldIndex,
|
|
USHORT NewIndex
|
|
);
|
|
|
|
BOOLEAN
|
|
RtlpValidateHeapHeaders(
|
|
IN PHEAP Heap,
|
|
IN BOOLEAN Recompute
|
|
);
|
|
|
|
|
|
#ifndef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Nondedicated free list optimization
|
|
//
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Define HEAP_VALIDATE_INDEX to activate a validation of the index
|
|
// after each operation with non-dedicated list
|
|
// This is only for debug-test, to make sure the list and index is consistent
|
|
//
|
|
|
|
//#define HEAP_VALIDATE_INDEX
|
|
|
|
#endif // DBG
|
|
|
|
|
|
#define HEAP_FRONT_LOOKASIDE 1
|
|
#define HEAP_FRONT_LOWFRAGHEAP 2
|
|
|
|
#define RtlpGetLookasideHeap(H) \
|
|
(((H)->FrontEndHeapType == HEAP_FRONT_LOOKASIDE) ? (H)->FrontEndHeap : NULL)
|
|
|
|
#define RtlpGetLowFragHeap(H) \
|
|
(((H)->FrontEndHeapType == HEAP_FRONT_LOWFRAGHEAP) ? (H)->FrontEndHeap : NULL)
|
|
|
|
#define RtlpIsFrontHeapUnlocked(H) \
|
|
((H)->FrontHeapLockCount == 0)
|
|
|
|
#define RtlpLockFrontHeap(H) \
|
|
{ \
|
|
(H)->FrontHeapLockCount += 1; \
|
|
}
|
|
|
|
#define RtlpUnlockFrontHeap(H) \
|
|
{ \
|
|
(H)->FrontHeapLockCount -= 1; \
|
|
}
|
|
|
|
|
|
#define HEAP_INDEX_THRESHOLD 32
|
|
|
|
//
|
|
// Heap performance counter support
|
|
//
|
|
|
|
#define HEAP_OP_COUNT 2
|
|
|
|
#define HEAP_OP_ALLOC 0
|
|
#define HEAP_OP_FREE 1
|
|
|
|
//
|
|
// The time / per operation is measured ones at 16 operations
|
|
//
|
|
|
|
#define HEAP_SAMPLING_MASK 0x000001FF
|
|
|
|
#define HEAP_SAMPLING_COUNT 100
|
|
|
|
typedef struct _HEAP_PERF_DATA {
|
|
|
|
UINT64 CountFrequence;
|
|
UINT64 OperationTime[HEAP_OP_COUNT];
|
|
|
|
//
|
|
// The data bellow are only for sampling
|
|
//
|
|
|
|
ULONG Sequence;
|
|
|
|
UINT64 TempTime[HEAP_OP_COUNT];
|
|
ULONG TempCount[HEAP_OP_COUNT];
|
|
|
|
} HEAP_PERF_DATA, *PHEAP_PERF_DATA;
|
|
|
|
#define HEAP_PERF_DECLARE_TIMER() \
|
|
UINT64 _HeapPerfStartTimer, _HeapPerfEndTimer;
|
|
|
|
#define HEAP_PERF_START_TIMER(H) \
|
|
{ \
|
|
PHEAP_INDEX HeapIndex = (PHEAP_INDEX)(H)->LargeBlocksIndex; \
|
|
if ( (HeapIndex != NULL) && \
|
|
(!((HeapIndex->PerfData.Sequence++) & HEAP_SAMPLING_MASK)) ) { \
|
|
\
|
|
NtQueryPerformanceCounter( (PLARGE_INTEGER)&_HeapPerfStartTimer , NULL); \
|
|
} else { \
|
|
_HeapPerfStartTimer = 0; \
|
|
} \
|
|
}
|
|
|
|
#define HEAP_PERF_STOP_TIMER(H,OP) \
|
|
{ \
|
|
if (_HeapPerfStartTimer) { \
|
|
PHEAP_INDEX HeapIndex = (PHEAP_INDEX)(H)->LargeBlocksIndex; \
|
|
\
|
|
NtQueryPerformanceCounter( (PLARGE_INTEGER)&_HeapPerfEndTimer , NULL); \
|
|
HeapIndex->PerfData.TempTime[OP] += (_HeapPerfEndTimer - _HeapPerfStartTimer); \
|
|
\
|
|
if ((HeapIndex->PerfData.TempCount[OP]++) >= HEAP_SAMPLING_COUNT) { \
|
|
HeapIndex->PerfData.OperationTime[OP] = HeapIndex->PerfData.TempTime[OP] / (HeapIndex->PerfData.TempCount[OP] - 1); \
|
|
\
|
|
HeapIndex->PerfData.TempCount[OP] = 0; \
|
|
HeapIndex->PerfData.TempTime[OP] = 0; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
#define RtlpRegisterOperation(H,S,Op) \
|
|
{ \
|
|
PHEAP_LOOKASIDE Lookaside; \
|
|
\
|
|
if ( (Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(H)) ) { \
|
|
\
|
|
SIZE_T Index = (S) >> 10; \
|
|
\
|
|
if (Index >= HEAP_MAXIMUM_FREELISTS) { \
|
|
\
|
|
Index = HEAP_MAXIMUM_FREELISTS - 1; \
|
|
} \
|
|
\
|
|
Lookaside[Index].Counters[(Op)] += 1; \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// The heap index structure
|
|
//
|
|
|
|
typedef struct _HEAP_INDEX {
|
|
|
|
ULONG ArraySize;
|
|
ULONG VirtualMemorySize;
|
|
|
|
//
|
|
// The timing counters are available only on heaps
|
|
// with an index created
|
|
//
|
|
|
|
HEAP_PERF_DATA PerfData;
|
|
|
|
LONG LargeBlocksCacheDepth;
|
|
LONG LargeBlocksCacheMaxDepth;
|
|
LONG LargeBlocksCacheMinDepth;
|
|
LONG LargeBlocksCacheSequence;
|
|
|
|
struct {
|
|
|
|
ULONG Committs;
|
|
ULONG Decommitts;
|
|
LONG LargestDepth;
|
|
LONG LargestRequiredDepth;
|
|
|
|
} CacheStats;
|
|
|
|
union {
|
|
|
|
PULONG FreeListsInUseUlong;
|
|
PUCHAR FreeListsInUseBytes;
|
|
} u;
|
|
|
|
PHEAP_FREE_ENTRY * FreeListHints;
|
|
|
|
} HEAP_INDEX, *PHEAP_INDEX;
|
|
|
|
//
|
|
// Macro for setting a bit in the freelist vector to indicate entries are
|
|
// present.
|
|
//
|
|
|
|
#define SET_INDEX_BIT( HeapIndex, AllocIndex ) \
|
|
{ \
|
|
ULONG _Index_; \
|
|
ULONG _Bit_; \
|
|
\
|
|
_Index_ = (AllocIndex) >> 3; \
|
|
_Bit_ = (1 << ((AllocIndex) & 7)); \
|
|
\
|
|
(HeapIndex)->u.FreeListsInUseBytes[ _Index_ ] |= _Bit_; \
|
|
}
|
|
|
|
//
|
|
// Macro for clearing a bit in the freelist vector to indicate entries are
|
|
// not present.
|
|
//
|
|
|
|
#define CLEAR_INDEX_BIT( HeapIndex, AllocIndex ) \
|
|
{ \
|
|
ULONG _Index_; \
|
|
ULONG _Bit_; \
|
|
\
|
|
_Index_ = (AllocIndex) >> 3; \
|
|
_Bit_ = (1 << ((AllocIndex) & 7)); \
|
|
\
|
|
(HeapIndex)->u.FreeListsInUseBytes[ _Index_ ] ^= _Bit_; \
|
|
}
|
|
|
|
VOID
|
|
RtlpInitializeListIndex (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
PLIST_ENTRY
|
|
RtlpFindEntry (
|
|
IN PHEAP Heap,
|
|
IN ULONG Size
|
|
);
|
|
|
|
VOID
|
|
RtlpUpdateIndexRemoveBlock (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_FREE_ENTRY FreeEntry
|
|
);
|
|
|
|
VOID
|
|
RtlpUpdateIndexInsertBlock (
|
|
IN PHEAP Heap,
|
|
IN PHEAP_FREE_ENTRY FreeEntry
|
|
);
|
|
|
|
VOID
|
|
RtlpFlushCacheContents (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
extern LONG RtlpSequenceNumberTest;
|
|
|
|
#define RtlpCheckLargeCache(H) \
|
|
{ \
|
|
PHEAP_INDEX HeapIndex = (PHEAP_INDEX)(H)->LargeBlocksIndex; \
|
|
if ((HeapIndex != NULL) && \
|
|
(HeapIndex->LargeBlocksCacheSequence >= RtlpSequenceNumberTest)) { \
|
|
\
|
|
RtlpFlushCacheContents(Heap); \
|
|
} \
|
|
}
|
|
|
|
VOID
|
|
RtlpFlushLargestCacheBlock (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
#ifdef HEAP_VALIDATE_INDEX
|
|
|
|
//
|
|
// The validation code for index
|
|
//
|
|
|
|
BOOLEAN
|
|
RtlpValidateNonDedicatedList (
|
|
IN PHEAP Heap
|
|
);
|
|
|
|
|
|
#else // HEAP_VALIDATE_INDEX
|
|
|
|
#define RtlpValidateNonDedicatedList(H)
|
|
|
|
#endif // HEAP_VALIDATE_INDEX
|
|
|
|
|
|
#else // NTOS_KERNEL_RUNTIME
|
|
|
|
#define HEAP_PERF_DECLARE_TIMER()
|
|
|
|
#define HEAP_PERF_START_TIMER(H)
|
|
|
|
#define HEAP_PERF_STOP_TIMER(H,Op)
|
|
|
|
#define RtlpRegisterOperation(H,S,Op)
|
|
|
|
|
|
#define RtlpInitializeListIndex(H)
|
|
|
|
#define RtlpFindEntry(H,S) (NULL)
|
|
|
|
#define RtlpUpdateIndexRemoveBlock(H,F)
|
|
|
|
#define RtlpUpdateIndexInsertBlock(H,F)
|
|
|
|
#define RtlpCheckLargeCache(H)
|
|
|
|
#define RtlpValidateNonDedicatedList(H)
|
|
|
|
#endif // NTOS_KERNEL_RUNTIME
|
|
|
|
|
|
//
|
|
// An extra bitmap manipulation routine
|
|
//
|
|
|
|
#define RtlFindFirstSetRightMember(Set) \
|
|
(((Set) & 0xFFFF) ? \
|
|
(((Set) & 0xFF) ? \
|
|
RtlpBitsClearLow[(Set) & 0xFF] : \
|
|
RtlpBitsClearLow[((Set) >> 8) & 0xFF] + 8) : \
|
|
((((Set) >> 16) & 0xFF) ? \
|
|
RtlpBitsClearLow[ ((Set) >> 16) & 0xFF] + 16 : \
|
|
RtlpBitsClearLow[ (Set) >> 24] + 24) \
|
|
)
|
|
|
|
|
|
//
|
|
// Macro for setting a bit in the freelist vector to indicate entries are
|
|
// present.
|
|
//
|
|
|
|
#define SET_FREELIST_BIT( H, FB ) \
|
|
{ \
|
|
ULONG _Index_; \
|
|
ULONG _Bit_; \
|
|
\
|
|
HEAPASSERT((FB)->Size < HEAP_MAXIMUM_FREELISTS); \
|
|
\
|
|
_Index_ = (FB)->Size >> 3; \
|
|
_Bit_ = (1 << ((FB)->Size & 7)); \
|
|
\
|
|
HEAPASSERT(((H)->u.FreeListsInUseBytes[ _Index_ ] & _Bit_) == 0); \
|
|
\
|
|
(H)->u.FreeListsInUseBytes[ _Index_ ] |= _Bit_; \
|
|
}
|
|
|
|
//
|
|
// Macro for clearing a bit in the freelist vector to indicate entries are
|
|
// not present.
|
|
//
|
|
|
|
#define CLEAR_FREELIST_BIT( H, FB ) \
|
|
{ \
|
|
ULONG _Index_; \
|
|
ULONG _Bit_; \
|
|
\
|
|
HEAPASSERT((FB)->Size < HEAP_MAXIMUM_FREELISTS); \
|
|
\
|
|
_Index_ = (FB)->Size >> 3; \
|
|
_Bit_ = (1 << ((FB)->Size & 7)); \
|
|
\
|
|
HEAPASSERT((H)->u.FreeListsInUseBytes[ _Index_ ] & _Bit_); \
|
|
HEAPASSERT(IsListEmpty(&(H)->FreeLists[ (FB)->Size ])); \
|
|
\
|
|
(H)->u.FreeListsInUseBytes[ _Index_ ] ^= _Bit_; \
|
|
}
|
|
|
|
|
|
//
|
|
// This macro inserts a free block into the appropriate free list including
|
|
// the [0] index list with entry filling if necessary
|
|
//
|
|
|
|
#define RtlpInsertFreeBlockDirect( H, FB, SIZE ) \
|
|
{ \
|
|
PLIST_ENTRY _HEAD, _NEXT; \
|
|
PHEAP_FREE_ENTRY _FB1; \
|
|
\
|
|
HEAPASSERT((FB)->Size == (SIZE)); \
|
|
(FB)->Flags &= ~(HEAP_ENTRY_FILL_PATTERN | \
|
|
HEAP_ENTRY_EXTRA_PRESENT | \
|
|
HEAP_ENTRY_BUSY); \
|
|
\
|
|
if ((H)->Flags & HEAP_FREE_CHECKING_ENABLED) { \
|
|
\
|
|
RtlFillMemoryUlong( (PCHAR)((FB) + 1), \
|
|
((SIZE) << HEAP_GRANULARITY_SHIFT) - \
|
|
sizeof( *(FB) ), \
|
|
FREE_HEAP_FILL ); \
|
|
\
|
|
(FB)->Flags |= HEAP_ENTRY_FILL_PATTERN; \
|
|
} \
|
|
\
|
|
if ((SIZE) < HEAP_MAXIMUM_FREELISTS) { \
|
|
\
|
|
_HEAD = &(H)->FreeLists[ (SIZE) ]; \
|
|
\
|
|
if (IsListEmpty(_HEAD)) { \
|
|
\
|
|
SET_FREELIST_BIT( H, FB ); \
|
|
} \
|
|
\
|
|
} else { \
|
|
\
|
|
_HEAD = &(H)->FreeLists[ 0 ]; \
|
|
_NEXT = (H)->LargeBlocksIndex ? \
|
|
RtlpFindEntry(H, SIZE) : \
|
|
_HEAD->Flink; \
|
|
\
|
|
while (_HEAD != _NEXT) { \
|
|
\
|
|
_FB1 = CONTAINING_RECORD( _NEXT, HEAP_FREE_ENTRY, FreeList ); \
|
|
\
|
|
if ((SIZE) <= _FB1->Size) { \
|
|
\
|
|
break; \
|
|
\
|
|
} else { \
|
|
\
|
|
_NEXT = _NEXT->Flink; \
|
|
} \
|
|
} \
|
|
\
|
|
_HEAD = _NEXT; \
|
|
} \
|
|
\
|
|
InsertTailList( _HEAD, &(FB)->FreeList ); \
|
|
RtlpUpdateIndexInsertBlock(H, FB); \
|
|
RtlpValidateNonDedicatedList(H); \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpInsertFreeBlockDirect does no filling.
|
|
//
|
|
|
|
#define RtlpFastInsertFreeBlockDirect( H, FB, SIZE ) \
|
|
{ \
|
|
if ((SIZE) < HEAP_MAXIMUM_FREELISTS) { \
|
|
\
|
|
RtlpFastInsertDedicatedFreeBlockDirect( H, FB, SIZE ); \
|
|
\
|
|
} else { \
|
|
\
|
|
RtlpFastInsertNonDedicatedFreeBlockDirect( H, FB, SIZE ); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpInsertFreeBlockDirect only works for dedicated free
|
|
// lists and doesn't do any filling.
|
|
//
|
|
|
|
#define RtlpFastInsertDedicatedFreeBlockDirect( H, FB, SIZE ) \
|
|
{ \
|
|
PLIST_ENTRY _HEAD; \
|
|
\
|
|
HEAPASSERT((FB)->Size == (SIZE)); \
|
|
\
|
|
if (!((FB)->Flags & HEAP_ENTRY_LAST_ENTRY)) { \
|
|
\
|
|
HEAPASSERT(((PHEAP_ENTRY)(FB) + (SIZE))->PreviousSize == (SIZE)); \
|
|
} \
|
|
\
|
|
(FB)->Flags &= HEAP_ENTRY_LAST_ENTRY; \
|
|
\
|
|
_HEAD = &(H)->FreeLists[ (SIZE) ]; \
|
|
\
|
|
if (IsListEmpty(_HEAD)) { \
|
|
\
|
|
SET_FREELIST_BIT( H, FB ); \
|
|
} \
|
|
\
|
|
InsertTailList( _HEAD, &(FB)->FreeList ); \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpInsertFreeBlockDirect only works for nondedicated free
|
|
// lists and doesn't do any filling.
|
|
//
|
|
|
|
#define RtlpFastInsertNonDedicatedFreeBlockDirect( H, FB, SIZE ) \
|
|
{ \
|
|
PLIST_ENTRY _HEAD, _NEXT; \
|
|
PHEAP_FREE_ENTRY _FB1; \
|
|
\
|
|
HEAPASSERT((FB)->Size == (SIZE)); \
|
|
\
|
|
if (!((FB)->Flags & HEAP_ENTRY_LAST_ENTRY)) { \
|
|
\
|
|
HEAPASSERT(((PHEAP_ENTRY)(FB) + (SIZE))->PreviousSize == (SIZE)); \
|
|
} \
|
|
\
|
|
(FB)->Flags &= (HEAP_ENTRY_LAST_ENTRY); \
|
|
\
|
|
_HEAD = &(H)->FreeLists[ 0 ]; \
|
|
_NEXT = (H)->LargeBlocksIndex ? \
|
|
RtlpFindEntry(H, SIZE) : \
|
|
_HEAD->Flink; \
|
|
\
|
|
while (_HEAD != _NEXT) { \
|
|
\
|
|
_FB1 = CONTAINING_RECORD( _NEXT, HEAP_FREE_ENTRY, FreeList ); \
|
|
\
|
|
if ((SIZE) <= _FB1->Size) { \
|
|
\
|
|
break; \
|
|
\
|
|
} else { \
|
|
\
|
|
_NEXT = _NEXT->Flink; \
|
|
} \
|
|
} \
|
|
\
|
|
InsertTailList( _NEXT, &(FB)->FreeList ); \
|
|
RtlpUpdateIndexInsertBlock(H, FB); \
|
|
RtlpValidateNonDedicatedList(H); \
|
|
}
|
|
|
|
|
|
//
|
|
// This macro removes a block from its free list with fill checking if
|
|
// necessary
|
|
//
|
|
|
|
#define RtlpRemoveFreeBlock( H, FB ) \
|
|
{ \
|
|
RtlpFastRemoveFreeBlock( H, FB ) \
|
|
\
|
|
if ((FB)->Flags & HEAP_ENTRY_FILL_PATTERN) { \
|
|
\
|
|
SIZE_T cb, cbEqual; \
|
|
PVOID p; \
|
|
\
|
|
cb = ((FB)->Size << HEAP_GRANULARITY_SHIFT) - sizeof( *(FB) ); \
|
|
\
|
|
if ((FB)->Flags & HEAP_ENTRY_EXTRA_PRESENT && \
|
|
cb > sizeof( HEAP_FREE_ENTRY_EXTRA )) { \
|
|
\
|
|
cb -= sizeof( HEAP_FREE_ENTRY_EXTRA ); \
|
|
} \
|
|
\
|
|
cbEqual = RtlCompareMemoryUlong( (PCHAR)((FB) + 1), \
|
|
cb, \
|
|
FREE_HEAP_FILL ); \
|
|
\
|
|
if (cbEqual != cb) { \
|
|
\
|
|
HeapDebugPrint(( \
|
|
"HEAP: Free Heap block %lx modified at %lx after it was freed\n", \
|
|
(FB), \
|
|
(PCHAR)((FB) + 1) + cbEqual )); \
|
|
\
|
|
HeapDebugBreak((FB)); \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpRemoveFreeBlock does no fill checking
|
|
//
|
|
|
|
#define RtlpFastRemoveFreeBlock( H, FB ) \
|
|
{ \
|
|
PLIST_ENTRY _EX_Blink; \
|
|
PLIST_ENTRY _EX_Flink; \
|
|
RtlpUpdateIndexRemoveBlock(H, FB); \
|
|
\
|
|
_EX_Flink = (FB)->FreeList.Flink; \
|
|
_EX_Blink = (FB)->FreeList.Blink; \
|
|
\
|
|
_EX_Blink->Flink = _EX_Flink; \
|
|
_EX_Flink->Blink = _EX_Blink; \
|
|
\
|
|
if ((_EX_Flink == _EX_Blink) && \
|
|
((FB)->Size < HEAP_MAXIMUM_FREELISTS)) { \
|
|
\
|
|
CLEAR_FREELIST_BIT( H, FB ); \
|
|
} \
|
|
RtlpValidateNonDedicatedList(H); \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpRemoveFreeBlock only works for dedicated free lists
|
|
// (where we know that (FB)->Mask != 0) and doesn't do any fill checking
|
|
//
|
|
|
|
#define RtlpFastRemoveDedicatedFreeBlock( H, FB ) \
|
|
{ \
|
|
PLIST_ENTRY _EX_Blink; \
|
|
PLIST_ENTRY _EX_Flink; \
|
|
\
|
|
_EX_Flink = (FB)->FreeList.Flink; \
|
|
_EX_Blink = (FB)->FreeList.Blink; \
|
|
\
|
|
_EX_Blink->Flink = _EX_Flink; \
|
|
_EX_Flink->Blink = _EX_Blink; \
|
|
\
|
|
if (_EX_Flink == _EX_Blink) { \
|
|
\
|
|
CLEAR_FREELIST_BIT( H, FB ); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// This version of RtlpRemoveFreeBlock only works for dedicated free lists
|
|
// (where we know that (FB)->Mask == 0) and doesn't do any fill checking
|
|
//
|
|
|
|
#define RtlpFastRemoveNonDedicatedFreeBlock( H, FB ) \
|
|
{ \
|
|
RtlpUpdateIndexRemoveBlock(H, FB); \
|
|
RemoveEntryList(&(FB)->FreeList); \
|
|
RtlpValidateNonDedicatedList(H); \
|
|
}
|
|
|
|
|
|
//
|
|
// Heap tagging routines implemented in heapdll.c
|
|
//
|
|
|
|
#if DBG
|
|
|
|
#define IS_HEAP_TAGGING_ENABLED() (TRUE)
|
|
|
|
#else
|
|
|
|
#define IS_HEAP_TAGGING_ENABLED() (RtlGetNtGlobalFlags() & FLG_HEAP_ENABLE_TAGGING)
|
|
|
|
#endif // DBG
|
|
|
|
//
|
|
// ORDER IS IMPORTANT HERE...SEE RtlpUpdateTagEntry sources
|
|
//
|
|
|
|
typedef enum _HEAP_TAG_ACTION {
|
|
|
|
AllocationAction,
|
|
VirtualAllocationAction,
|
|
FreeAction,
|
|
VirtualFreeAction,
|
|
ReAllocationAction,
|
|
VirtualReAllocationAction
|
|
|
|
} HEAP_TAG_ACTION;
|
|
|
|
PWSTR
|
|
RtlpGetTagName (
|
|
PHEAP Heap,
|
|
USHORT TagIndex
|
|
);
|
|
|
|
USHORT
|
|
RtlpUpdateTagEntry (
|
|
PHEAP Heap,
|
|
USHORT TagIndex,
|
|
SIZE_T OldSize, // Only valid for ReAllocation and Free actions
|
|
SIZE_T NewSize, // Only valid for ReAllocation and Allocation actions
|
|
HEAP_TAG_ACTION Action
|
|
);
|
|
|
|
VOID
|
|
RtlpResetTags (
|
|
PHEAP Heap
|
|
);
|
|
|
|
VOID
|
|
RtlpDestroyTags (
|
|
PHEAP Heap
|
|
);
|
|
|
|
|
|
//
|
|
// Define heap lookaside list allocation functions.
|
|
//
|
|
|
|
typedef struct _HEAP_LOOKASIDE {
|
|
SLIST_HEADER ListHead;
|
|
|
|
USHORT Depth;
|
|
USHORT MaximumDepth;
|
|
|
|
ULONG TotalAllocates;
|
|
ULONG AllocateMisses;
|
|
ULONG TotalFrees;
|
|
ULONG FreeMisses;
|
|
|
|
ULONG LastTotalAllocates;
|
|
ULONG LastAllocateMisses;
|
|
|
|
ULONG Counters[2];
|
|
|
|
} HEAP_LOOKASIDE, *PHEAP_LOOKASIDE;
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
RtlpInitializeHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside,
|
|
IN USHORT Depth
|
|
);
|
|
|
|
NTKERNELAPI
|
|
VOID
|
|
RtlpDeleteHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
);
|
|
|
|
VOID
|
|
RtlpAdjustHeapLookasideDepth (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
);
|
|
|
|
NTKERNELAPI
|
|
PVOID
|
|
RtlpAllocateFromHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
);
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
RtlpFreeToHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside,
|
|
IN PVOID Entry
|
|
);
|
|
|
|
#ifndef NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// Low Fragmentation Heap data structures and internal APIs
|
|
//
|
|
|
|
//
|
|
// The memory barrier exists on IA64 only
|
|
//
|
|
|
|
#if defined(_IA64_)
|
|
|
|
#define RtlMemoryBarrier() __mf ()
|
|
|
|
#else // #if defined(_IA64_)
|
|
|
|
//
|
|
// On x86 and AMD64 ignore the memory barrier
|
|
//
|
|
|
|
#define RtlMemoryBarrier()
|
|
|
|
#endif // #if defined(_IA64_)
|
|
|
|
extern ULONG RtlpDisableHeapLookaside;
|
|
|
|
#define HEAP_ENABLE_LOW_FRAG_HEAP 8
|
|
|
|
typedef struct _BLOCK_ENTRY {
|
|
|
|
HEAP_ENTRY;
|
|
|
|
USHORT LinkOffset;
|
|
USHORT Reserved2;
|
|
|
|
} BLOCK_ENTRY, *PBLOCK_ENTRY;
|
|
|
|
|
|
typedef struct _INTERLOCK_SEQ {
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
USHORT Depth;
|
|
USHORT FreeEntryOffset;
|
|
};
|
|
volatile ULONG OffsetAndDepth;
|
|
};
|
|
volatile ULONG Sequence;
|
|
};
|
|
|
|
volatile LONGLONG Exchg;
|
|
};
|
|
|
|
} INTERLOCK_SEQ, *PINTERLOCK_SEQ;
|
|
|
|
struct _HEAP_USERDATA_HEADER;
|
|
|
|
typedef struct _HEAP_SUBSEGMENT {
|
|
|
|
PVOID Bucket;
|
|
|
|
volatile struct _HEAP_USERDATA_HEADER * UserBlocks;
|
|
|
|
INTERLOCK_SEQ AggregateExchg;
|
|
|
|
union {
|
|
|
|
struct {
|
|
USHORT BlockSize;
|
|
USHORT FreeThreshold;
|
|
USHORT BlockCount;
|
|
UCHAR SizeIndex;
|
|
UCHAR AffinityIndex;
|
|
};
|
|
ULONG Alignment[2];
|
|
};
|
|
|
|
SINGLE_LIST_ENTRY SFreeListEntry;
|
|
volatile ULONG Lock;
|
|
|
|
} HEAP_SUBSEGMENT, *PHEAP_SUBSEGMENT;
|
|
|
|
typedef struct _HEAP_USERDATA_HEADER {
|
|
|
|
union {
|
|
|
|
SINGLE_LIST_ENTRY SFreeListEntry;
|
|
PHEAP_SUBSEGMENT SubSegment;
|
|
};
|
|
|
|
PVOID HeapHandle;
|
|
|
|
ULONG_PTR SizeIndex;
|
|
ULONG_PTR Signature;
|
|
|
|
} HEAP_USERDATA_HEADER, *PHEAP_USERDATA_HEADER;
|
|
|
|
#define HEAP_LFH_INDEX 0xFF
|
|
#define HEAP_LFH_IN_CONVERSION 0xFE
|
|
|
|
#define HEAP_NO_CACHE_BLOCK 0x800000
|
|
#define HEAP_LARGEST_LFH_BLOCK 0x4000
|
|
#define HEAP_LFH_USER_SIGNATURE 0xF0E0D0C0
|
|
|
|
#ifdef DISABLE_REGISTRY_TEST_HOOKS
|
|
|
|
#define RtlpIsLowFragHeapEnabled() FALSE
|
|
|
|
#else //DISABLE_REGISTRY_TEST_HOOKS
|
|
|
|
#define RtlpIsLowFragHeapEnabled() \
|
|
((RtlpDisableHeapLookaside & HEAP_ENABLE_LOW_FRAG_HEAP) != 0)
|
|
|
|
#endif //DISABLE_REGISTRY_TEST_HOOKS
|
|
|
|
|
|
ULONG
|
|
FORCEINLINE
|
|
RtlpGetAllocationUnits(
|
|
PHEAP Heap,
|
|
PHEAP_ENTRY Block
|
|
)
|
|
{
|
|
|
|
PHEAP_SUBSEGMENT SubSegment = (PHEAP_SUBSEGMENT)Block->SubSegment;
|
|
|
|
RtlMemoryBarrier();
|
|
|
|
if (Block->SegmentIndex == HEAP_LFH_INDEX) {
|
|
|
|
ULONG ReturnSize = *((volatile USHORT *)&SubSegment->BlockSize);
|
|
|
|
//
|
|
// ISSUE: Workaround the x86 compiler bug which eliminates the second test
|
|
// if (Block->SegmentIndex == HEAP_LFH_INDEX)
|
|
//
|
|
|
|
#if !defined(_WIN64)
|
|
_asm {
|
|
|
|
nop
|
|
}
|
|
#endif
|
|
|
|
RtlMemoryBarrier();
|
|
|
|
if (Block->SegmentIndex == HEAP_LFH_INDEX) {
|
|
|
|
return ReturnSize;
|
|
}
|
|
}
|
|
|
|
if (Block->SegmentIndex == HEAP_LFH_IN_CONVERSION) {
|
|
|
|
//
|
|
// This should be a very rare case when this query is
|
|
// done in the small window when the conversion code sets the
|
|
// Size & PrevSize fields.
|
|
//
|
|
|
|
RtlLockHeap(Heap);
|
|
RtlUnlockHeap(Heap);
|
|
|
|
//
|
|
// This makes sure the conversion completed, so we can grab the
|
|
// block size like for a regular block
|
|
//
|
|
|
|
}
|
|
|
|
return Block->Size;
|
|
}
|
|
|
|
VOID
|
|
FORCEINLINE
|
|
RtlpSetUnusedBytes(PHEAP Heap, PHEAP_ENTRY Block, SIZE_T UnusedBytes)
|
|
{
|
|
if (UnusedBytes < 0xff) {
|
|
|
|
Block->UnusedBytes = (UCHAR)(UnusedBytes);
|
|
|
|
} else {
|
|
|
|
PSIZE_T UnusedBytesULong = (PSIZE_T)(Block + RtlpGetAllocationUnits(Heap, Block));
|
|
|
|
UnusedBytesULong -= 1;
|
|
Block->UnusedBytes = 0xff;
|
|
*UnusedBytesULong = UnusedBytes;
|
|
}
|
|
}
|
|
|
|
SIZE_T
|
|
FORCEINLINE
|
|
RtlpGetUnusedBytes(PHEAP Heap, PHEAP_ENTRY Block)
|
|
{
|
|
if (Block->UnusedBytes < 0xff) {
|
|
|
|
return Block->UnusedBytes;
|
|
|
|
} else {
|
|
|
|
PSIZE_T UnusedBytesULong = (PSIZE_T)(Block + RtlpGetAllocationUnits(Heap, Block));
|
|
UnusedBytesULong -= 1;
|
|
|
|
return (*UnusedBytesULong);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RtlpInitializeLowFragHeapManager();
|
|
|
|
HANDLE
|
|
FASTCALL
|
|
RtlpCreateLowFragHeap(
|
|
HANDLE Heap
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
RtlpDestroyLowFragHeap(
|
|
HANDLE LowFragHeapHandle
|
|
);
|
|
|
|
PVOID
|
|
FASTCALL
|
|
RtlpLowFragHeapAlloc(
|
|
HANDLE LowFragHeapHandle,
|
|
SIZE_T BlockSize
|
|
);
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
RtlpLowFragHeapFree(
|
|
HANDLE LowFragHeapHandle,
|
|
PVOID p
|
|
);
|
|
|
|
NTSTATUS
|
|
RtlpActivateLowFragmentationHeap(
|
|
IN PVOID HeapHandle
|
|
);
|
|
|
|
#else // NTOS_KERNEL_RUNTIME
|
|
|
|
//
|
|
// The kernel mode heap does not ajdust the heap granularity
|
|
// therefore the unused bytes always fit the UCHAR.
|
|
// No need to check for overflow here
|
|
//
|
|
|
|
ULONG
|
|
FORCEINLINE
|
|
RtlpGetAllocationUnits(
|
|
PHEAP Heap,
|
|
PHEAP_ENTRY Block
|
|
)
|
|
{
|
|
return Block->Size;
|
|
}
|
|
|
|
VOID
|
|
FORCEINLINE
|
|
RtlpSetUnusedBytes(PHEAP Heap, PHEAP_ENTRY Block, SIZE_T UnusedBytes)
|
|
{
|
|
Block->UnusedBytes = (UCHAR)(UnusedBytes);
|
|
}
|
|
|
|
SIZE_T
|
|
FORCEINLINE
|
|
RtlpGetUnusedBytes(PHEAP Heap, PHEAP_ENTRY Block)
|
|
{
|
|
return Block->UnusedBytes;
|
|
}
|
|
|
|
#endif // NTOS_KERNEL_RUNTIME
|
|
|
|
#endif // _RTL_HEAP_PRIVATE_
|