diff --git a/reactos/dll/ntdll/ldr/startup.c b/reactos/dll/ntdll/ldr/startup.c index 2b5ae0140a9..06b90b07558 100644 --- a/reactos/dll/ntdll/ldr/startup.c +++ b/reactos/dll/ntdll/ldr/startup.c @@ -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 */ diff --git a/reactos/lib/rtl/heappage.c b/reactos/lib/rtl/heappage.c index a953d4ceb39..fb9ba898d06 100644 --- a/reactos/lib/rtl/heappage.c +++ b/reactos/lib/rtl/heappage.c @@ -17,14 +17,368 @@ #define NDEBUG #include +/* 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