[RTL/DPH]

- Add core DPH (Debug Page Heap) structures based on Windows 2003/Vista.
- Add misc generic support routines.
- Implement DPH version of RtlCreateHeap().
- Debug prints match those printed by Windows 2003.

svn path=/trunk/; revision=50667
This commit is contained in:
Aleksey Bragin 2011-02-12 16:38:00 +00:00
parent 6dddb1823b
commit 04bef099e6
2 changed files with 491 additions and 9 deletions

View file

@ -74,9 +74,9 @@ LoadImageFileExecutionOptions(PPEB Peb)
UNICODE_STRING ImageName;
UNICODE_STRING ImagePathName;
ULONG ValueSize;
extern ULONG RtlpPageHeapGlobalFlags, RtlpPageHeapSizeRangeStart, RtlpPageHeapSizeRangeEnd;
extern ULONG RtlpDphGlobalFlags, RtlpPageHeapSizeRangeStart, RtlpPageHeapSizeRangeEnd;
extern ULONG RtlpPageHeapDllRangeStart, RtlpPageHeapDllRangeEnd;
extern WCHAR RtlpPageHeapTargetDlls[512];
extern WCHAR RtlpDphTargetDlls[512];
extern BOOLEAN RtlpPageHeapEnabled;
if (Peb->ProcessParameters &&
@ -144,8 +144,8 @@ LoadImageFileExecutionOptions(PPEB Peb)
LdrQueryImageFileExecutionOptions(&ImageName,
L"PageHeapFlags",
REG_DWORD,
(PVOID)&RtlpPageHeapGlobalFlags,
sizeof(RtlpPageHeapGlobalFlags),
(PVOID)&RtlpDphGlobalFlags,
sizeof(RtlpDphGlobalFlags),
&ValueSize);
LdrQueryImageFileExecutionOptions(&ImageName,
@ -179,8 +179,8 @@ LoadImageFileExecutionOptions(PPEB Peb)
LdrQueryImageFileExecutionOptions(&ImageName,
L"PageHeapTargetDlls",
REG_SZ,
(PVOID)RtlpPageHeapTargetDlls,
sizeof(RtlpPageHeapTargetDlls),
(PVOID)RtlpDphTargetDlls,
sizeof(RtlpDphTargetDlls),
&ValueSize);
/* Now when all parameters are read, enable page heap */

View file

@ -17,14 +17,368 @@
#define NDEBUG
#include <debug.h>
/* TYPES **********************************************************************/
typedef struct _DPH_BLOCK_INFORMATION
{
ULONG StartStamp;
PVOID Heap;
ULONG RequestedSize;
ULONG 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;
ULONG nVirtualBlockSize;
ULONG nVirtualAccessSize;
ULONG nUserRequestedSize;
ULONG 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;
PRTL_CRITICAL_SECTION HeapCritSect;
ULONG nRemoteLockAcquired;
PDPH_HEAP_BLOCK pVirtualStorageListHead;
PDPH_HEAP_BLOCK pVirtualStorageListTail;
ULONG nVirtualStorageRanges;
ULONG nVirtualStorageBytes;
RTL_AVL_TABLE BusyNodesTable;
PDPH_HEAP_BLOCK NodeToAllocate;
ULONG nBusyAllocations;
ULONG nBusyAllocationBytesCommitted;
PDPH_HEAP_BLOCK pFreeAllocationListHead;
PDPH_HEAP_BLOCK pFreeAllocationListTail;
ULONG nFreeAllocations;
ULONG nFreeAllocationBytesCommitted;
LIST_ENTRY AvailableAllocationHead;
ULONG nAvailableAllocations;
ULONG nAvailableAllocationBytesCommitted;
PDPH_HEAP_BLOCK pUnusedNodeListHead;
PDPH_HEAP_BLOCK pUnusedNodeListTail;
ULONG nUnusedNodes;
ULONG nBusyAllocationBytesAccessible;
PDPH_HEAP_BLOCK pNodePoolListHead;
PDPH_HEAP_BLOCK pNodePoolListTail;
ULONG nNodePools;
ULONG 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 RtlpPageHeapGlobalFlags;
ULONG RtlpDphGlobalFlags;
ULONG RtlpPageHeapSizeRangeStart, RtlpPageHeapSizeRangeEnd;
ULONG RtlpPageHeapDllRangeStart, RtlpPageHeapDllRangeEnd;
WCHAR RtlpPageHeapTargetDlls[512];
WCHAR RtlpDphTargetDlls[512];
ULONG RtlpDphBreakOptions;
ULONG RtlpDphDebugOptions;
LIST_ENTRY RtlpDphPageHeapList;
BOOLEAN RtlpDphPageHeapListInitialized;
RTL_CRITICAL_SECTION RtlpDphPageHeapListLock;
ULONG RtlpDphPageHeapListLength;
UNICODE_STRING RtlpDphTargetDllsUnicode;
/* Counters */
LONG RtlpDphCounter;
LONG RtlpDphAllocFails;
LONG RtlpDphReleaseFails;
LONG RtlpDphFreeFails;
LONG RtlpDphProtectFails;
#define DPH_RESERVE_SIZE 0x100000
#define DPH_POOL_SIZE 0x4000
/* 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
/* RtlpDphDebugOptions */
#define DPH_DEBUG_INTERNAL_VALIDATE 0x01
#define DPH_DEBUG_VERBOSE 0x04
/* Fillers */
#define DPH_FILL 0xEEEEEEEE
/* Signatures */
#define DPH_SIGNATURE 0xFFEEDDCC
/* FUNCTIONS ******************************************************************/
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);
/* 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, %p, %x) failed with %x \n", Base, Size, Type, Status);
DbgBreakPoint();
return Status;
}
}
else
{
_InterlockedIncrement(&RtlpDphAllocFails);
if (RtlpDphBreakOptions & DPH_BREAK_ON_COMMIT_FAIL)
{
DPRINT1("Page heap: AllocVm (%p, %p, %x) failed with %x \n", Base, Size, Type, Status);
DbgBreakPoint();
return Status;
}
}
}
return Status;
}
NTSTATUS NTAPI
RtlpDphFreeVm(PVOID Base, SIZE_T Size, ULONG Type, ULONG Protection)
{
NTSTATUS Status;
/* Free the memory */
Status = RtlpSecMemFreeVirtualMemory(NtCurrentProcess(), &Base, &Size, Type);
/* 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, %p, %x) failed with %x \n", Base, Size, Type, Status);
DbgBreakPoint();
return Status;
}
}
else
{
_InterlockedIncrement(&RtlpDphFreeFails);
if (RtlpDphBreakOptions & DPH_BREAK_ON_FREE_FAIL)
{
DPRINT1("Page heap: FreeVm (%p, %p, %x) failed with %x \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, %p, %x) failed with %x \n", Base, Size, Protection, Status);
DbgBreakPoint();
return Status;
}
}
return Status;
}
VOID NTAPI
RtlpDphAddNewPool(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK NodeBlock, PVOID Virtual, SIZE_T Size, BOOLEAN Add)
{
UNIMPLEMENTED;
}
PDPH_HEAP_BLOCK NTAPI
RtlpDphAllocateNode(PDPH_HEAP_ROOT DphRoot)
{
UNIMPLEMENTED;
return NULL;
}
VOID NTAPI
RtlpDphPlaceOnPoolList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK DphNode)
{
UNIMPLEMENTED;
}
VOID NTAPI
RtlpDphPlaceOnVirtualList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK DphNode)
{
UNIMPLEMENTED;
}
VOID NTAPI
RtlpDphCoalesceNodeIntoAvailable(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK DphNode)
{
UNIMPLEMENTED;
}
RTL_GENERIC_COMPARE_RESULTS
NTAPI
RtlpDphCompareNodeForTable(IN PRTL_AVL_TABLE Table,
IN PVOID FirstStruct,
IN PVOID SecondStruct)
{
/* FIXME: TODO */
ASSERT(FALSE);
return 0;
}
PVOID
NTAPI
RtlpDphAllocateNodeForTable(IN PRTL_AVL_TABLE Table,
IN CLONG ByteSize)
{
/* FIXME: TODO */
ASSERT(FALSE);
return NULL;
}
VOID
NTAPI
RtlpDphFreeNodeForTable(IN PRTL_AVL_TABLE Table,
IN PVOID Buffer)
{
/* FIXME: TODO */
ASSERT(FALSE);
}
NTSTATUS NTAPI
RtlpDphInitializeDelayedFreeQueue()
{
UNIMPLEMENTED;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
RtlpDphTargetDllsLogicInitialize()
{
UNIMPLEMENTED;
return STATUS_SUCCESS;
}
VOID NTAPI
RtlpDphInternalValidatePageHeap(PDPH_HEAP_ROOT DphRoot, PVOID Address, ULONG Value)
{
UNIMPLEMENTED;
}
NTSTATUS NTAPI
RtlpDphProcessStartupInitialization()
{
NTSTATUS Status;
PTEB Teb = NtCurrentTeb();
/* Initialize the DPH heap list and its critical section */
InitializeListHead(&RtlpDphPageHeapList);
Status = RtlInitializeCriticalSection(&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%X: page heap enabled with flags 0x%X.\n", Teb->ClientId.UniqueProcess, RtlpDphGlobalFlags);
return Status;
}
HANDLE NTAPI
RtlpPageHeapCreate(ULONG Flags,
PVOID Addr,
@ -33,7 +387,135 @@ RtlpPageHeapCreate(ULONG Flags,
PVOID Lock,
PRTL_HEAP_PARAMETERS Parameters)
{
return NULL;
PVOID Base;
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 */
RtlFillMemory(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 = (PRTL_CRITICAL_SECTION)((PCHAR)DphRoot + DPH_POOL_SIZE);
DphRoot->ExtraFlags = RtlpDphGlobalFlags;
ZwQueryPerformanceCounter(&PerfCounter, NULL);
DphRoot->Seed = PerfCounter.LowPart;
RtlInitializeCriticalSection(DphRoot->HeapCritSect);
/* 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 */
RtlEnterCriticalSection(&RtlpDphPageHeapListLock);
/* 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 */
RtlLeaveCriticalSection(&RtlpDphPageHeapListLock);
if (RtlpDphDebugOptions & DPH_DEBUG_VERBOSE)
{
DPRINT1("Page heap: process 0x%X 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