Author: fireball Date: Mon Feb 21 22:42:21 2011 New Revision: 50860
URL: http://svn.reactos.org/svn/reactos?rev=50860&view=rev Log: [RTL/DPH] - Implement heap free operation using already implemented busy/free/available/unused lists support and other base routines. - Implement missing place to free list and remove from busy list routines. - Implement find busy block routine (using AVL tree). - Fix a bug in RtlpDphCoalesceNodeIntoAvailable() which resulted in unwanted attempt to merge the only node with itself (which failed anyway). - Fix a bug in RtlpDphCoalesceNodeIntoAvailable() which incorrectly removed a node from available list (which is implemented as a standard NT double-linked list, compared to unused and free lists which are implemented as single-linked custom lists and busy list which is an AVL tree). Result of that bug was an infinite loop at the next attempt to traverse the list of available nodes. - In RtlpDphCoalesceFreeIntoAvailable() break when FreeAllocations becomes less than LeaveOnFreeList (before it would break 1 size too early). - Fix list traversal in RtlpDphSearchAvailableMemoryListForBestFit(). If it couldn't find a node, it would return the last node in the list instead of NULL. - In RtlpDphFindAvailableMemory(), a new smaller size should be 4 times smaller, not just 2. - Add a #if0-ed section in RtlpDphRemoveFromAvailableList which checks if a request to remove node not in the list performed. Used only for debugging. - Add a trace dprint to every type of list insert/removal operation for easier tracking. - Add break on NULL ptr freeing support. - RtlpDphSetProtectionAfterUse() is stubbed and protection is set directly in RtlpDphHeapFree(). To be moved into this function.
Modified: trunk/reactos/lib/rtl/heappage.c
Modified: trunk/reactos/lib/rtl/heappage.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/rtl/heappage.c?rev=5086... ============================================================================== --- trunk/reactos/lib/rtl/heappage.c [iso-8859-1] (original) +++ trunk/reactos/lib/rtl/heappage.c [iso-8859-1] Mon Feb 21 22:42:21 2011 @@ -137,20 +137,22 @@ #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_CHECK_UNDERRUN 0x10 -#define DPH_LOG_STACK_TRACES 0x0 //FIXME: Get correct value +#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
@@ -453,9 +455,31 @@ }
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) { - /* DphNode is being added to the tail of the list */ + 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 */ @@ -473,6 +497,8 @@ 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) @@ -490,6 +516,8 @@ 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;
@@ -508,6 +536,8 @@ 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) @@ -528,6 +558,37 @@
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);
@@ -541,11 +602,31 @@ }
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; @@ -578,12 +659,13 @@
/* 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; @@ -592,10 +674,9 @@ CurEntry = CurEntry->Flink; }
- /* Did we find a node to insert our one after? */ if (!PrevNode) { - /* No, just add to the head of the list then */ + /* That means either this list is empty, or we should add to the head of it */ InsertHeadList(AvailListHead, &Node->AvailableEntry); } else @@ -615,20 +696,22 @@ /* 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) - { - /* They are adjacent - merge! */ - Node->nVirtualBlockSize += NextNode->nVirtualBlockSize; - Node->pNextAlloc = NextNode->pNextAlloc; - RtlpDphReturnNodeToUnusedList(DphRoot, NextNode); - DphRoot->nAvailableAllocations--; + + /* 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) + { + /* 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--; + } } } } @@ -643,10 +726,12 @@ /* Make sure requested size is not too big */ ASSERT(FreeAllocations >= LeaveOnFreeList);
+ DPRINT("RtlpDphCoalesceFreeIntoAvailable(%p %d)\n", DphRoot, LeaveOnFreeList); + while (Node) { FreeAllocations--; - if (FreeAllocations <= LeaveOnFreeList) break; + if (FreeAllocations < LeaveOnFreeList) break;
/* Get the next pointer, because it may be changed after following two calls */ Next = Node->pNextAlloc; @@ -657,6 +742,7 @@ /* And put into the available */ RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
+ /* Go to the next node */ Node = Next; } } @@ -713,20 +799,21 @@ SIZE_T Size) { PLIST_ENTRY CurEntry; - PDPH_HEAP_BLOCK Node; + PDPH_HEAP_BLOCK Node, NodeFound = NULL;
CurEntry = DphRoot->AvailableAllocationHead.Flink;
- while (TRUE) - { - /* If we reached end of the list - return right away */ - if (CurEntry == &DphRoot->AvailableAllocationHead) return NULL; - + while (CurEntry != &DphRoot->AvailableAllocationHead) + { /* Get the current available node */ Node = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
/* Check its size */ - if (Node->nVirtualBlockSize >= Size) break; + if (Node->nVirtualBlockSize >= Size) + { + NodeFound = Node; + break; + }
/* Move to the next available entry */ CurEntry = CurEntry->Flink; @@ -736,7 +823,7 @@ //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Flink)); //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Blink));
- return Node; + return NodeFound; }
PDPH_HEAP_BLOCK NTAPI @@ -757,7 +844,7 @@ if (DphRoot->nFreeAllocations <= DPH_FREE_LIST_MINIMUM) break;
/* Calculate a new free list size */ - NewSize = DphRoot->nFreeAllocations >> 1; + NewSize = DphRoot->nFreeAllocations >> 2; if (NewSize < DPH_FREE_LIST_MINIMUM) NewSize = DPH_FREE_LIST_MINIMUM;
/* Coalesce free into available */ @@ -795,6 +882,23 @@ 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) { @@ -815,6 +919,20 @@ Protection = PAGE_READWRITE;
return RtlpDphProtectVm(Base, UserSize, Protection); +} + +NTSTATUS NTAPI +RtlpDphSetProtectionAfterUse(PDPH_HEAP_ROOT DphRoot, /*PUCHAR VirtualBlock*/PDPH_HEAP_BLOCK Node) +{ + // FIXME: Bring stuff here + if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN) + { + } + else + { + } + + return STATUS_SUCCESS; }
PDPH_HEAP_BLOCK NTAPI @@ -1685,8 +1803,91 @@ ULONG Flags, PVOID Ptr) { - UNIMPLEMENTED; - return FALSE; + PDPH_HEAP_ROOT DphRoot; + PDPH_HEAP_BLOCK Node; + ULONG ValidationInfo; + PDPH_BLOCK_INFORMATION Info; + + /* Check for a NULL pointer freeing */ + if (!HeapPtr) + { + 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