mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
2441 lines
70 KiB
C
2441 lines
70 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: lib/rtl/heappage.c
|
|
* PURPOSE: RTL Page Heap implementation
|
|
* PROGRAMMERS: Copyright 2011 Aleksey Bragin
|
|
*/
|
|
|
|
/* Useful references:
|
|
http://msdn.microsoft.com/en-us/library/ms220938(VS.80).aspx
|
|
http://blogs.msdn.com/b/jiangyue/archive/2010/03/16/windows-heap-overrun-monitoring.aspx
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <rtl.h>
|
|
#include <heap.h>
|
|
#include <reactos/verifier.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* TYPES **********************************************************************/
|
|
|
|
typedef struct _DPH_BLOCK_INFORMATION
|
|
{
|
|
ULONG StartStamp;
|
|
PVOID Heap;
|
|
SIZE_T RequestedSize;
|
|
SIZE_T ActualSize;
|
|
union
|
|
{
|
|
LIST_ENTRY FreeQueue;
|
|
SINGLE_LIST_ENTRY FreePushList;
|
|
WORD TraceIndex;
|
|
};
|
|
PVOID StackTrace;
|
|
ULONG EndStamp;
|
|
} DPH_BLOCK_INFORMATION, *PDPH_BLOCK_INFORMATION;
|
|
|
|
typedef struct _DPH_HEAP_BLOCK
|
|
{
|
|
union
|
|
{
|
|
struct _DPH_HEAP_BLOCK *pNextAlloc;
|
|
LIST_ENTRY AvailableEntry;
|
|
RTL_BALANCED_LINKS TableLinks;
|
|
};
|
|
PUCHAR pUserAllocation;
|
|
PUCHAR pVirtualBlock;
|
|
SIZE_T nVirtualBlockSize;
|
|
SIZE_T nVirtualAccessSize;
|
|
SIZE_T nUserRequestedSize;
|
|
SIZE_T nUserActualSize;
|
|
PVOID UserValue;
|
|
ULONG UserFlags;
|
|
PRTL_TRACE_BLOCK StackTrace;
|
|
LIST_ENTRY AdjacencyEntry;
|
|
PUCHAR pVirtualRegion;
|
|
} DPH_HEAP_BLOCK, *PDPH_HEAP_BLOCK;
|
|
|
|
typedef struct _DPH_HEAP_ROOT
|
|
{
|
|
ULONG Signature;
|
|
ULONG HeapFlags;
|
|
PHEAP_LOCK HeapCritSect;
|
|
ULONG nRemoteLockAcquired;
|
|
|
|
PDPH_HEAP_BLOCK pVirtualStorageListHead;
|
|
PDPH_HEAP_BLOCK pVirtualStorageListTail;
|
|
ULONG nVirtualStorageRanges;
|
|
SIZE_T nVirtualStorageBytes;
|
|
|
|
RTL_AVL_TABLE BusyNodesTable;
|
|
PDPH_HEAP_BLOCK NodeToAllocate;
|
|
ULONG nBusyAllocations;
|
|
SIZE_T nBusyAllocationBytesCommitted;
|
|
|
|
PDPH_HEAP_BLOCK pFreeAllocationListHead;
|
|
PDPH_HEAP_BLOCK pFreeAllocationListTail;
|
|
ULONG nFreeAllocations;
|
|
SIZE_T nFreeAllocationBytesCommitted;
|
|
|
|
LIST_ENTRY AvailableAllocationHead;
|
|
ULONG nAvailableAllocations;
|
|
SIZE_T nAvailableAllocationBytesCommitted;
|
|
|
|
PDPH_HEAP_BLOCK pUnusedNodeListHead;
|
|
PDPH_HEAP_BLOCK pUnusedNodeListTail;
|
|
ULONG nUnusedNodes;
|
|
SIZE_T nBusyAllocationBytesAccessible;
|
|
PDPH_HEAP_BLOCK pNodePoolListHead;
|
|
PDPH_HEAP_BLOCK pNodePoolListTail;
|
|
ULONG nNodePools;
|
|
SIZE_T nNodePoolBytes;
|
|
|
|
LIST_ENTRY NextHeap;
|
|
ULONG ExtraFlags;
|
|
ULONG Seed;
|
|
PVOID NormalHeap;
|
|
PRTL_TRACE_BLOCK CreateStackTrace;
|
|
PVOID FirstThread;
|
|
} DPH_HEAP_ROOT, *PDPH_HEAP_ROOT;
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
BOOLEAN RtlpPageHeapEnabled = FALSE;
|
|
ULONG RtlpDphGlobalFlags;
|
|
ULONG RtlpPageHeapSizeRangeStart, RtlpPageHeapSizeRangeEnd;
|
|
ULONG RtlpPageHeapDllRangeStart, RtlpPageHeapDllRangeEnd;
|
|
WCHAR RtlpDphTargetDlls[512];
|
|
|
|
LIST_ENTRY RtlpDphPageHeapList;
|
|
BOOLEAN RtlpDphPageHeapListInitialized;
|
|
HEAP_LOCK _RtlpDphPageHeapListLock;
|
|
PHEAP_LOCK RtlpDphPageHeapListLock = &_RtlpDphPageHeapListLock;
|
|
ULONG RtlpDphPageHeapListLength;
|
|
UNICODE_STRING RtlpDphTargetDllsUnicode;
|
|
|
|
HEAP_LOCK _RtlpDphDelayedFreeQueueLock;
|
|
PHEAP_LOCK RtlpDphDelayedFreeQueueLock = &_RtlpDphDelayedFreeQueueLock;
|
|
LIST_ENTRY RtlpDphDelayedFreeQueue;
|
|
SLIST_HEADER RtlpDphDelayedTemporaryPushList;
|
|
SIZE_T RtlpDphMemoryUsedByDelayedFreeBlocks;
|
|
ULONG RtlpDphNumberOfDelayedFreeBlocks;
|
|
|
|
/* Counters */
|
|
LONG RtlpDphCounter;
|
|
LONG RtlpDphAllocFails;
|
|
LONG RtlpDphReleaseFails;
|
|
LONG RtlpDphFreeFails;
|
|
LONG RtlpDphProtectFails;
|
|
|
|
#define DPH_RESERVE_SIZE 0x100000
|
|
#define DPH_POOL_SIZE 0x4000
|
|
#define DPH_FREE_LIST_MINIMUM 8
|
|
|
|
/* RtlpDphBreakOptions */
|
|
#define DPH_BREAK_ON_RESERVE_FAIL 0x01
|
|
#define DPH_BREAK_ON_COMMIT_FAIL 0x02
|
|
#define DPH_BREAK_ON_RELEASE_FAIL 0x04
|
|
#define DPH_BREAK_ON_FREE_FAIL 0x08
|
|
#define DPH_BREAK_ON_PROTECT_FAIL 0x10
|
|
#define DPH_BREAK_ON_NULL_FREE 0x80
|
|
|
|
/* RtlpDphDebugOptions */
|
|
#define DPH_DEBUG_INTERNAL_VALIDATE 0x01
|
|
#define DPH_DEBUG_VERBOSE 0x04
|
|
|
|
/* DPH ExtraFlags */
|
|
#define DPH_EXTRA_LOG_STACK_TRACES 0x02
|
|
#define DPH_EXTRA_CHECK_UNDERRUN 0x10
|
|
|
|
/* Fillers */
|
|
#define DPH_FILL 0xEEEEEEEE
|
|
#define DPH_FILL_START_STAMP_1 0xABCDBBBB
|
|
#define DPH_FILL_START_STAMP_2 0xABCDBBBA
|
|
#define DPH_FILL_END_STAMP_1 0xDCBABBBB
|
|
#define DPH_FILL_END_STAMP_2 0xDCBABBBA
|
|
#define DPH_FILL_SUFFIX 0xD0
|
|
#define DPH_FILL_INFIX 0xC0
|
|
|
|
/* Validation info flags */
|
|
#define DPH_VALINFO_BAD_START_STAMP 0x01
|
|
#define DPH_VALINFO_BAD_END_STAMP 0x02
|
|
#define DPH_VALINFO_BAD_POINTER 0x04
|
|
#define DPH_VALINFO_BAD_PREFIX_PATTERN 0x08
|
|
#define DPH_VALINFO_BAD_SUFFIX_PATTERN 0x10
|
|
#define DPH_VALINFO_EXCEPTION 0x20
|
|
#define DPH_VALINFO_1 0x40
|
|
#define DPH_VALINFO_BAD_INFIX_PATTERN 0x80
|
|
#define DPH_VALINFO_ALREADY_FREED 0x100
|
|
#define DPH_VALINFO_CORRUPTED_AFTER_FREE 0x200
|
|
|
|
/* Signatures */
|
|
#define DPH_SIGNATURE 0xFFEEDDCC
|
|
|
|
/* Biased pointer macros */
|
|
#define IS_BIASED_POINTER(ptr) ((ULONG_PTR)(ptr) & 1)
|
|
#define POINTER_REMOVE_BIAS(ptr) ((ULONG_PTR)(ptr) & ~(ULONG_PTR)1)
|
|
#define POINTER_ADD_BIAS(ptr) ((ULONG_PTR)(ptr) | 1)
|
|
|
|
|
|
ULONG RtlpDphBreakOptions = 0;//0xFFFFFFFF;
|
|
ULONG RtlpDphDebugOptions;
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphGrowVirtual(PDPH_HEAP_ROOT DphRoot, SIZE_T Size);
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphIsNormalFreeHeapBlock(PVOID Block, PULONG ValidationInformation, BOOLEAN CheckFillers);
|
|
|
|
VOID NTAPI
|
|
RtlpDphReportCorruptedBlock(
|
|
_In_ PDPH_HEAP_ROOT DphRoot,
|
|
_In_ ULONG Reserved,
|
|
_In_ PVOID Block,
|
|
_In_ ULONG ValidationInfo);
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphNormalHeapValidate(PDPH_HEAP_ROOT DphRoot, ULONG Flags, PVOID BaseAddress);
|
|
|
|
|
|
VOID NTAPI
|
|
RtlpDphRaiseException(NTSTATUS Status)
|
|
{
|
|
EXCEPTION_RECORD Exception;
|
|
|
|
/* Initialize exception record */
|
|
Exception.ExceptionCode = Status;
|
|
Exception.ExceptionAddress = RtlpDphRaiseException;
|
|
Exception.ExceptionFlags = 0;
|
|
Exception.ExceptionRecord = NULL;
|
|
Exception.NumberParameters = 0;
|
|
|
|
/* Raise the exception */
|
|
RtlRaiseException(&Exception);
|
|
}
|
|
|
|
PVOID NTAPI
|
|
RtlpDphPointerFromHandle(PVOID Handle)
|
|
{
|
|
PHEAP NormalHeap = (PHEAP)Handle;
|
|
PDPH_HEAP_ROOT DphHeap = (PDPH_HEAP_ROOT)((PUCHAR)Handle + PAGE_SIZE);
|
|
|
|
if (NormalHeap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
|
|
{
|
|
if (DphHeap->Signature == DPH_SIGNATURE)
|
|
return DphHeap;
|
|
}
|
|
|
|
DPRINT1("heap handle with incorrect signature\n");
|
|
DbgBreakPoint();
|
|
return NULL;
|
|
}
|
|
|
|
PVOID NTAPI
|
|
RtlpDphHeapFromPointer(PDPH_HEAP_ROOT DphHeap)
|
|
{
|
|
return ((PUCHAR)DphHeap) - PAGE_SIZE;
|
|
}
|
|
|
|
ULONG NTAPI
|
|
RtlpDphGetBlockSizeFromCorruptedBlock(PVOID Block)
|
|
{
|
|
PDPH_BLOCK_INFORMATION BlockInfo;
|
|
BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
|
|
|
|
/* Check stamps */
|
|
if (BlockInfo->StartStamp != DPH_FILL_START_STAMP_1 && BlockInfo->StartStamp != DPH_FILL_START_STAMP_2)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return BlockInfo->RequestedSize;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphEnterCriticalSection(PDPH_HEAP_ROOT DphRoot, ULONG Flags)
|
|
{
|
|
if (Flags & HEAP_NO_SERIALIZE)
|
|
{
|
|
/* More complex scenario */
|
|
if (!RtlTryEnterHeapLock(DphRoot->HeapCritSect, TRUE))
|
|
{
|
|
if (!DphRoot->nRemoteLockAcquired)
|
|
{
|
|
DPRINT1("multithreaded access in HEAP_NO_SERIALIZE heap\n");
|
|
DbgBreakPoint();
|
|
|
|
/* Clear out the no serialize flag */
|
|
DphRoot->HeapFlags &= ~HEAP_NO_SERIALIZE;
|
|
}
|
|
|
|
/* Enter the heap's critical section */
|
|
RtlEnterHeapLock(DphRoot->HeapCritSect, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Just enter the heap's critical section */
|
|
RtlEnterHeapLock(DphRoot->HeapCritSect, TRUE);
|
|
}
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphLeaveCriticalSection(PDPH_HEAP_ROOT DphRoot)
|
|
{
|
|
/* Just leave the heap's critical section */
|
|
RtlLeaveHeapLock(DphRoot->HeapCritSect);
|
|
}
|
|
|
|
|
|
VOID NTAPI
|
|
RtlpDphPreProcessing(PDPH_HEAP_ROOT DphRoot, ULONG Flags)
|
|
{
|
|
RtlpDphEnterCriticalSection(DphRoot, Flags);
|
|
|
|
/* FIXME: Validate integrity, internal lists if necessary */
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphPostProcessing(PDPH_HEAP_ROOT DphRoot)
|
|
{
|
|
if (!DphRoot) return;
|
|
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
{
|
|
/* FIXME: Validate integrity, internal lists if necessary */
|
|
}
|
|
|
|
/* Release the lock */
|
|
RtlpDphLeaveCriticalSection(DphRoot);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpSecMemFreeVirtualMemory(HANDLE Process, PVOID *Base, PSIZE_T Size, ULONG Type)
|
|
{
|
|
NTSTATUS Status;
|
|
//PVOID *SavedBase = Base;
|
|
//PSIZE_T SavedSize = Size;
|
|
|
|
/* Free the memory */
|
|
Status = ZwFreeVirtualMemory(Process, Base, Size, Type);
|
|
|
|
/* Flush secure memory cache if needed and retry freeing */
|
|
#if 0
|
|
if (Status == STATUS_INVALID_PAGE_PROTECTION &&
|
|
Process == NtCurrentProcess() &&
|
|
RtlFlushSecureMemoryCache(*SavedBase, *SavedSize))
|
|
{
|
|
Status = ZwFreeVirtualMemory(NtCurrentProcess(), SavedBase, SavedSize, Type);
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphAllocateVm(PVOID *Base, SIZE_T Size, ULONG Type, ULONG Protection)
|
|
{
|
|
NTSTATUS Status;
|
|
Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
|
Base,
|
|
0,
|
|
&Size,
|
|
Type,
|
|
Protection);
|
|
DPRINT("Page heap: AllocVm (%p, %Ix, %lx) status %lx\n", Base, Size, Type, Status);
|
|
/* Check for failures */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Type == MEM_RESERVE)
|
|
{
|
|
_InterlockedIncrement(&RtlpDphCounter);
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_RESERVE_FAIL)
|
|
{
|
|
DPRINT1("Page heap: AllocVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
|
|
DbgBreakPoint();
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_InterlockedIncrement(&RtlpDphAllocFails);
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_COMMIT_FAIL)
|
|
{
|
|
DPRINT1("Page heap: AllocVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
|
|
DbgBreakPoint();
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphFreeVm(PVOID Base, SIZE_T Size, ULONG Type)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Free the memory */
|
|
Status = RtlpSecMemFreeVirtualMemory(NtCurrentProcess(), &Base, &Size, Type);
|
|
DPRINT("Page heap: FreeVm (%p, %Ix, %lx) status %lx\n", Base, Size, Type, Status);
|
|
/* Log/report failures */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Type == MEM_RELEASE)
|
|
{
|
|
_InterlockedIncrement(&RtlpDphReleaseFails);
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_RELEASE_FAIL)
|
|
{
|
|
DPRINT1("Page heap: FreeVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
|
|
DbgBreakPoint();
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_InterlockedIncrement(&RtlpDphFreeFails);
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_FREE_FAIL)
|
|
{
|
|
DPRINT1("Page heap: FreeVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
|
|
DbgBreakPoint();
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphProtectVm(PVOID Base, SIZE_T Size, ULONG Protection)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG OldProtection;
|
|
|
|
/* Change protection */
|
|
Status = ZwProtectVirtualMemory(NtCurrentProcess(), &Base, &Size, Protection, &OldProtection);
|
|
|
|
/* Log/report failures */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
_InterlockedIncrement(&RtlpDphProtectFails);
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_PROTECT_FAIL)
|
|
{
|
|
DPRINT1("Page heap: ProtectVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Protection, Status);
|
|
DbgBreakPoint();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphWritePageHeapBlockInformation(PDPH_HEAP_ROOT DphRoot, PVOID UserAllocation, SIZE_T Size, SIZE_T UserSize)
|
|
{
|
|
PDPH_BLOCK_INFORMATION BlockInfo;
|
|
PUCHAR FillPtr;
|
|
|
|
/* Get pointer to the block info structure */
|
|
BlockInfo = (PDPH_BLOCK_INFORMATION)UserAllocation - 1;
|
|
|
|
/* Set up basic fields */
|
|
BlockInfo->Heap = DphRoot;
|
|
BlockInfo->ActualSize = UserSize;
|
|
BlockInfo->RequestedSize = Size;
|
|
BlockInfo->StartStamp = DPH_FILL_START_STAMP_1;
|
|
BlockInfo->EndStamp = DPH_FILL_END_STAMP_1;
|
|
|
|
/* Fill with a pattern */
|
|
FillPtr = (PUCHAR)UserAllocation + Size;
|
|
RtlFillMemory(FillPtr, ROUND_UP(FillPtr, PAGE_SIZE) - (ULONG_PTR)FillPtr, DPH_FILL_SUFFIX);
|
|
|
|
/* FIXME: Check if logging stack traces is turned on */
|
|
//if (DphRoot->ExtraFlags &
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphPlaceOnBusyList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK DphNode)
|
|
{
|
|
BOOLEAN NewElement;
|
|
PVOID AddressUserData;
|
|
|
|
DPRINT("RtlpDphPlaceOnBusyList(%p %p)\n", DphRoot, DphNode);
|
|
|
|
/* Add it to the AVL busy nodes table */
|
|
DphRoot->NodeToAllocate = DphNode;
|
|
AddressUserData = RtlInsertElementGenericTableAvl(&DphRoot->BusyNodesTable,
|
|
&DphNode->pUserAllocation,
|
|
sizeof(ULONG_PTR),
|
|
&NewElement);
|
|
|
|
ASSERT(AddressUserData == &DphNode->pUserAllocation);
|
|
ASSERT(NewElement == TRUE);
|
|
|
|
/* Update heap counters */
|
|
DphRoot->nBusyAllocations++;
|
|
DphRoot->nBusyAllocationBytesAccessible += DphNode->nVirtualAccessSize;
|
|
DphRoot->nBusyAllocationBytesCommitted += DphNode->nVirtualBlockSize;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphPlaceOnFreeList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
|
|
{
|
|
DPRINT("RtlpDphPlaceOnFreeList(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Node is being added to the tail of the list */
|
|
Node->pNextAlloc = NULL;
|
|
|
|
/* Add it to the tail of the linked list */
|
|
if (DphRoot->pFreeAllocationListTail)
|
|
DphRoot->pFreeAllocationListTail->pNextAlloc = Node;
|
|
else
|
|
DphRoot->pFreeAllocationListHead = Node;
|
|
DphRoot->pFreeAllocationListTail = Node;
|
|
|
|
/* Update byte counts taking in account this new node */
|
|
DphRoot->nFreeAllocations++;
|
|
DphRoot->nFreeAllocationBytesCommitted += Node->nVirtualBlockSize;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphPlaceOnPoolList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
|
|
{
|
|
DPRINT("RtlpDphPlaceOnPoolList(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Node is being added to the tail of the list */
|
|
Node->pNextAlloc = NULL;
|
|
|
|
/* Add it to the tail of the linked list */
|
|
if (DphRoot->pNodePoolListTail)
|
|
DphRoot->pNodePoolListTail->pNextAlloc = Node;
|
|
else
|
|
DphRoot->pNodePoolListHead = Node;
|
|
DphRoot->pNodePoolListTail = Node;
|
|
|
|
/* Update byte counts taking in account this new node */
|
|
DphRoot->nNodePools++;
|
|
DphRoot->nNodePoolBytes += Node->nVirtualBlockSize;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphPlaceOnVirtualList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
|
|
{
|
|
DPRINT("RtlpDphPlaceOnVirtualList(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Add it to the head of the virtual list */
|
|
Node->pNextAlloc = DphRoot->pVirtualStorageListHead;
|
|
if (!DphRoot->pVirtualStorageListHead)
|
|
DphRoot->pVirtualStorageListTail = Node;
|
|
DphRoot->pVirtualStorageListHead = Node;
|
|
|
|
/* Update byte counts taking in account this new node */
|
|
DphRoot->nVirtualStorageRanges++;
|
|
DphRoot->nVirtualStorageBytes += Node->nVirtualBlockSize;
|
|
}
|
|
|
|
PDPH_HEAP_BLOCK NTAPI
|
|
RtlpDphTakeNodeFromUnusedList(PDPH_HEAP_ROOT DphRoot)
|
|
{
|
|
PDPH_HEAP_BLOCK Node = DphRoot->pUnusedNodeListHead;
|
|
PDPH_HEAP_BLOCK Next;
|
|
|
|
DPRINT("RtlpDphTakeNodeFromUnusedList(%p), ret %p\n", DphRoot, Node);
|
|
|
|
/* Take the first entry */
|
|
if (!Node) return NULL;
|
|
|
|
/* Remove that entry (Node) from the list */
|
|
Next = Node->pNextAlloc;
|
|
if (DphRoot->pUnusedNodeListHead == Node) DphRoot->pUnusedNodeListHead = Next;
|
|
if (DphRoot->pUnusedNodeListTail == Node) DphRoot->pUnusedNodeListTail = NULL;
|
|
|
|
/* Decrease amount of unused nodes */
|
|
DphRoot->nUnusedNodes--;
|
|
|
|
return Node;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphReturnNodeToUnusedList(PDPH_HEAP_ROOT DphRoot,
|
|
PDPH_HEAP_BLOCK Node)
|
|
{
|
|
DPRINT("RtlpDphReturnNodeToUnusedList(%p, %p)\n", DphRoot, Node);
|
|
|
|
/* Add it back to the head of the unused list */
|
|
Node->pNextAlloc = DphRoot->pUnusedNodeListHead;
|
|
if (!DphRoot->pUnusedNodeListHead)
|
|
DphRoot->pUnusedNodeListTail = Node;
|
|
DphRoot->pUnusedNodeListHead = Node;
|
|
|
|
/* Increase amount of unused nodes */
|
|
DphRoot->nUnusedNodes++;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphRemoveFromAvailableList(PDPH_HEAP_ROOT DphRoot,
|
|
PDPH_HEAP_BLOCK Node)
|
|
{
|
|
/* Make sure Adjacency list pointers are biased */
|
|
//ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Flink));
|
|
//ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Blink));
|
|
|
|
DPRINT("RtlpDphRemoveFromAvailableList(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Check if it is in the list */
|
|
#if 0
|
|
{
|
|
PLIST_ENTRY CurEntry;
|
|
PDPH_HEAP_BLOCK NodeEntry;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
/* Find where to put this node according to its virtual address */
|
|
CurEntry = DphRoot->AvailableAllocationHead.Flink;
|
|
|
|
while (CurEntry != &DphRoot->AvailableAllocationHead)
|
|
{
|
|
NodeEntry = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
|
|
|
|
if (NodeEntry == Node)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
CurEntry = CurEntry->Flink;
|
|
}
|
|
|
|
if (!Found)
|
|
{
|
|
DPRINT1("Trying to remove non-existing in availlist node!\n");
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Remove it from the list */
|
|
RemoveEntryList(&Node->AvailableEntry);
|
|
|
|
/* Decrease heap counters */
|
|
DphRoot->nAvailableAllocations--;
|
|
DphRoot->nAvailableAllocationBytesCommitted -= Node->nVirtualBlockSize;
|
|
|
|
/* Remove bias from the AdjacencyEntry pointer */
|
|
Node->AdjacencyEntry.Flink = (PLIST_ENTRY)POINTER_REMOVE_BIAS(Node->AdjacencyEntry.Flink);
|
|
Node->AdjacencyEntry.Blink = (PLIST_ENTRY)POINTER_REMOVE_BIAS(Node->AdjacencyEntry.Blink);
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphRemoveFromBusyList(PDPH_HEAP_ROOT DphRoot,
|
|
PDPH_HEAP_BLOCK Node)
|
|
{
|
|
BOOLEAN ElementPresent;
|
|
|
|
DPRINT("RtlpDphRemoveFromBusyList(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Delete it from busy nodes table */
|
|
ElementPresent = RtlDeleteElementGenericTableAvl(&DphRoot->BusyNodesTable, &Node->pUserAllocation);
|
|
ASSERT(ElementPresent == TRUE);
|
|
|
|
/* Update counters */
|
|
DphRoot->nBusyAllocations--;
|
|
DphRoot->nBusyAllocationBytesCommitted -= Node->nVirtualBlockSize;
|
|
DphRoot->nBusyAllocationBytesAccessible -= Node->nVirtualAccessSize;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphRemoveFromFreeList(PDPH_HEAP_ROOT DphRoot,
|
|
PDPH_HEAP_BLOCK Node,
|
|
PDPH_HEAP_BLOCK Prev)
|
|
{
|
|
PDPH_HEAP_BLOCK Next;
|
|
|
|
DPRINT("RtlpDphRemoveFromFreeList(%p %p %p)\n", DphRoot, Node, Prev);
|
|
|
|
/* Detach it from the list */
|
|
Next = Node->pNextAlloc;
|
|
if (DphRoot->pFreeAllocationListHead == Node)
|
|
DphRoot->pFreeAllocationListHead = Next;
|
|
if (DphRoot->pFreeAllocationListTail == Node)
|
|
DphRoot->pFreeAllocationListTail = Prev;
|
|
if (Prev) Prev->pNextAlloc = Next;
|
|
|
|
/* Decrease heap counters */
|
|
DphRoot->nFreeAllocations--;
|
|
DphRoot->nFreeAllocationBytesCommitted -= Node->nVirtualBlockSize;
|
|
|
|
Node->StackTrace = NULL;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphCoalesceNodeIntoAvailable(PDPH_HEAP_ROOT DphRoot,
|
|
PDPH_HEAP_BLOCK Node)
|
|
{
|
|
PDPH_HEAP_BLOCK NodeEntry, PrevNode = NULL, NextNode;
|
|
PLIST_ENTRY AvailListHead;
|
|
PLIST_ENTRY CurEntry;
|
|
|
|
DPRINT("RtlpDphCoalesceNodeIntoAvailable(%p %p)\n", DphRoot, Node);
|
|
|
|
/* Update heap counters */
|
|
DphRoot->nAvailableAllocationBytesCommitted += Node->nVirtualBlockSize;
|
|
DphRoot->nAvailableAllocations++;
|
|
|
|
/* Find where to put this node according to its virtual address */
|
|
AvailListHead = &DphRoot->AvailableAllocationHead;
|
|
|
|
/* Find a point where to insert an available node */
|
|
CurEntry = AvailListHead->Flink;
|
|
|
|
while (CurEntry != AvailListHead)
|
|
{
|
|
NodeEntry = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
|
|
if (NodeEntry->pVirtualBlock >= Node->pVirtualBlock)
|
|
{
|
|
PrevNode = NodeEntry;
|
|
break;
|
|
}
|
|
CurEntry = CurEntry->Flink;
|
|
}
|
|
|
|
if (!PrevNode)
|
|
{
|
|
/* That means either this list is empty, or we should add to the head of it */
|
|
InsertHeadList(AvailListHead, &Node->AvailableEntry);
|
|
}
|
|
else
|
|
{
|
|
/* Check the previous node and merge if possible */
|
|
if (PrevNode->pVirtualBlock + PrevNode->nVirtualBlockSize == Node->pVirtualBlock)
|
|
{
|
|
/* Check they actually belong to the same virtual memory block */
|
|
NTSTATUS Status;
|
|
MEMORY_BASIC_INFORMATION MemoryBasicInfo;
|
|
|
|
Status = ZwQueryVirtualMemory(
|
|
ZwCurrentProcess(),
|
|
Node->pVirtualBlock,
|
|
MemoryBasicInformation,
|
|
&MemoryBasicInfo,
|
|
sizeof(MemoryBasicInfo),
|
|
NULL);
|
|
|
|
/* There is no way this can fail, we committed this memory! */
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if ((PUCHAR)MemoryBasicInfo.AllocationBase <= PrevNode->pVirtualBlock)
|
|
{
|
|
/* They are adjacent, and from the same VM region. - merge! */
|
|
PrevNode->nVirtualBlockSize += Node->nVirtualBlockSize;
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, Node);
|
|
DphRoot->nAvailableAllocations--;
|
|
|
|
Node = PrevNode;
|
|
}
|
|
else
|
|
{
|
|
/* Insert after PrevNode */
|
|
InsertTailList(&PrevNode->AvailableEntry, &Node->AvailableEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Insert after PrevNode */
|
|
InsertTailList(&PrevNode->AvailableEntry, &Node->AvailableEntry);
|
|
}
|
|
|
|
/* Now check the next entry after our one */
|
|
if (Node->AvailableEntry.Flink != AvailListHead)
|
|
{
|
|
NextNode = CONTAINING_RECORD(Node->AvailableEntry.Flink, DPH_HEAP_BLOCK, AvailableEntry);
|
|
/* Node is not at the tail of the list, check if it's adjacent */
|
|
if (Node->pVirtualBlock + Node->nVirtualBlockSize == NextNode->pVirtualBlock)
|
|
{
|
|
/* Check they actually belong to the same virtual memory block */
|
|
NTSTATUS Status;
|
|
MEMORY_BASIC_INFORMATION MemoryBasicInfo;
|
|
|
|
Status = ZwQueryVirtualMemory(
|
|
ZwCurrentProcess(),
|
|
NextNode->pVirtualBlock,
|
|
MemoryBasicInformation,
|
|
&MemoryBasicInfo,
|
|
sizeof(MemoryBasicInfo),
|
|
NULL);
|
|
|
|
/* There is no way this can fail, we committed this memory! */
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if ((PUCHAR)MemoryBasicInfo.AllocationBase <= Node->pVirtualBlock)
|
|
{
|
|
/* They are adjacent - merge! */
|
|
Node->nVirtualBlockSize += NextNode->nVirtualBlockSize;
|
|
|
|
/* Remove next entry from the list and put it into unused entries list */
|
|
RemoveEntryList(&NextNode->AvailableEntry);
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, NextNode);
|
|
DphRoot->nAvailableAllocations--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphCoalesceFreeIntoAvailable(PDPH_HEAP_ROOT DphRoot,
|
|
ULONG LeaveOnFreeList)
|
|
{
|
|
PDPH_HEAP_BLOCK Node = DphRoot->pFreeAllocationListHead, Next;
|
|
SIZE_T FreeAllocations = DphRoot->nFreeAllocations;
|
|
|
|
/* Make sure requested size is not too big */
|
|
ASSERT(FreeAllocations >= LeaveOnFreeList);
|
|
|
|
DPRINT("RtlpDphCoalesceFreeIntoAvailable(%p %lu)\n", DphRoot, LeaveOnFreeList);
|
|
|
|
while (Node)
|
|
{
|
|
FreeAllocations--;
|
|
if (FreeAllocations < LeaveOnFreeList) break;
|
|
|
|
/* Get the next pointer, because it may be changed after following two calls */
|
|
Next = Node->pNextAlloc;
|
|
|
|
/* Remove it from the free list */
|
|
RtlpDphRemoveFromFreeList(DphRoot, Node, NULL);
|
|
|
|
/* And put into the available */
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
|
|
|
|
/* Go to the next node */
|
|
Node = Next;
|
|
}
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphAddNewPool(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK NodeBlock, PVOID Virtual, SIZE_T Size, BOOLEAN PlaceOnPool)
|
|
{
|
|
PDPH_HEAP_BLOCK DphNode, DphStartNode;
|
|
ULONG NodeCount, i;
|
|
|
|
//NodeCount = (Size >> 6) - 1;
|
|
NodeCount = (ULONG)(Size / sizeof(DPH_HEAP_BLOCK));
|
|
DphStartNode = Virtual;
|
|
|
|
/* Set pNextAlloc for all blocks */
|
|
for (DphNode = Virtual, i=NodeCount-1; i > 0; i--)
|
|
{
|
|
DphNode->pNextAlloc = DphNode + 1;
|
|
DphNode = DphNode->pNextAlloc;
|
|
}
|
|
|
|
/* and the last one */
|
|
DphNode->pNextAlloc = NULL;
|
|
|
|
/* Add it to the tail of unused node list */
|
|
if (DphRoot->pUnusedNodeListTail)
|
|
DphRoot->pUnusedNodeListTail->pNextAlloc = DphStartNode;
|
|
else
|
|
DphRoot->pUnusedNodeListHead = DphStartNode;
|
|
|
|
DphRoot->pUnusedNodeListTail = DphNode;
|
|
|
|
/* Increase counters */
|
|
DphRoot->nUnusedNodes += NodeCount;
|
|
|
|
/* Check if we need to place it on the pool list */
|
|
if (PlaceOnPool)
|
|
{
|
|
/* Get a node from the unused list */
|
|
DphNode = RtlpDphTakeNodeFromUnusedList(DphRoot);
|
|
ASSERT(DphNode);
|
|
|
|
/* Set its virtual block values */
|
|
DphNode->pVirtualBlock = Virtual;
|
|
DphNode->nVirtualBlockSize = Size;
|
|
|
|
/* Place it on the pool list */
|
|
RtlpDphPlaceOnPoolList(DphRoot, DphNode);
|
|
}
|
|
}
|
|
|
|
PDPH_HEAP_BLOCK NTAPI
|
|
RtlpDphSearchAvailableMemoryListForBestFit(PDPH_HEAP_ROOT DphRoot,
|
|
SIZE_T Size)
|
|
{
|
|
PLIST_ENTRY CurEntry;
|
|
PDPH_HEAP_BLOCK Node, NodeFound = NULL;
|
|
|
|
CurEntry = DphRoot->AvailableAllocationHead.Flink;
|
|
|
|
while (CurEntry != &DphRoot->AvailableAllocationHead)
|
|
{
|
|
/* Get the current available node */
|
|
Node = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
|
|
|
|
/* Check its size */
|
|
if (Node->nVirtualBlockSize >= Size)
|
|
{
|
|
NodeFound = Node;
|
|
break;
|
|
}
|
|
|
|
/* Move to the next available entry */
|
|
CurEntry = CurEntry->Flink;
|
|
}
|
|
|
|
/* Make sure Adjacency list pointers are biased */
|
|
//ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Flink));
|
|
//ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Blink));
|
|
|
|
return NodeFound;
|
|
}
|
|
|
|
PDPH_HEAP_BLOCK NTAPI
|
|
RtlpDphFindAvailableMemory(PDPH_HEAP_ROOT DphRoot,
|
|
SIZE_T Size,
|
|
BOOLEAN Grow)
|
|
{
|
|
PDPH_HEAP_BLOCK Node;
|
|
ULONG NewSize;
|
|
|
|
/* Find an available best fitting node */
|
|
Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
|
|
|
|
/* If that didn't work, try to search a smaller one in the loop */
|
|
while (!Node)
|
|
{
|
|
/* Break if the free list becomes too small */
|
|
if (DphRoot->nFreeAllocations <= DPH_FREE_LIST_MINIMUM) break;
|
|
|
|
/* Calculate a new free list size */
|
|
NewSize = DphRoot->nFreeAllocations >> 2;
|
|
if (NewSize < DPH_FREE_LIST_MINIMUM) NewSize = DPH_FREE_LIST_MINIMUM;
|
|
|
|
/* Coalesce free into available */
|
|
RtlpDphCoalesceFreeIntoAvailable(DphRoot, NewSize);
|
|
|
|
/* Try to find an available best fitting node again */
|
|
Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
|
|
}
|
|
|
|
/* If Node is NULL, then we could fix the situation only by
|
|
growing the available VM size */
|
|
if (!Node && Grow)
|
|
{
|
|
/* Grow VM size, if it fails - return failure directly */
|
|
if (!RtlpDphGrowVirtual(DphRoot, Size)) return NULL;
|
|
|
|
/* Try to find an available best fitting node again */
|
|
Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
|
|
|
|
if (!Node)
|
|
{
|
|
/* Do the last attempt: coalesce all free into available (if Size fits there) */
|
|
if (DphRoot->nFreeAllocationBytesCommitted + DphRoot->nAvailableAllocationBytesCommitted >= Size)
|
|
{
|
|
/* Coalesce free into available */
|
|
RtlpDphCoalesceFreeIntoAvailable(DphRoot, 0);
|
|
|
|
/* Try to find an available best fitting node again */
|
|
Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return node we found */
|
|
return Node;
|
|
}
|
|
|
|
PDPH_HEAP_BLOCK NTAPI
|
|
RtlpDphFindBusyMemory(PDPH_HEAP_ROOT DphRoot,
|
|
PVOID pUserMem)
|
|
{
|
|
PDPH_HEAP_BLOCK Node;
|
|
PVOID Ptr;
|
|
|
|
/* Lookup busy block in AVL */
|
|
Ptr = RtlLookupElementGenericTableAvl(&DphRoot->BusyNodesTable, &pUserMem);
|
|
if (!Ptr) return NULL;
|
|
|
|
/* Restore pointer to the heap block */
|
|
Node = CONTAINING_RECORD(Ptr, DPH_HEAP_BLOCK, pUserAllocation);
|
|
ASSERT(Node->pUserAllocation == pUserMem);
|
|
return Node;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphSetProtectionBeforeUse(PDPH_HEAP_ROOT DphRoot, PUCHAR VirtualBlock, ULONG UserSize)
|
|
{
|
|
ULONG Protection;
|
|
PVOID Base;
|
|
|
|
if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
|
|
{
|
|
Base = VirtualBlock + PAGE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
Base = VirtualBlock;
|
|
}
|
|
|
|
// FIXME: It should be different, but for now it's fine
|
|
Protection = PAGE_READWRITE;
|
|
|
|
return RtlpDphProtectVm(Base, UserSize, Protection);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphSetProtectionAfterUse(PDPH_HEAP_ROOT DphRoot, /*PUCHAR VirtualBlock*/PDPH_HEAP_BLOCK Node)
|
|
{
|
|
ASSERT((Node->nVirtualAccessSize + PAGE_SIZE) <= Node->nVirtualBlockSize);
|
|
|
|
// FIXME: Bring stuff here
|
|
if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PDPH_HEAP_BLOCK NTAPI
|
|
RtlpDphAllocateNode(PDPH_HEAP_ROOT DphRoot)
|
|
{
|
|
PDPH_HEAP_BLOCK Node;
|
|
NTSTATUS Status;
|
|
SIZE_T Size = DPH_POOL_SIZE, SizeVirtual;
|
|
PVOID Ptr = NULL;
|
|
|
|
/* Check for the easy case */
|
|
if (DphRoot->pUnusedNodeListHead)
|
|
{
|
|
/* Just take a node from this list */
|
|
Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
|
|
ASSERT(Node);
|
|
return Node;
|
|
}
|
|
|
|
/* There is a need to make free space */
|
|
Node = RtlpDphFindAvailableMemory(DphRoot, DPH_POOL_SIZE, FALSE);
|
|
|
|
if (!DphRoot->pUnusedNodeListHead && !Node)
|
|
{
|
|
/* Retry with a smaller request */
|
|
Size = PAGE_SIZE;
|
|
Node = RtlpDphFindAvailableMemory(DphRoot, PAGE_SIZE, FALSE);
|
|
}
|
|
|
|
if (!DphRoot->pUnusedNodeListHead)
|
|
{
|
|
if (Node)
|
|
{
|
|
RtlpDphRemoveFromAvailableList(DphRoot, Node);
|
|
Ptr = Node->pVirtualBlock;
|
|
SizeVirtual = Node->nVirtualBlockSize;
|
|
}
|
|
else
|
|
{
|
|
/* No free space, need to alloc a new VM block */
|
|
Size = DPH_POOL_SIZE;
|
|
SizeVirtual = DPH_RESERVE_SIZE;
|
|
Status = RtlpDphAllocateVm(&Ptr, SizeVirtual, MEM_COMMIT, PAGE_NOACCESS);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Retry with a smaller size */
|
|
SizeVirtual = 0x10000;
|
|
Status = RtlpDphAllocateVm(&Ptr, SizeVirtual, MEM_COMMIT, PAGE_NOACCESS);
|
|
if (!NT_SUCCESS(Status)) return NULL;
|
|
}
|
|
}
|
|
|
|
/* VM is allocated at this point, set protection */
|
|
Status = RtlpDphProtectVm(Ptr, Size, PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Node)
|
|
{
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
|
|
}
|
|
else
|
|
{
|
|
//RtlpDphFreeVm();
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Zero the memory */
|
|
if (Node) RtlZeroMemory(Ptr, Size);
|
|
|
|
/* Add a new pool based on this VM */
|
|
RtlpDphAddNewPool(DphRoot, Node, Ptr, Size, TRUE);
|
|
|
|
if (Node)
|
|
{
|
|
if (Node->nVirtualBlockSize > Size)
|
|
{
|
|
Node->pVirtualBlock += Size;
|
|
Node->nVirtualBlockSize -= Size;
|
|
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
|
|
}
|
|
else
|
|
{
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, Node);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The new VM block was just allocated a few code lines ago,
|
|
so initialize it */
|
|
Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
|
|
Node->pVirtualBlock = Ptr;
|
|
Node->nVirtualBlockSize = SizeVirtual;
|
|
RtlpDphPlaceOnVirtualList(DphRoot, Node);
|
|
|
|
Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
|
|
Node->pVirtualBlock = (PUCHAR)Ptr + Size;
|
|
Node->nVirtualBlockSize = SizeVirtual - Size;
|
|
RtlpDphPlaceOnVirtualList(DphRoot, Node);
|
|
|
|
/* Coalesce them into available list */
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
|
|
}
|
|
}
|
|
|
|
return RtlpDphTakeNodeFromUnusedList(DphRoot);
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphGrowVirtual(PDPH_HEAP_ROOT DphRoot,
|
|
SIZE_T Size)
|
|
{
|
|
PDPH_HEAP_BLOCK Node, AvailableNode;
|
|
PVOID Base = NULL;
|
|
SIZE_T VirtualSize;
|
|
NTSTATUS Status;
|
|
|
|
/* Start with allocating a couple of nodes */
|
|
Node = RtlpDphAllocateNode(DphRoot);
|
|
if (!Node) return FALSE;
|
|
|
|
AvailableNode = RtlpDphAllocateNode(DphRoot);
|
|
if (!AvailableNode)
|
|
{
|
|
/* Free the allocated node and return failure */
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, Node);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Calculate size of VM to allocate by rounding it up */
|
|
Size = ROUND_UP(Size, 0xFFFF);
|
|
VirtualSize = Size;
|
|
if (Size < DPH_RESERVE_SIZE)
|
|
VirtualSize = DPH_RESERVE_SIZE;
|
|
|
|
/* Allocate the virtual memory */
|
|
// FIXME: Shouldn't it be MEM_RESERVE with later committing?
|
|
Status = RtlpDphAllocateVm(&Base, VirtualSize, MEM_COMMIT, PAGE_NOACCESS);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Retry again with a smaller size */
|
|
VirtualSize = Size;
|
|
Status = RtlpDphAllocateVm(&Base, VirtualSize, MEM_COMMIT, PAGE_NOACCESS);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Free the allocated node and return failure */
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, Node);
|
|
RtlpDphReturnNodeToUnusedList(DphRoot, AvailableNode);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Set up our two nodes describing this VM */
|
|
Node->pVirtualBlock = Base;
|
|
Node->nVirtualBlockSize = VirtualSize;
|
|
AvailableNode->pVirtualBlock = Base;
|
|
AvailableNode->nVirtualBlockSize = VirtualSize;
|
|
|
|
/* Add them to virtual and available lists respectively */
|
|
RtlpDphPlaceOnVirtualList(DphRoot, Node);
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, AvailableNode);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
NTAPI
|
|
RtlpDphCompareNodeForTable(IN PRTL_AVL_TABLE Table,
|
|
IN PVOID FirstStruct,
|
|
IN PVOID SecondStruct)
|
|
{
|
|
ULONG_PTR FirstBlock, SecondBlock;
|
|
|
|
FirstBlock = *((ULONG_PTR *)FirstStruct);
|
|
SecondBlock = *((ULONG_PTR *)SecondStruct);
|
|
|
|
if (FirstBlock < SecondBlock)
|
|
return GenericLessThan;
|
|
else if (FirstBlock > SecondBlock)
|
|
return GenericGreaterThan;
|
|
|
|
return GenericEqual;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
RtlpDphAllocateNodeForTable(IN PRTL_AVL_TABLE Table,
|
|
IN CLONG ByteSize)
|
|
{
|
|
PDPH_HEAP_BLOCK pBlock;
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
|
|
/* This mega-assert comes from a text search over Windows 2003 checked binary of ntdll.dll */
|
|
ASSERT((ULONG_PTR)(((PRTL_BALANCED_LINKS)0)+1) + sizeof(PUCHAR) == ByteSize);
|
|
|
|
/* Get pointer to the containing heap root record */
|
|
DphRoot = CONTAINING_RECORD(Table, DPH_HEAP_ROOT, BusyNodesTable);
|
|
pBlock = DphRoot->NodeToAllocate;
|
|
|
|
DphRoot->NodeToAllocate = NULL;
|
|
ASSERT(pBlock);
|
|
|
|
return &(pBlock->TableLinks);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
RtlpDphFreeNodeForTable(IN PRTL_AVL_TABLE Table,
|
|
IN PVOID Buffer)
|
|
{
|
|
/* Nothing */
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphInitializeDelayedFreeQueue(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlInitializeHeapLock(&RtlpDphDelayedFreeQueueLock);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
// TODO: Log this error!
|
|
DPRINT1("Failure initializing delayed free queue critical section\n");
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize lists */
|
|
InitializeListHead(&RtlpDphDelayedFreeQueue);
|
|
RtlInitializeSListHead(&RtlpDphDelayedTemporaryPushList);
|
|
|
|
/* Reset counters */
|
|
RtlpDphMemoryUsedByDelayedFreeBlocks = 0;
|
|
RtlpDphNumberOfDelayedFreeBlocks = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphFreeDelayedBlocksFromHeap(PDPH_HEAP_ROOT DphRoot,
|
|
PHEAP NormalHeap)
|
|
{
|
|
PLIST_ENTRY Current, Next;
|
|
PDPH_BLOCK_INFORMATION BlockInfo;
|
|
ULONG ValidationInfo;
|
|
|
|
/* The original routine seems to use a temporary SList to put blocks to be freed,
|
|
then it releases the lock and frees the blocks. But let's make it simple for now */
|
|
|
|
/* Acquire the delayed free queue lock */
|
|
RtlEnterHeapLock(RtlpDphDelayedFreeQueueLock, TRUE);
|
|
|
|
/* Traverse the list */
|
|
Current = RtlpDphDelayedFreeQueue.Flink;
|
|
while (Current != &RtlpDphDelayedFreeQueue)
|
|
{
|
|
/* Get the next entry pointer */
|
|
Next = Current->Flink;
|
|
|
|
BlockInfo = CONTAINING_RECORD(Current, DPH_BLOCK_INFORMATION, FreeQueue);
|
|
|
|
/* Check if it belongs to the same heap */
|
|
if (BlockInfo->Heap == DphRoot)
|
|
{
|
|
/* Remove it from the list */
|
|
RemoveEntryList(Current);
|
|
|
|
/* Reset its heap to NULL */
|
|
BlockInfo->Heap = NULL;
|
|
|
|
if (!RtlpDphIsNormalFreeHeapBlock(BlockInfo + 1, &ValidationInfo, TRUE))
|
|
{
|
|
RtlpDphReportCorruptedBlock(DphRoot, 10, BlockInfo + 1, ValidationInfo);
|
|
}
|
|
|
|
/* Decrement counters */
|
|
RtlpDphMemoryUsedByDelayedFreeBlocks -= BlockInfo->ActualSize;
|
|
RtlpDphNumberOfDelayedFreeBlocks--;
|
|
|
|
/* Free the normal heap */
|
|
RtlFreeHeap(NormalHeap, 0, BlockInfo);
|
|
}
|
|
|
|
/* Move to the next one */
|
|
Current = Next;
|
|
}
|
|
|
|
/* Release the delayed free queue lock */
|
|
RtlLeaveHeapLock(RtlpDphDelayedFreeQueueLock);
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphTargetDllsLogicInitialize(VOID)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphInternalValidatePageHeap(PDPH_HEAP_ROOT DphRoot, PVOID Address, ULONG Value)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphVerifyIntegrity(PDPH_HEAP_ROOT DphRoot)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
VOID NTAPI
|
|
RtlpDphReportCorruptedBlock(
|
|
_In_ PDPH_HEAP_ROOT DphRoot,
|
|
_In_ ULONG Reserved,
|
|
_In_ PVOID Block,
|
|
_In_ ULONG ValidationInfo)
|
|
{
|
|
PVOID Size = (PVOID)(ULONG_PTR)RtlpDphGetBlockSizeFromCorruptedBlock(Block);
|
|
DPH_BLOCK_INFORMATION SafeInfo = {0};
|
|
|
|
DPRINT1("Corrupted heap block %p\n", Block);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
PDPH_BLOCK_INFORMATION BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
|
|
RtlCopyMemory(&SafeInfo, BlockInfo, sizeof(SafeInfo));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPRINT1("ERROR: Could not read DPH_BLOCK_INFORMATION\n");
|
|
RtlZeroMemory(&SafeInfo, sizeof(SafeInfo));
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (ValidationInfo & DPH_VALINFO_CORRUPTED_AFTER_FREE)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_AFTER_FREE, "block corrupted after having been freed",
|
|
RtlpDphHeapFromPointer(DphRoot), "Heap handle", Block, "Heap block", (PVOID)Size, "Block size", 0, "");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_ALREADY_FREED)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_DOUBLE_FREE, "block already freed", RtlpDphHeapFromPointer(DphRoot), "Heap handle",
|
|
Block, "Heap block", Size, "Block size", 0, "");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_INFIX_PATTERN)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_INFIX_PATTERN, "corrupted infix pattern for freed block",
|
|
RtlpDphHeapFromPointer(DphRoot), "Heap handle", Block, "Heap block", Size, "Block size", 0, "");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_POINTER)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPT_HEAP_POINTER, "corrupted heap pointer or using wrong heap",
|
|
RtlpDphHeapFromPointer(DphRoot), "Heap handle used", Block, "Heap block", Size, "Block size",
|
|
SafeInfo.Heap, "Actual heap handle");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_SUFFIX_PATTERN)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_SUFFIX_PATTERN, "corrupted suffix pattern", RtlpDphHeapFromPointer(DphRoot),
|
|
"Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_PREFIX_PATTERN)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_PREFIX_PATTERN, "corrupted prefix pattern", RtlpDphHeapFromPointer(DphRoot),
|
|
"Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_START_STAMP)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_START_STAMP, "corrupted start stamp", RtlpDphHeapFromPointer(DphRoot),
|
|
"Heap handle used", Block, "Heap block", Size, "Block size", (PVOID)(ULONG_PTR)SafeInfo.StartStamp,
|
|
"Corrupted start stamp");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_BAD_END_STAMP)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_CORRUPTED_END_STAMP, "corrupted end stamp", RtlpDphHeapFromPointer(DphRoot),
|
|
"Heap handle used", Block, "Heap block", Size, "Block size", (PVOID)(ULONG_PTR)SafeInfo.EndStamp,
|
|
"Corrupted end stamp");
|
|
}
|
|
|
|
if (ValidationInfo & DPH_VALINFO_EXCEPTION)
|
|
{
|
|
RtlApplicationVerifierStop(
|
|
APPLICATION_VERIFIER_EXCEPTION_WHILE_VERIFYING_BLOCK_HEADER, "exception raised while verifying block",
|
|
RtlpDphHeapFromPointer(DphRoot), "Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
|
|
}
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphIsPageHeapBlock(PDPH_HEAP_ROOT DphRoot,
|
|
PVOID Block,
|
|
PULONG ValidationInformation,
|
|
BOOLEAN CheckFillers)
|
|
{
|
|
PDPH_BLOCK_INFORMATION BlockInfo;
|
|
BOOLEAN SomethingWrong = FALSE;
|
|
PUCHAR Byte, Start, End;
|
|
|
|
ASSERT(ValidationInformation != NULL);
|
|
*ValidationInformation = 0;
|
|
|
|
// _SEH2_TRY {
|
|
BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
|
|
|
|
/* Check stamps */
|
|
if (BlockInfo->StartStamp != DPH_FILL_START_STAMP_1)
|
|
{
|
|
*ValidationInformation |= DPH_VALINFO_BAD_START_STAMP;
|
|
SomethingWrong = TRUE;
|
|
|
|
/* Check if it has an alloc/free mismatch */
|
|
if (BlockInfo->StartStamp == DPH_FILL_START_STAMP_2)
|
|
{
|
|
/* Notify respectively */
|
|
*ValidationInformation = 0x101;
|
|
}
|
|
}
|
|
|
|
if (BlockInfo->EndStamp != DPH_FILL_END_STAMP_1)
|
|
{
|
|
*ValidationInformation |= DPH_VALINFO_BAD_END_STAMP;
|
|
SomethingWrong = TRUE;
|
|
}
|
|
|
|
/* Check root heap pointer */
|
|
if (BlockInfo->Heap != DphRoot)
|
|
{
|
|
*ValidationInformation |= DPH_VALINFO_BAD_POINTER;
|
|
SomethingWrong = TRUE;
|
|
}
|
|
|
|
/* Check other fillers if requested */
|
|
if (CheckFillers)
|
|
{
|
|
/* Check space after the block */
|
|
Start = (PUCHAR)Block + BlockInfo->RequestedSize;
|
|
End = (PUCHAR)ROUND_UP(Start, PAGE_SIZE);
|
|
for (Byte = Start; Byte < End; Byte++)
|
|
{
|
|
if (*Byte != DPH_FILL_SUFFIX)
|
|
{
|
|
*ValidationInformation |= DPH_VALINFO_BAD_SUFFIX_PATTERN;
|
|
SomethingWrong = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (SomethingWrong == FALSE);
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphIsNormalFreeHeapBlock(PVOID Block,
|
|
PULONG ValidationInformation,
|
|
BOOLEAN CheckFillers)
|
|
{
|
|
ASSERT(ValidationInformation != NULL);
|
|
|
|
UNIMPLEMENTED;
|
|
*ValidationInformation = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS NTAPI
|
|
RtlpDphProcessStartupInitialization(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
PTEB Teb = NtCurrentTeb();
|
|
|
|
/* Initialize the DPH heap list and its critical section */
|
|
InitializeListHead(&RtlpDphPageHeapList);
|
|
Status = RtlInitializeHeapLock(&RtlpDphPageHeapListLock);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize delayed-free queue */
|
|
Status = RtlpDphInitializeDelayedFreeQueue();
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Initialize the target dlls string */
|
|
RtlInitUnicodeString(&RtlpDphTargetDllsUnicode, RtlpDphTargetDlls);
|
|
Status = RtlpDphTargetDllsLogicInitialize();
|
|
|
|
/* Per-process DPH init is done */
|
|
RtlpDphPageHeapListInitialized = TRUE;
|
|
|
|
DPRINT1("Page heap: pid 0x%p: page heap enabled with flags 0x%X.\n",
|
|
Teb->ClientId.UniqueProcess, RtlpDphGlobalFlags);
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpDphShouldAllocateInPageHeap(PDPH_HEAP_ROOT DphRoot,
|
|
SIZE_T Size)
|
|
{
|
|
//UNIMPLEMENTED;
|
|
/* Always use page heap for now */
|
|
return TRUE;
|
|
}
|
|
|
|
HANDLE NTAPI
|
|
RtlpPageHeapCreate(ULONG Flags,
|
|
PVOID Addr,
|
|
SIZE_T TotalSize,
|
|
SIZE_T CommitSize,
|
|
PVOID Lock,
|
|
PRTL_HEAP_PARAMETERS Parameters)
|
|
{
|
|
PVOID Base = NULL;
|
|
PHEAP HeapPtr;
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK DphNode;
|
|
ULONG MemSize;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER PerfCounter;
|
|
|
|
/* Check for a DPH bypass flag */
|
|
if ((ULONG_PTR)Parameters == -1) return NULL;
|
|
|
|
/* Make sure no user-allocated stuff was provided */
|
|
if (Addr || Lock) return NULL;
|
|
|
|
/* Allocate minimum amount of virtual memory */
|
|
MemSize = DPH_RESERVE_SIZE;
|
|
Status = RtlpDphAllocateVm(&Base, MemSize, MEM_COMMIT, PAGE_NOACCESS);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Set protection */
|
|
Status = RtlpDphProtectVm(Base, 2*PAGE_SIZE + DPH_POOL_SIZE, PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//RtlpDphFreeVm(Base, 0, 0, 0);
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Start preparing the 1st page. Fill it with the default filler */
|
|
RtlFillMemoryUlong(Base, PAGE_SIZE, DPH_FILL);
|
|
|
|
/* Set flags in the "HEAP" structure */
|
|
HeapPtr = (PHEAP)Base;
|
|
HeapPtr->Flags = Flags | HEAP_FLAG_PAGE_ALLOCS;
|
|
HeapPtr->ForceFlags = Flags | HEAP_FLAG_PAGE_ALLOCS;
|
|
|
|
/* Set 1st page to read only now */
|
|
Status = RtlpDphProtectVm(Base, PAGE_SIZE, PAGE_READONLY);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* 2nd page is the real DPH root block */
|
|
DphRoot = (PDPH_HEAP_ROOT)((PCHAR)Base + PAGE_SIZE);
|
|
|
|
/* Initialize the DPH root */
|
|
DphRoot->Signature = DPH_SIGNATURE;
|
|
DphRoot->HeapFlags = Flags;
|
|
DphRoot->HeapCritSect = (PHEAP_LOCK)((PCHAR)DphRoot + DPH_POOL_SIZE);
|
|
DphRoot->ExtraFlags = RtlpDphGlobalFlags;
|
|
|
|
ZwQueryPerformanceCounter(&PerfCounter, NULL);
|
|
DphRoot->Seed = PerfCounter.LowPart;
|
|
|
|
RtlInitializeHeapLock(&DphRoot->HeapCritSect);
|
|
InitializeListHead(&DphRoot->AvailableAllocationHead);
|
|
|
|
/* Create a normal heap for this paged heap */
|
|
DphRoot->NormalHeap = RtlCreateHeap(Flags, NULL, TotalSize, CommitSize, NULL, (PRTL_HEAP_PARAMETERS)-1);
|
|
if (!DphRoot->NormalHeap)
|
|
{
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* 3rd page: a pool for DPH allocations */
|
|
RtlpDphAddNewPool(DphRoot, NULL, DphRoot + 1, DPH_POOL_SIZE - sizeof(DPH_HEAP_ROOT), FALSE);
|
|
|
|
/* Allocate internal heap blocks. For the root */
|
|
DphNode = RtlpDphAllocateNode(DphRoot);
|
|
ASSERT(DphNode != NULL);
|
|
DphNode->pVirtualBlock = (PUCHAR)DphRoot;
|
|
DphNode->nVirtualBlockSize = DPH_POOL_SIZE;
|
|
RtlpDphPlaceOnPoolList(DphRoot, DphNode);
|
|
|
|
/* For the memory we allocated as a whole */
|
|
DphNode = RtlpDphAllocateNode(DphRoot);
|
|
ASSERT(DphNode != NULL);
|
|
DphNode->pVirtualBlock = Base;
|
|
DphNode->nVirtualBlockSize = MemSize;
|
|
RtlpDphPlaceOnVirtualList(DphRoot, DphNode);
|
|
|
|
/* For the remaining part */
|
|
DphNode = RtlpDphAllocateNode(DphRoot);
|
|
ASSERT(DphNode != NULL);
|
|
DphNode->pVirtualBlock = (PUCHAR)Base + 2*PAGE_SIZE + DPH_POOL_SIZE;
|
|
DphNode->nVirtualBlockSize = MemSize - (2*PAGE_SIZE + DPH_POOL_SIZE);
|
|
RtlpDphCoalesceNodeIntoAvailable(DphRoot, DphNode);
|
|
|
|
//DphRoot->CreateStackTrace = RtlpDphLogStackTrace(1);
|
|
|
|
/* Initialize AVL-based busy nodes table */
|
|
RtlInitializeGenericTableAvl(&DphRoot->BusyNodesTable,
|
|
RtlpDphCompareNodeForTable,
|
|
RtlpDphAllocateNodeForTable,
|
|
RtlpDphFreeNodeForTable,
|
|
NULL);
|
|
|
|
/* Initialize per-process startup info */
|
|
if (!RtlpDphPageHeapListInitialized) RtlpDphProcessStartupInitialization();
|
|
|
|
/* Acquire the heap list lock */
|
|
RtlEnterHeapLock(RtlpDphPageHeapListLock, TRUE);
|
|
|
|
/* Insert this heap to the tail of the global list */
|
|
InsertTailList(&RtlpDphPageHeapList, &DphRoot->NextHeap);
|
|
|
|
/* Note we increased the size of the list */
|
|
RtlpDphPageHeapListLength++;
|
|
|
|
/* Release the heap list lock */
|
|
RtlLeaveHeapLock(RtlpDphPageHeapListLock);
|
|
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_VERBOSE)
|
|
{
|
|
DPRINT1("Page heap: process 0x%p created heap @ %p (%p, flags 0x%X)\n",
|
|
NtCurrentTeb()->ClientId.UniqueProcess, (PUCHAR)DphRoot - PAGE_SIZE,
|
|
DphRoot->NormalHeap, DphRoot->ExtraFlags);
|
|
}
|
|
|
|
/* Perform internal validation if required */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
|
|
return (PUCHAR)DphRoot - PAGE_SIZE;
|
|
}
|
|
|
|
PVOID NTAPI
|
|
RtlpPageHeapDestroy(HANDLE HeapPtr)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PVOID Ptr;
|
|
PDPH_HEAP_BLOCK Node, Next;
|
|
PHEAP NormalHeap;
|
|
ULONG Value;
|
|
|
|
/* Check if it's not a process heap */
|
|
if (HeapPtr == RtlGetProcessHeap())
|
|
{
|
|
DbgBreakPoint();
|
|
return NULL;
|
|
}
|
|
|
|
/* Get pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return NULL;
|
|
|
|
RtlpDphPreProcessing(DphRoot, DphRoot->HeapFlags);
|
|
|
|
/* Get the pointer to the normal heap */
|
|
NormalHeap = DphRoot->NormalHeap;
|
|
|
|
/* Free the delayed-free blocks */
|
|
RtlpDphFreeDelayedBlocksFromHeap(DphRoot, NormalHeap);
|
|
|
|
/* Go through the busy blocks */
|
|
Ptr = RtlEnumerateGenericTableAvl(&DphRoot->BusyNodesTable, TRUE);
|
|
|
|
while (Ptr)
|
|
{
|
|
Node = CONTAINING_RECORD(Ptr, DPH_HEAP_BLOCK, pUserAllocation);
|
|
if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
|
|
{
|
|
if (!RtlpDphIsPageHeapBlock(DphRoot, Node->pUserAllocation, &Value, TRUE))
|
|
{
|
|
RtlpDphReportCorruptedBlock(DphRoot, 3, Node->pUserAllocation, Value);
|
|
}
|
|
}
|
|
|
|
/* FIXME: Call AV notification */
|
|
//AVrfInternalHeapFreeNotification();
|
|
|
|
/* Go to the next node */
|
|
Ptr = RtlEnumerateGenericTableAvl(&DphRoot->BusyNodesTable, FALSE);
|
|
}
|
|
|
|
/* Acquire the global heap list lock */
|
|
RtlEnterHeapLock(RtlpDphPageHeapListLock, TRUE);
|
|
|
|
/* Remove the entry and decrement the global counter */
|
|
RemoveEntryList(&DphRoot->NextHeap);
|
|
RtlpDphPageHeapListLength--;
|
|
|
|
/* Release the global heap list lock */
|
|
RtlLeaveHeapLock(RtlpDphPageHeapListLock);
|
|
|
|
/* Leave and delete this heap's critical section */
|
|
RtlLeaveHeapLock(DphRoot->HeapCritSect);
|
|
RtlDeleteHeapLock(DphRoot->HeapCritSect);
|
|
|
|
/* Now go through all virtual list nodes and release the VM */
|
|
Node = DphRoot->pVirtualStorageListHead;
|
|
while (Node)
|
|
{
|
|
Next = Node->pNextAlloc;
|
|
/* Release the memory without checking result */
|
|
RtlpDphFreeVm(Node->pVirtualBlock, 0, MEM_RELEASE);
|
|
Node = Next;
|
|
}
|
|
|
|
/* Destroy the normal heap */
|
|
RtlDestroyHeap(NormalHeap);
|
|
|
|
/* Report success */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_VERBOSE)
|
|
DPRINT1("Page heap: process 0x%p destroyed heap @ %p (%p)\n",
|
|
NtCurrentTeb()->ClientId.UniqueProcess, HeapPtr, NormalHeap);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PVOID NTAPI
|
|
RtlpPageHeapAllocate(IN PVOID HeapPtr,
|
|
IN ULONG Flags,
|
|
IN SIZE_T Size)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK AvailableNode, BusyNode;
|
|
BOOLEAN Biased = FALSE;
|
|
ULONG AllocateSize, AccessSize;
|
|
NTSTATUS Status;
|
|
SIZE_T UserActualSize;
|
|
PVOID Ptr;
|
|
|
|
/* Check requested size */
|
|
if (Size > 0x7FF00000)
|
|
{
|
|
DPRINT1("extreme size request\n");
|
|
|
|
/* Generate an exception if needed */
|
|
if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Unbias the pointer if necessary */
|
|
if (IS_BIASED_POINTER(HeapPtr))
|
|
{
|
|
HeapPtr = (PVOID)POINTER_REMOVE_BIAS(HeapPtr);
|
|
Biased = TRUE;
|
|
}
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return NULL;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Perform internal validation if specified by flags */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
|
|
{
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
}
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
if (!Biased && !RtlpDphShouldAllocateInPageHeap(DphRoot, Size))
|
|
{
|
|
/* Perform allocation from a normal heap */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Perform heap integrity check if specified by flags */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
{
|
|
RtlpDphVerifyIntegrity(DphRoot);
|
|
}
|
|
|
|
/* Calculate sizes */
|
|
AccessSize = ROUND_UP(Size + sizeof(DPH_BLOCK_INFORMATION), PAGE_SIZE);
|
|
AllocateSize = AccessSize + PAGE_SIZE;
|
|
|
|
// FIXME: Move RtlpDphAllocateNode(DphRoot) to this place
|
|
AvailableNode = RtlpDphFindAvailableMemory(DphRoot, AllocateSize, TRUE);
|
|
if (!AvailableNode)
|
|
{
|
|
DPRINT1("Page heap: Unable to allocate virtual memory\n");
|
|
DbgBreakPoint();
|
|
|
|
/* Release the lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
return NULL;
|
|
}
|
|
ASSERT(AvailableNode->nVirtualBlockSize >= AllocateSize);
|
|
|
|
/* Set protection */
|
|
Status = RtlpDphSetProtectionBeforeUse(DphRoot,
|
|
AvailableNode->pVirtualBlock,
|
|
AccessSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Save available node pointer */
|
|
Ptr = AvailableNode->pVirtualBlock;
|
|
|
|
/* Check node's size */
|
|
if (AvailableNode->nVirtualBlockSize > AllocateSize)
|
|
{
|
|
/* The block contains too much free space, reduce it */
|
|
AvailableNode->pVirtualBlock += AllocateSize;
|
|
AvailableNode->nVirtualBlockSize -= AllocateSize;
|
|
DphRoot->nAvailableAllocationBytesCommitted -= AllocateSize;
|
|
|
|
/* Allocate a new node which will be our busy node */
|
|
BusyNode = RtlpDphAllocateNode(DphRoot);
|
|
ASSERT(BusyNode != NULL);
|
|
BusyNode->pVirtualBlock = Ptr;
|
|
BusyNode->nVirtualBlockSize = AllocateSize;
|
|
}
|
|
else
|
|
{
|
|
/* The block's size fits exactly */
|
|
RtlpDphRemoveFromAvailableList(DphRoot, AvailableNode);
|
|
BusyNode = AvailableNode;
|
|
}
|
|
|
|
/* Calculate actual user size */
|
|
if (DphRoot->HeapFlags & HEAP_NO_ALIGNMENT)
|
|
UserActualSize = Size;
|
|
else
|
|
UserActualSize = ROUND_UP(Size, 8);
|
|
|
|
/* Set up the block */
|
|
BusyNode->nVirtualAccessSize = AccessSize;
|
|
BusyNode->nUserActualSize = UserActualSize;
|
|
BusyNode->nUserRequestedSize = Size;
|
|
|
|
if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
|
|
BusyNode->pUserAllocation = BusyNode->pVirtualBlock + PAGE_SIZE;
|
|
else
|
|
BusyNode->pUserAllocation = BusyNode->pVirtualBlock + BusyNode->nVirtualAccessSize - UserActualSize;
|
|
|
|
BusyNode->UserValue = NULL;
|
|
BusyNode->UserFlags = Flags & HEAP_SETTABLE_USER_FLAGS;
|
|
|
|
// FIXME: Don't forget about stack traces if such flag was set
|
|
BusyNode->StackTrace = NULL;
|
|
|
|
/* Place it on busy list */
|
|
RtlpDphPlaceOnBusyList(DphRoot, BusyNode);
|
|
|
|
/* Zero or patter-fill memory depending on flags */
|
|
if (Flags & HEAP_ZERO_MEMORY)
|
|
RtlZeroMemory(BusyNode->pUserAllocation, Size);
|
|
else
|
|
RtlFillMemory(BusyNode->pUserAllocation, Size, DPH_FILL_INFIX);
|
|
|
|
/* Write DPH info */
|
|
if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
|
|
{
|
|
RtlpDphWritePageHeapBlockInformation(DphRoot,
|
|
BusyNode->pUserAllocation,
|
|
Size,
|
|
AccessSize);
|
|
}
|
|
|
|
/* Finally allocation is done, perform validation again if required */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
|
|
{
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
}
|
|
|
|
/* Release the lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
DPRINT("Allocated user block pointer: %p\n", BusyNode->pUserAllocation);
|
|
|
|
/* Return pointer to user allocation */
|
|
return BusyNode->pUserAllocation;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpPageHeapFree(HANDLE HeapPtr,
|
|
ULONG Flags,
|
|
PVOID Ptr)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node;
|
|
ULONG ValidationInfo;
|
|
PDPH_BLOCK_INFORMATION Info;
|
|
|
|
/* Check for a NULL pointer freeing */
|
|
if (!Ptr)
|
|
{
|
|
if (RtlpDphBreakOptions & DPH_BREAK_ON_NULL_FREE)
|
|
{
|
|
DPRINT1("Page heap: freeing a null pointer\n");
|
|
DbgBreakPoint();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Perform internal validation if specified by flags */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Find busy memory */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, Ptr);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapFree();
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
|
|
{
|
|
/* Check and report corrupted block */
|
|
if (!RtlpDphIsPageHeapBlock(DphRoot, Ptr, &ValidationInfo, TRUE))
|
|
{
|
|
RtlpDphReportCorruptedBlock(DphRoot, 1, Ptr, ValidationInfo);
|
|
}
|
|
|
|
// FIXME: Should go inside RtlpDphSetProtectionAfterUse
|
|
if (Node->nVirtualAccessSize != 0)
|
|
{
|
|
/* Set stamps */
|
|
Info = (PDPH_BLOCK_INFORMATION)Node->pUserAllocation - 1;
|
|
Info->StartStamp = DPH_FILL_START_STAMP_2;
|
|
Info->EndStamp = DPH_FILL_END_STAMP_2;
|
|
|
|
RtlpDphProtectVm(Node->pVirtualBlock, Node->nVirtualAccessSize, PAGE_NOACCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Should go inside RtlpDphSetProtectionAfterUse
|
|
if (Node->nVirtualAccessSize != 0)
|
|
RtlpDphProtectVm(Node->pVirtualBlock + PAGE_SIZE, Node->nVirtualAccessSize, PAGE_NOACCESS);
|
|
}
|
|
|
|
/* Set new protection */
|
|
//RtlpDphSetProtectionAfterUse(DphRoot, Node);
|
|
|
|
/* Remove it from the list of busy nodes */
|
|
RtlpDphRemoveFromBusyList(DphRoot, Node);
|
|
|
|
/* And put it into the list of free nodes */
|
|
RtlpDphPlaceOnFreeList(DphRoot, Node);
|
|
|
|
//if (DphRoot->ExtraFlags & DPH_EXTRA_LOG_STACK_TRACES)
|
|
// Node->StackTrace = RtlpDphLogStackTrace(3);
|
|
//else
|
|
Node->StackTrace = NULL;
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
PVOID NTAPI
|
|
RtlpPageHeapReAllocate(HANDLE HeapPtr,
|
|
ULONG Flags,
|
|
PVOID Ptr,
|
|
SIZE_T Size)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node = NULL, AllocatedNode;
|
|
BOOLEAN Biased = FALSE, UseNormalHeap = FALSE, OldBlockPageHeap = TRUE;
|
|
ULONG ValidationInfo;
|
|
SIZE_T DataSize;
|
|
PVOID NewAlloc = NULL;
|
|
|
|
/* Check requested size */
|
|
if (Size > 0x7FF00000)
|
|
{
|
|
DPRINT1("extreme size request\n");
|
|
|
|
/* Generate an exception if needed */
|
|
if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Unbias the pointer if necessary */
|
|
if (IS_BIASED_POINTER(HeapPtr))
|
|
{
|
|
HeapPtr = (PVOID)POINTER_REMOVE_BIAS(HeapPtr);
|
|
Biased = TRUE;
|
|
}
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return NULL;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Perform internal validation if specified by flags */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
{
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
}
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Exit with NULL right away if inplace is specified */
|
|
if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
|
|
{
|
|
/* Release the lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Generate an exception if needed */
|
|
if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Try to get node of the allocated block */
|
|
AllocatedNode = RtlpDphFindBusyMemory(DphRoot, Ptr);
|
|
|
|
if (!AllocatedNode)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapFree();
|
|
ASSERT(FALSE);
|
|
OldBlockPageHeap = FALSE;
|
|
}
|
|
|
|
/* Check the block */
|
|
if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
|
|
{
|
|
if (!RtlpDphIsPageHeapBlock(DphRoot, AllocatedNode->pUserAllocation, &ValidationInfo, TRUE))
|
|
{
|
|
RtlpDphReportCorruptedBlock(DphRoot, 3, AllocatedNode->pUserAllocation, ValidationInfo);
|
|
}
|
|
}
|
|
|
|
/* Remove old one from the busy list */
|
|
RtlpDphRemoveFromBusyList(DphRoot, AllocatedNode);
|
|
|
|
if (!Biased && !RtlpDphShouldAllocateInPageHeap(DphRoot, Size))
|
|
{
|
|
// FIXME: Use normal heap
|
|
ASSERT(FALSE);
|
|
UseNormalHeap = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Now do a trick: bias the pointer and call our allocate routine */
|
|
NewAlloc = RtlpPageHeapAllocate((PVOID)POINTER_ADD_BIAS(HeapPtr), Flags, Size);
|
|
}
|
|
|
|
if (!NewAlloc)
|
|
{
|
|
/* New allocation failed, put the block back (if it was found in page heap) */
|
|
RtlpDphPlaceOnBusyList(DphRoot, AllocatedNode);
|
|
|
|
/* Release the lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Perform validation again if required */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
|
|
{
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
}
|
|
|
|
/* Generate an exception if needed */
|
|
if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy contents of the old block */
|
|
if (AllocatedNode->nUserRequestedSize > Size)
|
|
DataSize = Size;
|
|
else
|
|
DataSize = AllocatedNode->nUserRequestedSize;
|
|
|
|
if (DataSize != 0) RtlCopyMemory(NewAlloc, Ptr, DataSize);
|
|
|
|
/* Copy user flags and values */
|
|
if (!UseNormalHeap)
|
|
{
|
|
/* Get the node of the new block */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, NewAlloc);
|
|
ASSERT(Node != NULL);
|
|
|
|
/* Set its values/flags */
|
|
Node->UserValue = AllocatedNode->UserValue;
|
|
if (Flags & HEAP_SETTABLE_USER_FLAGS)
|
|
Node->UserFlags = Flags & HEAP_SETTABLE_USER_FLAGS;
|
|
else
|
|
Node->UserFlags = AllocatedNode->UserFlags;
|
|
}
|
|
|
|
if (!OldBlockPageHeap)
|
|
{
|
|
/* Weird scenario, investigate */
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
/* Mark the old block as no access */
|
|
if (AllocatedNode->nVirtualAccessSize != 0)
|
|
{
|
|
RtlpDphProtectVm(AllocatedNode->pVirtualBlock, AllocatedNode->nVirtualAccessSize, PAGE_NOACCESS);
|
|
}
|
|
|
|
/* And place it on the free list */
|
|
RtlpDphPlaceOnFreeList(DphRoot, AllocatedNode);
|
|
|
|
// FIXME: Capture stack traces if needed
|
|
AllocatedNode->StackTrace = NULL;
|
|
|
|
/* Finally allocation is done, perform validation again if required */
|
|
if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
|
|
{
|
|
RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
|
|
}
|
|
|
|
/* Release the lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
DPRINT("Allocated new user block pointer: %p\n", NewAlloc);
|
|
|
|
/* Return pointer to user allocation */
|
|
return NewAlloc;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpPageHeapGetUserInfo(PVOID HeapHandle,
|
|
ULONG Flags,
|
|
PVOID BaseAddress,
|
|
PVOID *UserValue,
|
|
PULONG UserFlags)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node;
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapHandle);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Find busy memory */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapGetUserInfo();
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get user values and flags and store them in user provided pointers */
|
|
if (UserValue) *UserValue = Node->UserValue;
|
|
if (UserFlags) *UserFlags = Node->UserFlags;
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN NTAPI
|
|
RtlpPageHeapSetUserValue(PVOID HeapHandle,
|
|
ULONG Flags,
|
|
PVOID BaseAddress,
|
|
PVOID UserValue)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node;
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapHandle);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Find busy memory */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapSetUserValue();
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get user values and flags and store them in user provided pointers */
|
|
Node->UserValue = UserValue;
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlpPageHeapSetUserFlags(PVOID HeapHandle,
|
|
ULONG Flags,
|
|
PVOID BaseAddress,
|
|
ULONG UserFlagsReset,
|
|
ULONG UserFlagsSet)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node;
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapHandle);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Find busy memory */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapSetUserFlags();
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get user values and flags and store them in user provided pointers */
|
|
Node->UserFlags &= ~(UserFlagsReset);
|
|
Node->UserFlags |= UserFlagsSet;
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return success */
|
|
return TRUE;
|
|
}
|
|
|
|
SIZE_T NTAPI
|
|
RtlpPageHeapSize(HANDLE HeapHandle,
|
|
ULONG Flags,
|
|
PVOID BaseAddress)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node;
|
|
SIZE_T Size;
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapHandle);
|
|
if (!DphRoot) return -1;
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Find busy memory */
|
|
Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, try a normal heap instead */
|
|
//RtlpDphNormalHeapSize();
|
|
ASSERT(FALSE);
|
|
return -1;
|
|
}
|
|
|
|
/* Get heap block size */
|
|
Size = Node->nUserRequestedSize;
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return user requested size */
|
|
return Size;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlpDebugPageHeapValidate(PVOID HeapHandle,
|
|
ULONG Flags,
|
|
PVOID BaseAddress)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
PDPH_HEAP_BLOCK Node = NULL;
|
|
BOOLEAN Valid = FALSE;
|
|
|
|
/* Get a pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapHandle);
|
|
if (!DphRoot) return -1;
|
|
|
|
/* Add heap flags */
|
|
Flags |= DphRoot->HeapFlags;
|
|
|
|
/* Acquire the heap lock */
|
|
RtlpDphPreProcessing(DphRoot, Flags);
|
|
|
|
/* Find busy memory */
|
|
if (BaseAddress)
|
|
Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
|
|
|
|
if (!Node)
|
|
{
|
|
/* This block was not found in page heap, or the request is to validate all normal heap */
|
|
Valid = RtlpDphNormalHeapValidate(DphRoot, Flags, BaseAddress);
|
|
}
|
|
|
|
/* Leave the heap lock */
|
|
RtlpDphPostProcessing(DphRoot);
|
|
|
|
/* Return result of a normal heap validation */
|
|
if (BaseAddress && !Node)
|
|
return Valid;
|
|
|
|
/* Otherwise return our own result */
|
|
if (!BaseAddress || Node) Valid = TRUE;
|
|
|
|
return Valid;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlpDphNormalHeapValidate(PDPH_HEAP_ROOT DphRoot,
|
|
ULONG Flags,
|
|
PVOID BaseAddress)
|
|
{
|
|
PDPH_BLOCK_INFORMATION BlockInfo = (PDPH_BLOCK_INFORMATION)BaseAddress - 1;
|
|
if (!BaseAddress)
|
|
{
|
|
/* Validate all normal heap */
|
|
return RtlValidateHeap(DphRoot->NormalHeap, Flags, NULL);
|
|
}
|
|
|
|
// FIXME: Check is this a normal heap block
|
|
/*if (!RtlpDphIsNormalHeapBlock(DphRoot, BaseAddress, &ValidationInfo))
|
|
{
|
|
}*/
|
|
|
|
return RtlValidateHeap(DphRoot->NormalHeap, Flags, BlockInfo);
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlpPageHeapLock(HANDLE HeapPtr)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
|
|
/* Get pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
RtlpDphEnterCriticalSection(DphRoot, DphRoot->HeapFlags);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlpPageHeapUnlock(HANDLE HeapPtr)
|
|
{
|
|
PDPH_HEAP_ROOT DphRoot;
|
|
|
|
/* Get pointer to the heap root */
|
|
DphRoot = RtlpDphPointerFromHandle(HeapPtr);
|
|
if (!DphRoot) return FALSE;
|
|
|
|
RtlpDphLeaveCriticalSection(DphRoot);
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|