/* * 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 #include #define NDEBUG #include /* 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(PDPH_HEAP_ROOT DphRoot, ULONG Reserved, PVOID Block, 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; } 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, %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, %Ix, %x) failed with %x \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, %x) status %x \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, %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, %Ix, %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, %Ix, %x) failed with %x \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(PDPH_HEAP_ROOT DphRoot, ULONG Reserved, PVOID Block, ULONG ValidationInfo) { //RtlpDphGetBlockSizeFromCorruptedBlock(); if (ValidationInfo & DPH_VALINFO_CORRUPTED_AFTER_FREE) { DPRINT1("block corrupted after having been freed\n"); } if (ValidationInfo & DPH_VALINFO_ALREADY_FREED) { DPRINT1("block already freed\n"); } if (ValidationInfo & DPH_VALINFO_BAD_INFIX_PATTERN) { DPRINT1("corrupted infix pattern for freed block\n"); } if (ValidationInfo & DPH_VALINFO_BAD_POINTER) { DPRINT1("corrupted heap pointer or using wrong heap\n"); } if (ValidationInfo & DPH_VALINFO_BAD_SUFFIX_PATTERN) { DPRINT1("corrupted suffix pattern\n"); } if (ValidationInfo & DPH_VALINFO_BAD_PREFIX_PATTERN) { DPRINT1("corrupted prefix pattern\n"); } if (ValidationInfo & DPH_VALINFO_BAD_START_STAMP) { DPRINT1("corrupted start stamp\n"); } if (ValidationInfo & DPH_VALINFO_BAD_END_STAMP) { DPRINT1("corrupted end stamp\n"); } if (ValidationInfo & DPH_VALINFO_EXCEPTION) { DPRINT1("exception raised while verifying block\n"); } DPRINT1("Corrupted heap block %p\n", Block); } 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 */