xref: /reactos/sdk/lib/rtl/heappage.c (revision 0b366ea1)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/rtl/heappage.c
5  * PURPOSE:         RTL Page Heap implementation
6  * PROGRAMMERS:     Copyright 2011 Aleksey Bragin
7  */
8 
9 /* Useful references:
10     http://msdn.microsoft.com/en-us/library/ms220938(VS.80).aspx
11     http://blogs.msdn.com/b/jiangyue/archive/2010/03/16/windows-heap-overrun-monitoring.aspx
12 */
13 
14 /* INCLUDES *****************************************************************/
15 
16 #include <rtl.h>
17 #include <heap.h>
18 #include <reactos/verifier.h>
19 
20 #define NDEBUG
21 #include <debug.h>
22 
23 /* TYPES **********************************************************************/
24 
25 typedef struct _DPH_BLOCK_INFORMATION
26 {
27      ULONG StartStamp;
28      PVOID Heap;
29      SIZE_T RequestedSize;
30      SIZE_T ActualSize;
31      union
32      {
33           LIST_ENTRY FreeQueue;
34           SINGLE_LIST_ENTRY FreePushList;
35           WORD TraceIndex;
36      };
37      PVOID StackTrace;
38      ULONG EndStamp;
39 } DPH_BLOCK_INFORMATION, *PDPH_BLOCK_INFORMATION;
40 
41 typedef struct _DPH_HEAP_BLOCK
42 {
43      union
44      {
45           struct _DPH_HEAP_BLOCK *pNextAlloc;
46           LIST_ENTRY AvailableEntry;
47           RTL_BALANCED_LINKS TableLinks;
48      };
49      PUCHAR pUserAllocation;
50      PUCHAR pVirtualBlock;
51      SIZE_T nVirtualBlockSize;
52      SIZE_T nVirtualAccessSize;
53      SIZE_T nUserRequestedSize;
54      SIZE_T nUserActualSize;
55      PVOID UserValue;
56      ULONG UserFlags;
57      PRTL_TRACE_BLOCK StackTrace;
58      LIST_ENTRY AdjacencyEntry;
59      PUCHAR pVirtualRegion;
60 } DPH_HEAP_BLOCK, *PDPH_HEAP_BLOCK;
61 
62 typedef struct _DPH_HEAP_ROOT
63 {
64      ULONG Signature;
65      ULONG HeapFlags;
66      PHEAP_LOCK HeapCritSect;
67      ULONG nRemoteLockAcquired;
68 
69      PDPH_HEAP_BLOCK pVirtualStorageListHead;
70      PDPH_HEAP_BLOCK pVirtualStorageListTail;
71      ULONG nVirtualStorageRanges;
72      SIZE_T nVirtualStorageBytes;
73 
74      RTL_AVL_TABLE BusyNodesTable;
75      PDPH_HEAP_BLOCK NodeToAllocate;
76      ULONG nBusyAllocations;
77      SIZE_T nBusyAllocationBytesCommitted;
78 
79      PDPH_HEAP_BLOCK pFreeAllocationListHead;
80      PDPH_HEAP_BLOCK pFreeAllocationListTail;
81      ULONG nFreeAllocations;
82      SIZE_T nFreeAllocationBytesCommitted;
83 
84      LIST_ENTRY AvailableAllocationHead;
85      ULONG nAvailableAllocations;
86      SIZE_T nAvailableAllocationBytesCommitted;
87 
88      PDPH_HEAP_BLOCK pUnusedNodeListHead;
89      PDPH_HEAP_BLOCK pUnusedNodeListTail;
90      ULONG nUnusedNodes;
91      SIZE_T nBusyAllocationBytesAccessible;
92      PDPH_HEAP_BLOCK pNodePoolListHead;
93      PDPH_HEAP_BLOCK pNodePoolListTail;
94      ULONG nNodePools;
95      SIZE_T nNodePoolBytes;
96 
97      LIST_ENTRY NextHeap;
98      ULONG ExtraFlags;
99      ULONG Seed;
100      PVOID NormalHeap;
101      PRTL_TRACE_BLOCK CreateStackTrace;
102      PVOID FirstThread;
103 } DPH_HEAP_ROOT, *PDPH_HEAP_ROOT;
104 
105 /* GLOBALS ********************************************************************/
106 
107 BOOLEAN RtlpPageHeapEnabled = FALSE;
108 ULONG RtlpDphGlobalFlags;
109 ULONG RtlpPageHeapSizeRangeStart, RtlpPageHeapSizeRangeEnd;
110 ULONG RtlpPageHeapDllRangeStart, RtlpPageHeapDllRangeEnd;
111 WCHAR RtlpDphTargetDlls[512];
112 
113 LIST_ENTRY RtlpDphPageHeapList;
114 BOOLEAN RtlpDphPageHeapListInitialized;
115 HEAP_LOCK _RtlpDphPageHeapListLock;
116 PHEAP_LOCK RtlpDphPageHeapListLock = &_RtlpDphPageHeapListLock;
117 ULONG RtlpDphPageHeapListLength;
118 UNICODE_STRING RtlpDphTargetDllsUnicode;
119 
120 HEAP_LOCK _RtlpDphDelayedFreeQueueLock;
121 PHEAP_LOCK RtlpDphDelayedFreeQueueLock = &_RtlpDphDelayedFreeQueueLock;
122 LIST_ENTRY RtlpDphDelayedFreeQueue;
123 SLIST_HEADER RtlpDphDelayedTemporaryPushList;
124 SIZE_T RtlpDphMemoryUsedByDelayedFreeBlocks;
125 ULONG RtlpDphNumberOfDelayedFreeBlocks;
126 
127 /* Counters */
128 LONG RtlpDphCounter;
129 LONG RtlpDphAllocFails;
130 LONG RtlpDphReleaseFails;
131 LONG RtlpDphFreeFails;
132 LONG RtlpDphProtectFails;
133 
134 #define DPH_RESERVE_SIZE 0x100000
135 #define DPH_POOL_SIZE 0x4000
136 #define DPH_FREE_LIST_MINIMUM 8
137 
138 /* RtlpDphBreakOptions */
139 #define DPH_BREAK_ON_RESERVE_FAIL 0x01
140 #define DPH_BREAK_ON_COMMIT_FAIL  0x02
141 #define DPH_BREAK_ON_RELEASE_FAIL 0x04
142 #define DPH_BREAK_ON_FREE_FAIL    0x08
143 #define DPH_BREAK_ON_PROTECT_FAIL 0x10
144 #define DPH_BREAK_ON_NULL_FREE    0x80
145 
146 /* RtlpDphDebugOptions */
147 #define DPH_DEBUG_INTERNAL_VALIDATE 0x01
148 #define DPH_DEBUG_VERBOSE           0x04
149 
150 /* DPH ExtraFlags */
151 #define DPH_EXTRA_LOG_STACK_TRACES 0x02
152 #define DPH_EXTRA_CHECK_UNDERRUN   0x10
153 
154 /* Fillers */
155 #define DPH_FILL 0xEEEEEEEE
156 #define DPH_FILL_START_STAMP_1 0xABCDBBBB
157 #define DPH_FILL_START_STAMP_2 0xABCDBBBA
158 #define DPH_FILL_END_STAMP_1   0xDCBABBBB
159 #define DPH_FILL_END_STAMP_2   0xDCBABBBA
160 #define DPH_FILL_SUFFIX        0xD0
161 #define DPH_FILL_INFIX         0xC0
162 
163 /* Validation info flags */
164 #define DPH_VALINFO_BAD_START_STAMP      0x01
165 #define DPH_VALINFO_BAD_END_STAMP        0x02
166 #define DPH_VALINFO_BAD_POINTER          0x04
167 #define DPH_VALINFO_BAD_PREFIX_PATTERN   0x08
168 #define DPH_VALINFO_BAD_SUFFIX_PATTERN   0x10
169 #define DPH_VALINFO_EXCEPTION            0x20
170 #define DPH_VALINFO_1                    0x40
171 #define DPH_VALINFO_BAD_INFIX_PATTERN    0x80
172 #define DPH_VALINFO_ALREADY_FREED        0x100
173 #define DPH_VALINFO_CORRUPTED_AFTER_FREE 0x200
174 
175 /* Signatures */
176 #define DPH_SIGNATURE 0xFFEEDDCC
177 
178 /* Biased pointer macros */
179 #define IS_BIASED_POINTER(ptr) ((ULONG_PTR)(ptr) & 1)
180 #define POINTER_REMOVE_BIAS(ptr) ((ULONG_PTR)(ptr) & ~(ULONG_PTR)1)
181 #define POINTER_ADD_BIAS(ptr) ((ULONG_PTR)(ptr) | 1)
182 
183 
184 ULONG RtlpDphBreakOptions = 0;//0xFFFFFFFF;
185 ULONG RtlpDphDebugOptions;
186 
187 /* FUNCTIONS ******************************************************************/
188 
189 BOOLEAN NTAPI
190 RtlpDphGrowVirtual(PDPH_HEAP_ROOT DphRoot, SIZE_T Size);
191 
192 BOOLEAN NTAPI
193 RtlpDphIsNormalFreeHeapBlock(PVOID Block, PULONG ValidationInformation, BOOLEAN CheckFillers);
194 
195 VOID NTAPI
196 RtlpDphReportCorruptedBlock(
197     _In_ PDPH_HEAP_ROOT DphRoot,
198     _In_ ULONG Reserved,
199     _In_ PVOID Block,
200     _In_ ULONG ValidationInfo);
201 
202 BOOLEAN NTAPI
203 RtlpDphNormalHeapValidate(PDPH_HEAP_ROOT DphRoot, ULONG Flags, PVOID BaseAddress);
204 
205 
206 VOID NTAPI
207 RtlpDphRaiseException(NTSTATUS Status)
208 {
209     EXCEPTION_RECORD Exception;
210 
211     /* Initialize exception record */
212     Exception.ExceptionCode = Status;
213     Exception.ExceptionAddress = RtlpDphRaiseException;
214     Exception.ExceptionFlags = 0;
215     Exception.ExceptionRecord = NULL;
216     Exception.NumberParameters = 0;
217 
218     /* Raise the exception */
219     RtlRaiseException(&Exception);
220 }
221 
222 PVOID NTAPI
223 RtlpDphPointerFromHandle(PVOID Handle)
224 {
225     PHEAP NormalHeap = (PHEAP)Handle;
226     PDPH_HEAP_ROOT DphHeap = (PDPH_HEAP_ROOT)((PUCHAR)Handle + PAGE_SIZE);
227 
228     if (NormalHeap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
229     {
230         if (DphHeap->Signature == DPH_SIGNATURE)
231             return DphHeap;
232     }
233 
234     DPRINT1("heap handle with incorrect signature\n");
235     DbgBreakPoint();
236     return NULL;
237 }
238 
239 PVOID NTAPI
240 RtlpDphHeapFromPointer(PDPH_HEAP_ROOT DphHeap)
241 {
242     return ((PUCHAR)DphHeap) - PAGE_SIZE;
243 }
244 
245 ULONG NTAPI
246 RtlpDphGetBlockSizeFromCorruptedBlock(PVOID Block)
247 {
248     PDPH_BLOCK_INFORMATION BlockInfo;
249     BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
250 
251     /* Check stamps */
252     if (BlockInfo->StartStamp != DPH_FILL_START_STAMP_1 && BlockInfo->StartStamp != DPH_FILL_START_STAMP_2)
253     {
254         return 0;
255     }
256 
257     return BlockInfo->RequestedSize;
258 }
259 
260 VOID NTAPI
261 RtlpDphEnterCriticalSection(PDPH_HEAP_ROOT DphRoot, ULONG Flags)
262 {
263     if (Flags & HEAP_NO_SERIALIZE)
264     {
265         /* More complex scenario */
266         if (!RtlTryEnterHeapLock(DphRoot->HeapCritSect, TRUE))
267         {
268             if (!DphRoot->nRemoteLockAcquired)
269             {
270                 DPRINT1("multithreaded access in HEAP_NO_SERIALIZE heap\n");
271                 DbgBreakPoint();
272 
273                 /* Clear out the no serialize flag */
274                 DphRoot->HeapFlags &= ~HEAP_NO_SERIALIZE;
275             }
276 
277             /* Enter the heap's critical section */
278             RtlEnterHeapLock(DphRoot->HeapCritSect, TRUE);
279         }
280     }
281     else
282     {
283         /* Just enter the heap's critical section */
284         RtlEnterHeapLock(DphRoot->HeapCritSect, TRUE);
285     }
286 }
287 
288 VOID NTAPI
289 RtlpDphLeaveCriticalSection(PDPH_HEAP_ROOT DphRoot)
290 {
291     /* Just leave the heap's critical section */
292     RtlLeaveHeapLock(DphRoot->HeapCritSect);
293 }
294 
295 
296 VOID NTAPI
297 RtlpDphPreProcessing(PDPH_HEAP_ROOT DphRoot, ULONG Flags)
298 {
299     RtlpDphEnterCriticalSection(DphRoot, Flags);
300 
301     /* FIXME: Validate integrity, internal lists if necessary */
302 }
303 
304 VOID NTAPI
305 RtlpDphPostProcessing(PDPH_HEAP_ROOT DphRoot)
306 {
307     if (!DphRoot) return;
308 
309     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
310     {
311         /* FIXME: Validate integrity, internal lists if necessary */
312     }
313 
314     /* Release the lock */
315     RtlpDphLeaveCriticalSection(DphRoot);
316 }
317 
318 NTSTATUS NTAPI
319 RtlpSecMemFreeVirtualMemory(HANDLE Process, PVOID *Base, PSIZE_T Size, ULONG Type)
320 {
321     NTSTATUS Status;
322     //PVOID *SavedBase = Base;
323     //PSIZE_T SavedSize = Size;
324 
325     /* Free the memory */
326     Status = ZwFreeVirtualMemory(Process, Base, Size, Type);
327 
328     /* Flush secure memory cache if needed and retry freeing */
329 #if 0
330     if (Status == STATUS_INVALID_PAGE_PROTECTION &&
331         Process == NtCurrentProcess() &&
332         RtlFlushSecureMemoryCache(*SavedBase, *SavedSize))
333     {
334         Status = ZwFreeVirtualMemory(NtCurrentProcess(), SavedBase, SavedSize, Type);
335     }
336 #endif
337 
338     return Status;
339 }
340 
341 NTSTATUS NTAPI
342 RtlpDphAllocateVm(PVOID *Base, SIZE_T Size, ULONG Type, ULONG Protection)
343 {
344     NTSTATUS Status;
345     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
346                                      Base,
347                                      0,
348                                      &Size,
349                                      Type,
350                                      Protection);
351     DPRINT("Page heap: AllocVm (%p, %Ix, %lx) status %lx\n", Base, Size, Type, Status);
352     /* Check for failures */
353     if (!NT_SUCCESS(Status))
354     {
355         if (Type == MEM_RESERVE)
356         {
357             _InterlockedIncrement(&RtlpDphCounter);
358             if (RtlpDphBreakOptions & DPH_BREAK_ON_RESERVE_FAIL)
359             {
360                 DPRINT1("Page heap: AllocVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
361                 DbgBreakPoint();
362                 return Status;
363             }
364         }
365         else
366         {
367             _InterlockedIncrement(&RtlpDphAllocFails);
368             if (RtlpDphBreakOptions & DPH_BREAK_ON_COMMIT_FAIL)
369             {
370                 DPRINT1("Page heap: AllocVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
371                 DbgBreakPoint();
372                 return Status;
373             }
374         }
375     }
376 
377     return Status;
378 }
379 
380 NTSTATUS NTAPI
381 RtlpDphFreeVm(PVOID Base, SIZE_T Size, ULONG Type)
382 {
383     NTSTATUS Status;
384 
385     /* Free the memory */
386     Status = RtlpSecMemFreeVirtualMemory(NtCurrentProcess(), &Base, &Size, Type);
387     DPRINT("Page heap: FreeVm (%p, %Ix, %lx) status %lx\n", Base, Size, Type, Status);
388     /* Log/report failures */
389     if (!NT_SUCCESS(Status))
390     {
391         if (Type == MEM_RELEASE)
392         {
393             _InterlockedIncrement(&RtlpDphReleaseFails);
394             if (RtlpDphBreakOptions & DPH_BREAK_ON_RELEASE_FAIL)
395             {
396                 DPRINT1("Page heap: FreeVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
397                 DbgBreakPoint();
398                 return Status;
399             }
400         }
401         else
402         {
403             _InterlockedIncrement(&RtlpDphFreeFails);
404             if (RtlpDphBreakOptions & DPH_BREAK_ON_FREE_FAIL)
405             {
406                 DPRINT1("Page heap: FreeVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Type, Status);
407                 DbgBreakPoint();
408                 return Status;
409             }
410         }
411     }
412 
413     return Status;
414 }
415 
416 NTSTATUS NTAPI
417 RtlpDphProtectVm(PVOID Base, SIZE_T Size, ULONG Protection)
418 {
419     NTSTATUS Status;
420     ULONG OldProtection;
421 
422     /* Change protection */
423     Status = ZwProtectVirtualMemory(NtCurrentProcess(), &Base, &Size, Protection, &OldProtection);
424 
425     /* Log/report failures */
426     if (!NT_SUCCESS(Status))
427     {
428         _InterlockedIncrement(&RtlpDphProtectFails);
429         if (RtlpDphBreakOptions & DPH_BREAK_ON_PROTECT_FAIL)
430         {
431             DPRINT1("Page heap: ProtectVm (%p, %Ix, %lx) failed with %lx\n", Base, Size, Protection, Status);
432             DbgBreakPoint();
433             return Status;
434         }
435     }
436 
437     return Status;
438 }
439 
440 BOOLEAN NTAPI
441 RtlpDphWritePageHeapBlockInformation(PDPH_HEAP_ROOT DphRoot, PVOID UserAllocation, SIZE_T Size, SIZE_T UserSize)
442 {
443     PDPH_BLOCK_INFORMATION BlockInfo;
444     PUCHAR FillPtr;
445 
446     /* Get pointer to the block info structure */
447     BlockInfo = (PDPH_BLOCK_INFORMATION)UserAllocation - 1;
448 
449     /* Set up basic fields */
450     BlockInfo->Heap = DphRoot;
451     BlockInfo->ActualSize = UserSize;
452     BlockInfo->RequestedSize = Size;
453     BlockInfo->StartStamp = DPH_FILL_START_STAMP_1;
454     BlockInfo->EndStamp = DPH_FILL_END_STAMP_1;
455 
456     /* Fill with a pattern */
457     FillPtr = (PUCHAR)UserAllocation + Size;
458     RtlFillMemory(FillPtr, ROUND_UP(FillPtr, PAGE_SIZE) - (ULONG_PTR)FillPtr, DPH_FILL_SUFFIX);
459 
460     /* FIXME: Check if logging stack traces is turned on */
461     //if (DphRoot->ExtraFlags &
462 
463     return TRUE;
464 }
465 
466 VOID NTAPI
467 RtlpDphPlaceOnBusyList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK DphNode)
468 {
469     BOOLEAN NewElement;
470     PVOID AddressUserData;
471 
472     DPRINT("RtlpDphPlaceOnBusyList(%p %p)\n", DphRoot, DphNode);
473 
474     /* Add it to the AVL busy nodes table */
475     DphRoot->NodeToAllocate = DphNode;
476     AddressUserData = RtlInsertElementGenericTableAvl(&DphRoot->BusyNodesTable,
477                                                       &DphNode->pUserAllocation,
478                                                       sizeof(ULONG_PTR),
479                                                       &NewElement);
480 
481     ASSERT(AddressUserData == &DphNode->pUserAllocation);
482     ASSERT(NewElement == TRUE);
483 
484     /* Update heap counters */
485     DphRoot->nBusyAllocations++;
486     DphRoot->nBusyAllocationBytesAccessible += DphNode->nVirtualAccessSize;
487     DphRoot->nBusyAllocationBytesCommitted += DphNode->nVirtualBlockSize;
488 }
489 
490 VOID NTAPI
491 RtlpDphPlaceOnFreeList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
492 {
493     DPRINT("RtlpDphPlaceOnFreeList(%p %p)\n", DphRoot, Node);
494 
495     /* Node is being added to the tail of the list */
496     Node->pNextAlloc = NULL;
497 
498     /* Add it to the tail of the linked list */
499     if (DphRoot->pFreeAllocationListTail)
500         DphRoot->pFreeAllocationListTail->pNextAlloc = Node;
501     else
502         DphRoot->pFreeAllocationListHead = Node;
503     DphRoot->pFreeAllocationListTail = Node;
504 
505     /* Update byte counts taking in account this new node */
506     DphRoot->nFreeAllocations++;
507     DphRoot->nFreeAllocationBytesCommitted += Node->nVirtualBlockSize;
508 }
509 
510 VOID NTAPI
511 RtlpDphPlaceOnPoolList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
512 {
513     DPRINT("RtlpDphPlaceOnPoolList(%p %p)\n", DphRoot, Node);
514 
515     /* Node is being added to the tail of the list */
516     Node->pNextAlloc = NULL;
517 
518     /* Add it to the tail of the linked list */
519     if (DphRoot->pNodePoolListTail)
520         DphRoot->pNodePoolListTail->pNextAlloc = Node;
521     else
522         DphRoot->pNodePoolListHead = Node;
523     DphRoot->pNodePoolListTail = Node;
524 
525     /* Update byte counts taking in account this new node */
526     DphRoot->nNodePools++;
527     DphRoot->nNodePoolBytes += Node->nVirtualBlockSize;
528 }
529 
530 VOID NTAPI
531 RtlpDphPlaceOnVirtualList(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK Node)
532 {
533     DPRINT("RtlpDphPlaceOnVirtualList(%p %p)\n", DphRoot, Node);
534 
535     /* Add it to the head of the virtual list */
536     Node->pNextAlloc = DphRoot->pVirtualStorageListHead;
537     if (!DphRoot->pVirtualStorageListHead)
538         DphRoot->pVirtualStorageListTail = Node;
539     DphRoot->pVirtualStorageListHead = Node;
540 
541     /* Update byte counts taking in account this new node */
542     DphRoot->nVirtualStorageRanges++;
543     DphRoot->nVirtualStorageBytes += Node->nVirtualBlockSize;
544 }
545 
546 PDPH_HEAP_BLOCK NTAPI
547 RtlpDphTakeNodeFromUnusedList(PDPH_HEAP_ROOT DphRoot)
548 {
549     PDPH_HEAP_BLOCK Node = DphRoot->pUnusedNodeListHead;
550     PDPH_HEAP_BLOCK Next;
551 
552     DPRINT("RtlpDphTakeNodeFromUnusedList(%p), ret %p\n", DphRoot, Node);
553 
554     /* Take the first entry */
555     if (!Node) return NULL;
556 
557     /* Remove that entry (Node) from the list */
558     Next = Node->pNextAlloc;
559     if (DphRoot->pUnusedNodeListHead == Node) DphRoot->pUnusedNodeListHead = Next;
560     if (DphRoot->pUnusedNodeListTail == Node) DphRoot->pUnusedNodeListTail = NULL;
561 
562     /* Decrease amount of unused nodes */
563     DphRoot->nUnusedNodes--;
564 
565     return Node;
566 }
567 
568 VOID NTAPI
569 RtlpDphReturnNodeToUnusedList(PDPH_HEAP_ROOT DphRoot,
570                               PDPH_HEAP_BLOCK Node)
571 {
572     DPRINT("RtlpDphReturnNodeToUnusedList(%p, %p)\n", DphRoot, Node);
573 
574     /* Add it back to the head of the unused list */
575     Node->pNextAlloc = DphRoot->pUnusedNodeListHead;
576     if (!DphRoot->pUnusedNodeListHead)
577         DphRoot->pUnusedNodeListTail = Node;
578     DphRoot->pUnusedNodeListHead = Node;
579 
580     /* Increase amount of unused nodes */
581     DphRoot->nUnusedNodes++;
582 }
583 
584 VOID NTAPI
585 RtlpDphRemoveFromAvailableList(PDPH_HEAP_ROOT DphRoot,
586                                PDPH_HEAP_BLOCK Node)
587 {
588     /* Make sure Adjacency list pointers are biased */
589     //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Flink));
590     //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Blink));
591 
592     DPRINT("RtlpDphRemoveFromAvailableList(%p %p)\n", DphRoot, Node);
593 
594     /* Check if it is in the list */
595 #if 0
596     {
597         PLIST_ENTRY CurEntry;
598         PDPH_HEAP_BLOCK NodeEntry;
599         BOOLEAN Found = FALSE;
600 
601         /* Find where to put this node according to its virtual address */
602         CurEntry = DphRoot->AvailableAllocationHead.Flink;
603 
604         while (CurEntry != &DphRoot->AvailableAllocationHead)
605         {
606             NodeEntry = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
607 
608             if (NodeEntry == Node)
609             {
610                 Found = TRUE;
611                 break;
612             }
613 
614             CurEntry = CurEntry->Flink;
615         }
616 
617         if (!Found)
618         {
619             DPRINT1("Trying to remove non-existing in availlist node!\n");
620             DbgBreakPoint();
621         }
622     }
623 #endif
624 
625     /* Remove it from the list */
626     RemoveEntryList(&Node->AvailableEntry);
627 
628     /* Decrease heap counters */
629     DphRoot->nAvailableAllocations--;
630     DphRoot->nAvailableAllocationBytesCommitted -= Node->nVirtualBlockSize;
631 
632     /* Remove bias from the AdjacencyEntry pointer */
633     Node->AdjacencyEntry.Flink = (PLIST_ENTRY)POINTER_REMOVE_BIAS(Node->AdjacencyEntry.Flink);
634     Node->AdjacencyEntry.Blink = (PLIST_ENTRY)POINTER_REMOVE_BIAS(Node->AdjacencyEntry.Blink);
635 }
636 
637 VOID NTAPI
638 RtlpDphRemoveFromBusyList(PDPH_HEAP_ROOT DphRoot,
639                           PDPH_HEAP_BLOCK Node)
640 {
641     BOOLEAN ElementPresent;
642 
643     DPRINT("RtlpDphRemoveFromBusyList(%p %p)\n", DphRoot, Node);
644 
645     /* Delete it from busy nodes table */
646     ElementPresent = RtlDeleteElementGenericTableAvl(&DphRoot->BusyNodesTable, &Node->pUserAllocation);
647     ASSERT(ElementPresent == TRUE);
648 
649     /* Update counters */
650     DphRoot->nBusyAllocations--;
651     DphRoot->nBusyAllocationBytesCommitted -= Node->nVirtualBlockSize;
652     DphRoot->nBusyAllocationBytesAccessible -= Node->nVirtualAccessSize;
653 }
654 
655 VOID NTAPI
656 RtlpDphRemoveFromFreeList(PDPH_HEAP_ROOT DphRoot,
657                           PDPH_HEAP_BLOCK Node,
658                           PDPH_HEAP_BLOCK Prev)
659 {
660     PDPH_HEAP_BLOCK Next;
661 
662     DPRINT("RtlpDphRemoveFromFreeList(%p %p %p)\n", DphRoot, Node, Prev);
663 
664     /* Detach it from the list */
665     Next = Node->pNextAlloc;
666     if (DphRoot->pFreeAllocationListHead == Node)
667         DphRoot->pFreeAllocationListHead = Next;
668     if (DphRoot->pFreeAllocationListTail == Node)
669         DphRoot->pFreeAllocationListTail = Prev;
670     if (Prev) Prev->pNextAlloc = Next;
671 
672     /* Decrease heap counters */
673     DphRoot->nFreeAllocations--;
674     DphRoot->nFreeAllocationBytesCommitted -= Node->nVirtualBlockSize;
675 
676     Node->StackTrace = NULL;
677 }
678 
679 VOID NTAPI
680 RtlpDphCoalesceNodeIntoAvailable(PDPH_HEAP_ROOT DphRoot,
681                                  PDPH_HEAP_BLOCK Node)
682 {
683     PDPH_HEAP_BLOCK NodeEntry, PrevNode = NULL, NextNode;
684     PLIST_ENTRY AvailListHead;
685     PLIST_ENTRY CurEntry;
686 
687     DPRINT("RtlpDphCoalesceNodeIntoAvailable(%p %p)\n", DphRoot, Node);
688 
689     /* Update heap counters */
690     DphRoot->nAvailableAllocationBytesCommitted += Node->nVirtualBlockSize;
691     DphRoot->nAvailableAllocations++;
692 
693     /* Find where to put this node according to its virtual address */
694     AvailListHead = &DphRoot->AvailableAllocationHead;
695 
696     /* Find a point where to insert an available node */
697     CurEntry = AvailListHead->Flink;
698 
699     while (CurEntry != AvailListHead)
700     {
701         NodeEntry = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
702         if (NodeEntry->pVirtualBlock >= Node->pVirtualBlock)
703         {
704             PrevNode = NodeEntry;
705             break;
706         }
707         CurEntry = CurEntry->Flink;
708     }
709 
710     if (!PrevNode)
711     {
712         /* That means either this list is empty, or we should add to the head of it */
713         InsertHeadList(AvailListHead, &Node->AvailableEntry);
714     }
715     else
716     {
717         /* Check the previous node and merge if possible */
718         if (PrevNode->pVirtualBlock + PrevNode->nVirtualBlockSize == Node->pVirtualBlock)
719         {
720             /* Check they actually belong to the same virtual memory block */
721             NTSTATUS Status;
722             MEMORY_BASIC_INFORMATION MemoryBasicInfo;
723 
724             Status = ZwQueryVirtualMemory(
725                 ZwCurrentProcess(),
726                 Node->pVirtualBlock,
727                 MemoryBasicInformation,
728                 &MemoryBasicInfo,
729                 sizeof(MemoryBasicInfo),
730                 NULL);
731 
732             /* There is no way this can fail, we committed this memory! */
733             ASSERT(NT_SUCCESS(Status));
734 
735             if ((PUCHAR)MemoryBasicInfo.AllocationBase <= PrevNode->pVirtualBlock)
736             {
737                 /* They are adjacent, and from the same VM region. - merge! */
738                 PrevNode->nVirtualBlockSize += Node->nVirtualBlockSize;
739                 RtlpDphReturnNodeToUnusedList(DphRoot, Node);
740                 DphRoot->nAvailableAllocations--;
741 
742                 Node = PrevNode;
743             }
744             else
745             {
746                 /* Insert after PrevNode */
747                 InsertTailList(&PrevNode->AvailableEntry, &Node->AvailableEntry);
748             }
749         }
750         else
751         {
752             /* Insert after PrevNode */
753             InsertTailList(&PrevNode->AvailableEntry, &Node->AvailableEntry);
754         }
755 
756         /* Now check the next entry after our one */
757         if (Node->AvailableEntry.Flink != AvailListHead)
758         {
759             NextNode = CONTAINING_RECORD(Node->AvailableEntry.Flink, DPH_HEAP_BLOCK, AvailableEntry);
760             /* Node is not at the tail of the list, check if it's adjacent */
761             if (Node->pVirtualBlock + Node->nVirtualBlockSize == NextNode->pVirtualBlock)
762             {
763                 /* Check they actually belong to the same virtual memory block */
764                 NTSTATUS Status;
765                 MEMORY_BASIC_INFORMATION MemoryBasicInfo;
766 
767                 Status = ZwQueryVirtualMemory(
768                     ZwCurrentProcess(),
769                     NextNode->pVirtualBlock,
770                     MemoryBasicInformation,
771                     &MemoryBasicInfo,
772                     sizeof(MemoryBasicInfo),
773                     NULL);
774 
775                 /* There is no way this can fail, we committed this memory! */
776                 ASSERT(NT_SUCCESS(Status));
777 
778                 if ((PUCHAR)MemoryBasicInfo.AllocationBase <= Node->pVirtualBlock)
779                 {
780                     /* They are adjacent - merge! */
781                     Node->nVirtualBlockSize += NextNode->nVirtualBlockSize;
782 
783                     /* Remove next entry from the list and put it into unused entries list */
784                     RemoveEntryList(&NextNode->AvailableEntry);
785                     RtlpDphReturnNodeToUnusedList(DphRoot, NextNode);
786                     DphRoot->nAvailableAllocations--;
787                 }
788             }
789         }
790     }
791 }
792 
793 VOID NTAPI
794 RtlpDphCoalesceFreeIntoAvailable(PDPH_HEAP_ROOT DphRoot,
795                                  ULONG LeaveOnFreeList)
796 {
797     PDPH_HEAP_BLOCK Node = DphRoot->pFreeAllocationListHead, Next;
798     SIZE_T FreeAllocations = DphRoot->nFreeAllocations;
799 
800     /* Make sure requested size is not too big */
801     ASSERT(FreeAllocations >= LeaveOnFreeList);
802 
803     DPRINT("RtlpDphCoalesceFreeIntoAvailable(%p %lu)\n", DphRoot, LeaveOnFreeList);
804 
805     while (Node)
806     {
807         FreeAllocations--;
808         if (FreeAllocations < LeaveOnFreeList) break;
809 
810         /* Get the next pointer, because it may be changed after following two calls */
811         Next = Node->pNextAlloc;
812 
813         /* Remove it from the free list */
814         RtlpDphRemoveFromFreeList(DphRoot, Node, NULL);
815 
816         /* And put into the available */
817         RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
818 
819         /* Go to the next node */
820         Node = Next;
821     }
822 }
823 
824 VOID NTAPI
825 RtlpDphAddNewPool(PDPH_HEAP_ROOT DphRoot, PDPH_HEAP_BLOCK NodeBlock, PVOID Virtual, SIZE_T Size, BOOLEAN PlaceOnPool)
826 {
827     PDPH_HEAP_BLOCK DphNode, DphStartNode;
828     ULONG NodeCount, i;
829 
830     //NodeCount = (Size >> 6) - 1;
831     NodeCount = (ULONG)(Size / sizeof(DPH_HEAP_BLOCK));
832     DphStartNode = Virtual;
833 
834     /* Set pNextAlloc for all blocks */
835     for (DphNode = Virtual, i=NodeCount-1; i > 0; i--)
836     {
837         DphNode->pNextAlloc = DphNode + 1;
838         DphNode = DphNode->pNextAlloc;
839     }
840 
841     /* and the last one */
842     DphNode->pNextAlloc = NULL;
843 
844     /* Add it to the tail of unused node list */
845     if (DphRoot->pUnusedNodeListTail)
846         DphRoot->pUnusedNodeListTail->pNextAlloc = DphStartNode;
847     else
848         DphRoot->pUnusedNodeListHead = DphStartNode;
849 
850     DphRoot->pUnusedNodeListTail = DphNode;
851 
852     /* Increase counters */
853     DphRoot->nUnusedNodes += NodeCount;
854 
855     /* Check if we need to place it on the pool list */
856     if (PlaceOnPool)
857     {
858         /* Get a node from the unused list */
859         DphNode = RtlpDphTakeNodeFromUnusedList(DphRoot);
860         ASSERT(DphNode);
861 
862         /* Set its virtual block values */
863         DphNode->pVirtualBlock = Virtual;
864         DphNode->nVirtualBlockSize = Size;
865 
866         /* Place it on the pool list */
867         RtlpDphPlaceOnPoolList(DphRoot, DphNode);
868     }
869 }
870 
871 PDPH_HEAP_BLOCK NTAPI
872 RtlpDphSearchAvailableMemoryListForBestFit(PDPH_HEAP_ROOT DphRoot,
873                                            SIZE_T Size)
874 {
875     PLIST_ENTRY CurEntry;
876     PDPH_HEAP_BLOCK Node, NodeFound = NULL;
877 
878     CurEntry = DphRoot->AvailableAllocationHead.Flink;
879 
880     while (CurEntry != &DphRoot->AvailableAllocationHead)
881     {
882         /* Get the current available node */
883         Node = CONTAINING_RECORD(CurEntry, DPH_HEAP_BLOCK, AvailableEntry);
884 
885         /* Check its size */
886         if (Node->nVirtualBlockSize >= Size)
887         {
888             NodeFound = Node;
889             break;
890         }
891 
892         /* Move to the next available entry */
893         CurEntry = CurEntry->Flink;
894     }
895 
896     /* Make sure Adjacency list pointers are biased */
897     //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Flink));
898     //ASSERT(IS_BIASED_POINTER(Node->AdjacencyEntry.Blink));
899 
900     return NodeFound;
901 }
902 
903 PDPH_HEAP_BLOCK NTAPI
904 RtlpDphFindAvailableMemory(PDPH_HEAP_ROOT DphRoot,
905                            SIZE_T Size,
906                            BOOLEAN Grow)
907 {
908     PDPH_HEAP_BLOCK Node;
909     ULONG NewSize;
910 
911     /* Find an available best fitting node */
912     Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
913 
914     /* If that didn't work, try to search a smaller one in the loop */
915     while (!Node)
916     {
917         /* Break if the free list becomes too small */
918         if (DphRoot->nFreeAllocations <= DPH_FREE_LIST_MINIMUM) break;
919 
920         /* Calculate a new free list size */
921         NewSize = DphRoot->nFreeAllocations >> 2;
922         if (NewSize < DPH_FREE_LIST_MINIMUM) NewSize = DPH_FREE_LIST_MINIMUM;
923 
924         /* Coalesce free into available */
925         RtlpDphCoalesceFreeIntoAvailable(DphRoot, NewSize);
926 
927         /* Try to find an available best fitting node again */
928         Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
929     }
930 
931     /* If Node is NULL, then we could fix the situation only by
932        growing the available VM size */
933     if (!Node && Grow)
934     {
935         /* Grow VM size, if it fails - return failure directly */
936         if (!RtlpDphGrowVirtual(DphRoot, Size)) return NULL;
937 
938         /* Try to find an available best fitting node again */
939         Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
940 
941         if (!Node)
942         {
943             /* Do the last attempt: coalesce all free into available (if Size fits there) */
944             if (DphRoot->nFreeAllocationBytesCommitted + DphRoot->nAvailableAllocationBytesCommitted >= Size)
945             {
946                 /* Coalesce free into available */
947                 RtlpDphCoalesceFreeIntoAvailable(DphRoot, 0);
948 
949                 /* Try to find an available best fitting node again */
950                 Node = RtlpDphSearchAvailableMemoryListForBestFit(DphRoot, Size);
951             }
952         }
953     }
954 
955     /* Return node we found */
956     return Node;
957 }
958 
959 PDPH_HEAP_BLOCK NTAPI
960 RtlpDphFindBusyMemory(PDPH_HEAP_ROOT DphRoot,
961                       PVOID pUserMem)
962 {
963     PDPH_HEAP_BLOCK Node;
964     PVOID Ptr;
965 
966     /* Lookup busy block in AVL */
967     Ptr = RtlLookupElementGenericTableAvl(&DphRoot->BusyNodesTable, &pUserMem);
968     if (!Ptr) return NULL;
969 
970     /* Restore pointer to the heap block */
971     Node = CONTAINING_RECORD(Ptr, DPH_HEAP_BLOCK, pUserAllocation);
972     ASSERT(Node->pUserAllocation == pUserMem);
973     return Node;
974 }
975 
976 NTSTATUS NTAPI
977 RtlpDphSetProtectionBeforeUse(PDPH_HEAP_ROOT DphRoot, PUCHAR VirtualBlock, ULONG UserSize)
978 {
979     ULONG Protection;
980     PVOID Base;
981 
982     if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
983     {
984         Base = VirtualBlock + PAGE_SIZE;
985     }
986     else
987     {
988         Base = VirtualBlock;
989     }
990 
991     // FIXME: It should be different, but for now it's fine
992     Protection = PAGE_READWRITE;
993 
994     return RtlpDphProtectVm(Base, UserSize, Protection);
995 }
996 
997 NTSTATUS NTAPI
998 RtlpDphSetProtectionAfterUse(PDPH_HEAP_ROOT DphRoot, /*PUCHAR VirtualBlock*/PDPH_HEAP_BLOCK Node)
999 {
1000     ASSERT((Node->nVirtualAccessSize + PAGE_SIZE) <= Node->nVirtualBlockSize);
1001 
1002     // FIXME: Bring stuff here
1003     if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
1004     {
1005     }
1006     else
1007     {
1008     }
1009 
1010     return STATUS_SUCCESS;
1011 }
1012 
1013 PDPH_HEAP_BLOCK NTAPI
1014 RtlpDphAllocateNode(PDPH_HEAP_ROOT DphRoot)
1015 {
1016     PDPH_HEAP_BLOCK Node;
1017     NTSTATUS Status;
1018     SIZE_T Size = DPH_POOL_SIZE, SizeVirtual;
1019     PVOID Ptr = NULL;
1020 
1021     /* Check for the easy case */
1022     if (DphRoot->pUnusedNodeListHead)
1023     {
1024         /* Just take a node from this list */
1025         Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
1026         ASSERT(Node);
1027         return Node;
1028     }
1029 
1030     /* There is a need to make free space */
1031     Node = RtlpDphFindAvailableMemory(DphRoot, DPH_POOL_SIZE, FALSE);
1032 
1033     if (!DphRoot->pUnusedNodeListHead && !Node)
1034     {
1035         /* Retry with a smaller request */
1036         Size = PAGE_SIZE;
1037         Node = RtlpDphFindAvailableMemory(DphRoot, PAGE_SIZE, FALSE);
1038     }
1039 
1040     if (!DphRoot->pUnusedNodeListHead)
1041     {
1042         if (Node)
1043         {
1044             RtlpDphRemoveFromAvailableList(DphRoot, Node);
1045             Ptr = Node->pVirtualBlock;
1046             SizeVirtual = Node->nVirtualBlockSize;
1047         }
1048         else
1049         {
1050             /* No free space, need to alloc a new VM block */
1051             Size = DPH_POOL_SIZE;
1052             SizeVirtual = DPH_RESERVE_SIZE;
1053             Status = RtlpDphAllocateVm(&Ptr, SizeVirtual, MEM_COMMIT, PAGE_NOACCESS);
1054 
1055             if (!NT_SUCCESS(Status))
1056             {
1057                 /* Retry with a smaller size */
1058                 SizeVirtual = 0x10000;
1059                 Status = RtlpDphAllocateVm(&Ptr, SizeVirtual, MEM_COMMIT, PAGE_NOACCESS);
1060                 if (!NT_SUCCESS(Status)) return NULL;
1061             }
1062         }
1063 
1064         /* VM is allocated at this point, set protection */
1065         Status = RtlpDphProtectVm(Ptr, Size, PAGE_READWRITE);
1066         if (!NT_SUCCESS(Status))
1067         {
1068             if (Node)
1069             {
1070                 RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
1071             }
1072             else
1073             {
1074                 //RtlpDphFreeVm();
1075                 ASSERT(FALSE);
1076             }
1077 
1078             return NULL;
1079         }
1080 
1081         /* Zero the memory */
1082         if (Node) RtlZeroMemory(Ptr, Size);
1083 
1084         /* Add a new pool based on this VM */
1085         RtlpDphAddNewPool(DphRoot, Node, Ptr, Size, TRUE);
1086 
1087         if (Node)
1088         {
1089             if (Node->nVirtualBlockSize > Size)
1090             {
1091                 Node->pVirtualBlock += Size;
1092                 Node->nVirtualBlockSize -= Size;
1093 
1094                 RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
1095             }
1096             else
1097             {
1098                 RtlpDphReturnNodeToUnusedList(DphRoot, Node);
1099             }
1100         }
1101         else
1102         {
1103             /* The new VM block was just allocated a few code lines ago,
1104                so initialize it */
1105             Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
1106             Node->pVirtualBlock = Ptr;
1107             Node->nVirtualBlockSize = SizeVirtual;
1108             RtlpDphPlaceOnVirtualList(DphRoot, Node);
1109 
1110             Node = RtlpDphTakeNodeFromUnusedList(DphRoot);
1111             Node->pVirtualBlock = (PUCHAR)Ptr + Size;
1112             Node->nVirtualBlockSize = SizeVirtual - Size;
1113             RtlpDphPlaceOnVirtualList(DphRoot, Node);
1114 
1115             /* Coalesce them into available list */
1116             RtlpDphCoalesceNodeIntoAvailable(DphRoot, Node);
1117         }
1118     }
1119 
1120     return RtlpDphTakeNodeFromUnusedList(DphRoot);
1121 }
1122 
1123 BOOLEAN NTAPI
1124 RtlpDphGrowVirtual(PDPH_HEAP_ROOT DphRoot,
1125                    SIZE_T Size)
1126 {
1127     PDPH_HEAP_BLOCK Node, AvailableNode;
1128     PVOID Base = NULL;
1129     SIZE_T VirtualSize;
1130     NTSTATUS Status;
1131 
1132     /* Start with allocating a couple of nodes */
1133     Node = RtlpDphAllocateNode(DphRoot);
1134     if (!Node) return FALSE;
1135 
1136     AvailableNode = RtlpDphAllocateNode(DphRoot);
1137     if (!AvailableNode)
1138     {
1139         /* Free the allocated node and return failure */
1140         RtlpDphReturnNodeToUnusedList(DphRoot, Node);
1141         return FALSE;
1142     }
1143 
1144     /* Calculate size of VM to allocate by rounding it up */
1145     Size = ROUND_UP(Size, 0xFFFF);
1146     VirtualSize = Size;
1147     if (Size < DPH_RESERVE_SIZE)
1148         VirtualSize = DPH_RESERVE_SIZE;
1149 
1150     /* Allocate the virtual memory */
1151     // FIXME: Shouldn't it be MEM_RESERVE with later committing?
1152     Status = RtlpDphAllocateVm(&Base, VirtualSize, MEM_COMMIT, PAGE_NOACCESS);
1153     if (!NT_SUCCESS(Status))
1154     {
1155         /* Retry again with a smaller size */
1156         VirtualSize = Size;
1157         Status = RtlpDphAllocateVm(&Base, VirtualSize, MEM_COMMIT, PAGE_NOACCESS);
1158         if (!NT_SUCCESS(Status))
1159         {
1160             /* Free the allocated node and return failure */
1161             RtlpDphReturnNodeToUnusedList(DphRoot, Node);
1162             RtlpDphReturnNodeToUnusedList(DphRoot, AvailableNode);
1163             return FALSE;
1164         }
1165     }
1166 
1167     /* Set up our two nodes describing this VM */
1168     Node->pVirtualBlock = Base;
1169     Node->nVirtualBlockSize = VirtualSize;
1170     AvailableNode->pVirtualBlock = Base;
1171     AvailableNode->nVirtualBlockSize = VirtualSize;
1172 
1173     /* Add them to virtual and available lists respectively */
1174     RtlpDphPlaceOnVirtualList(DphRoot, Node);
1175     RtlpDphCoalesceNodeIntoAvailable(DphRoot, AvailableNode);
1176 
1177     /* Return success */
1178     return TRUE;
1179 }
1180 
1181 RTL_GENERIC_COMPARE_RESULTS
1182 NTAPI
1183 RtlpDphCompareNodeForTable(IN PRTL_AVL_TABLE Table,
1184                            IN PVOID FirstStruct,
1185                            IN PVOID SecondStruct)
1186 {
1187     ULONG_PTR FirstBlock, SecondBlock;
1188 
1189     FirstBlock = *((ULONG_PTR *)FirstStruct);
1190     SecondBlock = *((ULONG_PTR *)SecondStruct);
1191 
1192     if (FirstBlock < SecondBlock)
1193         return GenericLessThan;
1194     else if (FirstBlock > SecondBlock)
1195         return GenericGreaterThan;
1196 
1197     return GenericEqual;
1198 }
1199 
1200 PVOID
1201 NTAPI
1202 RtlpDphAllocateNodeForTable(IN PRTL_AVL_TABLE Table,
1203                             IN CLONG ByteSize)
1204 {
1205     PDPH_HEAP_BLOCK pBlock;
1206     PDPH_HEAP_ROOT DphRoot;
1207 
1208     /* This mega-assert comes from a text search over Windows 2003 checked binary of ntdll.dll */
1209     ASSERT((ULONG_PTR)(((PRTL_BALANCED_LINKS)0)+1) + sizeof(PUCHAR) == ByteSize);
1210 
1211     /* Get pointer to the containing heap root record */
1212     DphRoot = CONTAINING_RECORD(Table, DPH_HEAP_ROOT, BusyNodesTable);
1213     pBlock = DphRoot->NodeToAllocate;
1214 
1215     DphRoot->NodeToAllocate = NULL;
1216     ASSERT(pBlock);
1217 
1218     return &(pBlock->TableLinks);
1219 }
1220 
1221 VOID
1222 NTAPI
1223 RtlpDphFreeNodeForTable(IN PRTL_AVL_TABLE Table,
1224                         IN PVOID Buffer)
1225 {
1226     /* Nothing */
1227 }
1228 
1229 NTSTATUS NTAPI
1230 RtlpDphInitializeDelayedFreeQueue(VOID)
1231 {
1232     NTSTATUS Status;
1233 
1234     Status = RtlInitializeHeapLock(&RtlpDphDelayedFreeQueueLock);
1235     if (!NT_SUCCESS(Status))
1236     {
1237         // TODO: Log this error!
1238         DPRINT1("Failure initializing delayed free queue critical section\n");
1239         return Status;
1240     }
1241 
1242     /* Initialize lists */
1243     InitializeListHead(&RtlpDphDelayedFreeQueue);
1244     RtlInitializeSListHead(&RtlpDphDelayedTemporaryPushList);
1245 
1246     /* Reset counters */
1247     RtlpDphMemoryUsedByDelayedFreeBlocks = 0;
1248     RtlpDphNumberOfDelayedFreeBlocks = 0;
1249 
1250     return Status;
1251 }
1252 
1253 VOID NTAPI
1254 RtlpDphFreeDelayedBlocksFromHeap(PDPH_HEAP_ROOT DphRoot,
1255                                  PHEAP NormalHeap)
1256 {
1257     PLIST_ENTRY Current, Next;
1258     PDPH_BLOCK_INFORMATION BlockInfo;
1259     ULONG ValidationInfo;
1260 
1261     /* The original routine seems to use a temporary SList to put blocks to be freed,
1262        then it releases the lock and frees the blocks. But let's make it simple for now */
1263 
1264     /* Acquire the delayed free queue lock */
1265     RtlEnterHeapLock(RtlpDphDelayedFreeQueueLock, TRUE);
1266 
1267     /* Traverse the list */
1268     Current = RtlpDphDelayedFreeQueue.Flink;
1269     while (Current != &RtlpDphDelayedFreeQueue)
1270     {
1271         /* Get the next entry pointer */
1272         Next = Current->Flink;
1273 
1274         BlockInfo = CONTAINING_RECORD(Current, DPH_BLOCK_INFORMATION, FreeQueue);
1275 
1276         /* Check if it belongs to the same heap */
1277         if (BlockInfo->Heap == DphRoot)
1278         {
1279             /* Remove it from the list */
1280             RemoveEntryList(Current);
1281 
1282             /* Reset its heap to NULL */
1283             BlockInfo->Heap = NULL;
1284 
1285             if (!RtlpDphIsNormalFreeHeapBlock(BlockInfo + 1, &ValidationInfo, TRUE))
1286             {
1287                 RtlpDphReportCorruptedBlock(DphRoot, 10, BlockInfo + 1, ValidationInfo);
1288             }
1289 
1290             /* Decrement counters */
1291             RtlpDphMemoryUsedByDelayedFreeBlocks -= BlockInfo->ActualSize;
1292             RtlpDphNumberOfDelayedFreeBlocks--;
1293 
1294             /* Free the normal heap */
1295             RtlFreeHeap(NormalHeap, 0, BlockInfo);
1296         }
1297 
1298         /* Move to the next one */
1299         Current = Next;
1300     }
1301 
1302     /* Release the delayed free queue lock */
1303     RtlLeaveHeapLock(RtlpDphDelayedFreeQueueLock);
1304 }
1305 
1306 NTSTATUS NTAPI
1307 RtlpDphTargetDllsLogicInitialize(VOID)
1308 {
1309     UNIMPLEMENTED;
1310     return STATUS_SUCCESS;
1311 }
1312 
1313 VOID NTAPI
1314 RtlpDphInternalValidatePageHeap(PDPH_HEAP_ROOT DphRoot, PVOID Address, ULONG Value)
1315 {
1316     UNIMPLEMENTED;
1317 }
1318 
1319 VOID NTAPI
1320 RtlpDphVerifyIntegrity(PDPH_HEAP_ROOT DphRoot)
1321 {
1322     UNIMPLEMENTED;
1323 }
1324 
1325 VOID NTAPI
1326 RtlpDphReportCorruptedBlock(
1327     _In_ PDPH_HEAP_ROOT DphRoot,
1328     _In_ ULONG Reserved,
1329     _In_ PVOID Block,
1330     _In_ ULONG ValidationInfo)
1331 {
1332     PVOID Size = (PVOID)(ULONG_PTR)RtlpDphGetBlockSizeFromCorruptedBlock(Block);
1333     DPH_BLOCK_INFORMATION SafeInfo = {0};
1334 
1335     DPRINT1("Corrupted heap block %p\n", Block);
1336 
1337     _SEH2_TRY
1338     {
1339         PDPH_BLOCK_INFORMATION BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
1340         RtlCopyMemory(&SafeInfo, BlockInfo, sizeof(SafeInfo));
1341     }
1342     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1343     {
1344         DPRINT1("ERROR: Could not read DPH_BLOCK_INFORMATION\n");
1345         RtlZeroMemory(&SafeInfo, sizeof(SafeInfo));
1346     }
1347     _SEH2_END;
1348 
1349     if (ValidationInfo & DPH_VALINFO_CORRUPTED_AFTER_FREE)
1350     {
1351         RtlApplicationVerifierStop(
1352             APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_AFTER_FREE, "block corrupted after having been freed",
1353             RtlpDphHeapFromPointer(DphRoot), "Heap handle", Block, "Heap block", (PVOID)Size, "Block size", 0, "");
1354     }
1355 
1356     if (ValidationInfo & DPH_VALINFO_ALREADY_FREED)
1357     {
1358         RtlApplicationVerifierStop(
1359             APPLICATION_VERIFIER_DOUBLE_FREE, "block already freed", RtlpDphHeapFromPointer(DphRoot), "Heap handle",
1360             Block, "Heap block", Size, "Block size", 0, "");
1361     }
1362 
1363     if (ValidationInfo & DPH_VALINFO_BAD_INFIX_PATTERN)
1364     {
1365         RtlApplicationVerifierStop(
1366             APPLICATION_VERIFIER_CORRUPTED_INFIX_PATTERN, "corrupted infix pattern for freed block",
1367             RtlpDphHeapFromPointer(DphRoot), "Heap handle", Block, "Heap block", Size, "Block size", 0, "");
1368     }
1369 
1370     if (ValidationInfo & DPH_VALINFO_BAD_POINTER)
1371     {
1372         RtlApplicationVerifierStop(
1373             APPLICATION_VERIFIER_CORRUPT_HEAP_POINTER, "corrupted heap pointer or using wrong heap",
1374             RtlpDphHeapFromPointer(DphRoot), "Heap handle used", Block, "Heap block", Size, "Block size",
1375             SafeInfo.Heap, "Actual heap handle");
1376     }
1377 
1378     if (ValidationInfo & DPH_VALINFO_BAD_SUFFIX_PATTERN)
1379     {
1380         RtlApplicationVerifierStop(
1381             APPLICATION_VERIFIER_CORRUPTED_SUFFIX_PATTERN, "corrupted suffix pattern", RtlpDphHeapFromPointer(DphRoot),
1382             "Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
1383     }
1384 
1385     if (ValidationInfo & DPH_VALINFO_BAD_PREFIX_PATTERN)
1386     {
1387         RtlApplicationVerifierStop(
1388             APPLICATION_VERIFIER_CORRUPTED_PREFIX_PATTERN, "corrupted prefix pattern", RtlpDphHeapFromPointer(DphRoot),
1389             "Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
1390     }
1391 
1392     if (ValidationInfo & DPH_VALINFO_BAD_START_STAMP)
1393     {
1394         RtlApplicationVerifierStop(
1395             APPLICATION_VERIFIER_CORRUPTED_START_STAMP, "corrupted start stamp", RtlpDphHeapFromPointer(DphRoot),
1396             "Heap handle used", Block, "Heap block", Size, "Block size", (PVOID)(ULONG_PTR)SafeInfo.StartStamp,
1397             "Corrupted start stamp");
1398     }
1399 
1400     if (ValidationInfo & DPH_VALINFO_BAD_END_STAMP)
1401     {
1402         RtlApplicationVerifierStop(
1403             APPLICATION_VERIFIER_CORRUPTED_END_STAMP, "corrupted end stamp", RtlpDphHeapFromPointer(DphRoot),
1404             "Heap handle used", Block, "Heap block", Size, "Block size", (PVOID)(ULONG_PTR)SafeInfo.EndStamp,
1405             "Corrupted end stamp");
1406     }
1407 
1408     if (ValidationInfo & DPH_VALINFO_EXCEPTION)
1409     {
1410         RtlApplicationVerifierStop(
1411             APPLICATION_VERIFIER_EXCEPTION_WHILE_VERIFYING_BLOCK_HEADER, "exception raised while verifying block",
1412             RtlpDphHeapFromPointer(DphRoot), "Heap handle used", Block, "Heap block", Size, "Block size", 0, "");
1413     }
1414 }
1415 
1416 BOOLEAN NTAPI
1417 RtlpDphIsPageHeapBlock(PDPH_HEAP_ROOT DphRoot,
1418                        PVOID Block,
1419                        PULONG ValidationInformation,
1420                        BOOLEAN CheckFillers)
1421 {
1422     PDPH_BLOCK_INFORMATION BlockInfo;
1423     BOOLEAN SomethingWrong = FALSE;
1424     PUCHAR Byte, Start, End;
1425 
1426     ASSERT(ValidationInformation != NULL);
1427     *ValidationInformation = 0;
1428 
1429     // _SEH2_TRY {
1430     BlockInfo = (PDPH_BLOCK_INFORMATION)Block - 1;
1431 
1432     /* Check stamps */
1433     if (BlockInfo->StartStamp != DPH_FILL_START_STAMP_1)
1434     {
1435         *ValidationInformation |= DPH_VALINFO_BAD_START_STAMP;
1436         SomethingWrong = TRUE;
1437 
1438         /* Check if it has an alloc/free mismatch */
1439         if (BlockInfo->StartStamp == DPH_FILL_START_STAMP_2)
1440         {
1441             /* Notify respectively */
1442             *ValidationInformation = 0x101;
1443         }
1444     }
1445 
1446     if (BlockInfo->EndStamp != DPH_FILL_END_STAMP_1)
1447     {
1448         *ValidationInformation |= DPH_VALINFO_BAD_END_STAMP;
1449         SomethingWrong = TRUE;
1450     }
1451 
1452     /* Check root heap pointer */
1453     if (BlockInfo->Heap != DphRoot)
1454     {
1455         *ValidationInformation |= DPH_VALINFO_BAD_POINTER;
1456         SomethingWrong = TRUE;
1457     }
1458 
1459     /* Check other fillers if requested */
1460     if (CheckFillers)
1461     {
1462         /* Check space after the block */
1463         Start = (PUCHAR)Block + BlockInfo->RequestedSize;
1464         End = (PUCHAR)ROUND_UP(Start, PAGE_SIZE);
1465         for (Byte = Start; Byte < End; Byte++)
1466         {
1467             if (*Byte != DPH_FILL_SUFFIX)
1468             {
1469                 *ValidationInformation |= DPH_VALINFO_BAD_SUFFIX_PATTERN;
1470                 SomethingWrong = TRUE;
1471                 break;
1472             }
1473         }
1474     }
1475 
1476     return (SomethingWrong == FALSE);
1477 }
1478 
1479 BOOLEAN NTAPI
1480 RtlpDphIsNormalFreeHeapBlock(PVOID Block,
1481                              PULONG ValidationInformation,
1482                              BOOLEAN CheckFillers)
1483 {
1484     ASSERT(ValidationInformation != NULL);
1485 
1486     UNIMPLEMENTED;
1487     *ValidationInformation = 0;
1488     return TRUE;
1489 }
1490 
1491 NTSTATUS NTAPI
1492 RtlpDphProcessStartupInitialization(VOID)
1493 {
1494     NTSTATUS Status;
1495     PTEB Teb = NtCurrentTeb();
1496 
1497     /* Initialize the DPH heap list and its critical section */
1498     InitializeListHead(&RtlpDphPageHeapList);
1499     Status = RtlInitializeHeapLock(&RtlpDphPageHeapListLock);
1500     if (!NT_SUCCESS(Status))
1501     {
1502         ASSERT(FALSE);
1503         return Status;
1504     }
1505 
1506     /* Initialize delayed-free queue */
1507     Status = RtlpDphInitializeDelayedFreeQueue();
1508     if (!NT_SUCCESS(Status)) return Status;
1509 
1510     /* Initialize the target dlls string */
1511     RtlInitUnicodeString(&RtlpDphTargetDllsUnicode, RtlpDphTargetDlls);
1512     Status = RtlpDphTargetDllsLogicInitialize();
1513 
1514     /* Per-process DPH init is done */
1515     RtlpDphPageHeapListInitialized = TRUE;
1516 
1517     DPRINT1("Page heap: pid 0x%p: page heap enabled with flags 0x%X.\n",
1518             Teb->ClientId.UniqueProcess, RtlpDphGlobalFlags);
1519 
1520     return Status;
1521 }
1522 
1523 BOOLEAN NTAPI
1524 RtlpDphShouldAllocateInPageHeap(PDPH_HEAP_ROOT DphRoot,
1525                                 SIZE_T Size)
1526 {
1527     //UNIMPLEMENTED;
1528     /* Always use page heap for now */
1529     return TRUE;
1530 }
1531 
1532 HANDLE NTAPI
1533 RtlpPageHeapCreate(ULONG Flags,
1534                    PVOID Addr,
1535                    SIZE_T TotalSize,
1536                    SIZE_T CommitSize,
1537                    PVOID Lock,
1538                    PRTL_HEAP_PARAMETERS Parameters)
1539 {
1540     PVOID Base = NULL;
1541     PHEAP HeapPtr;
1542     PDPH_HEAP_ROOT DphRoot;
1543     PDPH_HEAP_BLOCK DphNode;
1544     ULONG MemSize;
1545     NTSTATUS Status;
1546     LARGE_INTEGER PerfCounter;
1547 
1548     /* Check for a DPH bypass flag */
1549     if ((ULONG_PTR)Parameters == -1) return NULL;
1550 
1551     /* Make sure no user-allocated stuff was provided */
1552     if (Addr || Lock) return NULL;
1553 
1554     /* Allocate minimum amount of virtual memory */
1555     MemSize = DPH_RESERVE_SIZE;
1556     Status = RtlpDphAllocateVm(&Base, MemSize, MEM_COMMIT, PAGE_NOACCESS);
1557     if (!NT_SUCCESS(Status))
1558     {
1559         ASSERT(FALSE);
1560         return NULL;
1561     }
1562 
1563     /* Set protection */
1564     Status = RtlpDphProtectVm(Base, 2*PAGE_SIZE + DPH_POOL_SIZE, PAGE_READWRITE);
1565     if (!NT_SUCCESS(Status))
1566     {
1567         //RtlpDphFreeVm(Base, 0, 0, 0);
1568         ASSERT(FALSE);
1569         return NULL;
1570     }
1571 
1572     /* Start preparing the 1st page. Fill it with the default filler */
1573     RtlFillMemoryUlong(Base, PAGE_SIZE, DPH_FILL);
1574 
1575     /* Set flags in the "HEAP" structure */
1576     HeapPtr = (PHEAP)Base;
1577     HeapPtr->Flags = Flags | HEAP_FLAG_PAGE_ALLOCS;
1578     HeapPtr->ForceFlags = Flags | HEAP_FLAG_PAGE_ALLOCS;
1579 
1580     /* Set 1st page to read only now */
1581     Status = RtlpDphProtectVm(Base, PAGE_SIZE, PAGE_READONLY);
1582     if (!NT_SUCCESS(Status))
1583     {
1584         ASSERT(FALSE);
1585         return NULL;
1586     }
1587 
1588     /* 2nd page is the real DPH root block */
1589     DphRoot = (PDPH_HEAP_ROOT)((PCHAR)Base + PAGE_SIZE);
1590 
1591     /* Initialize the DPH root */
1592     DphRoot->Signature = DPH_SIGNATURE;
1593     DphRoot->HeapFlags = Flags;
1594     DphRoot->HeapCritSect = (PHEAP_LOCK)((PCHAR)DphRoot + DPH_POOL_SIZE);
1595     DphRoot->ExtraFlags = RtlpDphGlobalFlags;
1596 
1597     ZwQueryPerformanceCounter(&PerfCounter, NULL);
1598     DphRoot->Seed = PerfCounter.LowPart;
1599 
1600     RtlInitializeHeapLock(&DphRoot->HeapCritSect);
1601     InitializeListHead(&DphRoot->AvailableAllocationHead);
1602 
1603     /* Create a normal heap for this paged heap */
1604     DphRoot->NormalHeap = RtlCreateHeap(Flags, NULL, TotalSize, CommitSize, NULL, (PRTL_HEAP_PARAMETERS)-1);
1605     if (!DphRoot->NormalHeap)
1606     {
1607         ASSERT(FALSE);
1608         return NULL;
1609     }
1610 
1611     /* 3rd page: a pool for DPH allocations */
1612     RtlpDphAddNewPool(DphRoot, NULL, DphRoot + 1, DPH_POOL_SIZE - sizeof(DPH_HEAP_ROOT), FALSE);
1613 
1614     /* Allocate internal heap blocks. For the root */
1615     DphNode = RtlpDphAllocateNode(DphRoot);
1616     ASSERT(DphNode != NULL);
1617     DphNode->pVirtualBlock = (PUCHAR)DphRoot;
1618     DphNode->nVirtualBlockSize = DPH_POOL_SIZE;
1619     RtlpDphPlaceOnPoolList(DphRoot, DphNode);
1620 
1621     /* For the memory we allocated as a whole */
1622     DphNode = RtlpDphAllocateNode(DphRoot);
1623     ASSERT(DphNode != NULL);
1624     DphNode->pVirtualBlock = Base;
1625     DphNode->nVirtualBlockSize = MemSize;
1626     RtlpDphPlaceOnVirtualList(DphRoot, DphNode);
1627 
1628     /* For the remaining part */
1629     DphNode = RtlpDphAllocateNode(DphRoot);
1630     ASSERT(DphNode != NULL);
1631     DphNode->pVirtualBlock = (PUCHAR)Base + 2*PAGE_SIZE + DPH_POOL_SIZE;
1632     DphNode->nVirtualBlockSize = MemSize - (2*PAGE_SIZE + DPH_POOL_SIZE);
1633     RtlpDphCoalesceNodeIntoAvailable(DphRoot, DphNode);
1634 
1635     //DphRoot->CreateStackTrace = RtlpDphLogStackTrace(1);
1636 
1637     /* Initialize AVL-based busy nodes table */
1638     RtlInitializeGenericTableAvl(&DphRoot->BusyNodesTable,
1639                                  RtlpDphCompareNodeForTable,
1640                                  RtlpDphAllocateNodeForTable,
1641                                  RtlpDphFreeNodeForTable,
1642                                  NULL);
1643 
1644     /* Initialize per-process startup info */
1645     if (!RtlpDphPageHeapListInitialized) RtlpDphProcessStartupInitialization();
1646 
1647     /* Acquire the heap list lock */
1648     RtlEnterHeapLock(RtlpDphPageHeapListLock, TRUE);
1649 
1650     /* Insert this heap to the tail of the global list */
1651     InsertTailList(&RtlpDphPageHeapList, &DphRoot->NextHeap);
1652 
1653     /* Note we increased the size of the list */
1654     RtlpDphPageHeapListLength++;
1655 
1656     /* Release the heap list lock */
1657     RtlLeaveHeapLock(RtlpDphPageHeapListLock);
1658 
1659     if (RtlpDphDebugOptions & DPH_DEBUG_VERBOSE)
1660     {
1661         DPRINT1("Page heap: process 0x%p created heap @ %p (%p, flags 0x%X)\n",
1662                 NtCurrentTeb()->ClientId.UniqueProcess, (PUCHAR)DphRoot - PAGE_SIZE,
1663                 DphRoot->NormalHeap, DphRoot->ExtraFlags);
1664     }
1665 
1666     /* Perform internal validation if required */
1667     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
1668         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
1669 
1670     return (PUCHAR)DphRoot - PAGE_SIZE;
1671 }
1672 
1673 PVOID NTAPI
1674 RtlpPageHeapDestroy(HANDLE HeapPtr)
1675 {
1676     PDPH_HEAP_ROOT DphRoot;
1677     PVOID Ptr;
1678     PDPH_HEAP_BLOCK Node, Next;
1679     PHEAP NormalHeap;
1680     ULONG Value;
1681 
1682     /* Check if it's not a process heap */
1683     if (HeapPtr == RtlGetProcessHeap())
1684     {
1685         DbgBreakPoint();
1686         return NULL;
1687     }
1688 
1689     /* Get pointer to the heap root */
1690     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
1691     if (!DphRoot) return NULL;
1692 
1693     RtlpDphPreProcessing(DphRoot, DphRoot->HeapFlags);
1694 
1695     /* Get the pointer to the normal heap */
1696     NormalHeap = DphRoot->NormalHeap;
1697 
1698     /* Free the delayed-free blocks */
1699     RtlpDphFreeDelayedBlocksFromHeap(DphRoot, NormalHeap);
1700 
1701     /* Go through the busy blocks */
1702     Ptr = RtlEnumerateGenericTableAvl(&DphRoot->BusyNodesTable, TRUE);
1703 
1704     while (Ptr)
1705     {
1706         Node = CONTAINING_RECORD(Ptr, DPH_HEAP_BLOCK, pUserAllocation);
1707         if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
1708         {
1709             if (!RtlpDphIsPageHeapBlock(DphRoot, Node->pUserAllocation, &Value, TRUE))
1710             {
1711                 RtlpDphReportCorruptedBlock(DphRoot, 3, Node->pUserAllocation, Value);
1712             }
1713         }
1714 
1715         /* FIXME: Call AV notification */
1716         //AVrfInternalHeapFreeNotification();
1717 
1718         /* Go to the next node */
1719         Ptr = RtlEnumerateGenericTableAvl(&DphRoot->BusyNodesTable, FALSE);
1720     }
1721 
1722     /* Acquire the global heap list lock */
1723     RtlEnterHeapLock(RtlpDphPageHeapListLock, TRUE);
1724 
1725     /* Remove the entry and decrement the global counter */
1726     RemoveEntryList(&DphRoot->NextHeap);
1727     RtlpDphPageHeapListLength--;
1728 
1729     /* Release the global heap list lock */
1730     RtlLeaveHeapLock(RtlpDphPageHeapListLock);
1731 
1732     /* Leave and delete this heap's critical section */
1733     RtlLeaveHeapLock(DphRoot->HeapCritSect);
1734     RtlDeleteHeapLock(DphRoot->HeapCritSect);
1735 
1736     /* Now go through all virtual list nodes and release the VM */
1737     Node = DphRoot->pVirtualStorageListHead;
1738     while (Node)
1739     {
1740         Next = Node->pNextAlloc;
1741         /* Release the memory without checking result */
1742         RtlpDphFreeVm(Node->pVirtualBlock, 0, MEM_RELEASE);
1743         Node = Next;
1744     }
1745 
1746     /* Destroy the normal heap */
1747     RtlDestroyHeap(NormalHeap);
1748 
1749     /* Report success */
1750     if (RtlpDphDebugOptions & DPH_DEBUG_VERBOSE)
1751         DPRINT1("Page heap: process 0x%p destroyed heap @ %p (%p)\n",
1752                 NtCurrentTeb()->ClientId.UniqueProcess, HeapPtr, NormalHeap);
1753 
1754     return NULL;
1755 }
1756 
1757 PVOID NTAPI
1758 RtlpPageHeapAllocate(IN PVOID HeapPtr,
1759                      IN ULONG Flags,
1760                      IN SIZE_T Size)
1761 {
1762     PDPH_HEAP_ROOT DphRoot;
1763     PDPH_HEAP_BLOCK AvailableNode, BusyNode;
1764     BOOLEAN Biased = FALSE;
1765     ULONG AllocateSize, AccessSize;
1766     NTSTATUS Status;
1767     SIZE_T UserActualSize;
1768     PVOID Ptr;
1769 
1770     /* Check requested size */
1771     if (Size > 0x7FF00000)
1772     {
1773         DPRINT1("extreme size request\n");
1774 
1775         /* Generate an exception if needed */
1776         if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
1777 
1778         return NULL;
1779     }
1780 
1781     /* Unbias the pointer if necessary */
1782     if (IS_BIASED_POINTER(HeapPtr))
1783     {
1784         HeapPtr = (PVOID)POINTER_REMOVE_BIAS(HeapPtr);
1785         Biased = TRUE;
1786     }
1787 
1788     /* Get a pointer to the heap root */
1789     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
1790     if (!DphRoot) return NULL;
1791 
1792     /* Acquire the heap lock */
1793     RtlpDphPreProcessing(DphRoot, Flags);
1794 
1795     /* Perform internal validation if specified by flags */
1796     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
1797     {
1798         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
1799     }
1800 
1801     /* Add heap flags */
1802     Flags |= DphRoot->HeapFlags;
1803 
1804     if (!Biased && !RtlpDphShouldAllocateInPageHeap(DphRoot, Size))
1805     {
1806         /* Perform allocation from a normal heap */
1807         ASSERT(FALSE);
1808     }
1809 
1810     /* Perform heap integrity check if specified by flags */
1811     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
1812     {
1813         RtlpDphVerifyIntegrity(DphRoot);
1814     }
1815 
1816     /* Calculate sizes */
1817     AccessSize = ROUND_UP(Size + sizeof(DPH_BLOCK_INFORMATION), PAGE_SIZE);
1818     AllocateSize = AccessSize + PAGE_SIZE;
1819 
1820     // FIXME: Move RtlpDphAllocateNode(DphRoot) to this place
1821     AvailableNode = RtlpDphFindAvailableMemory(DphRoot, AllocateSize, TRUE);
1822     if (!AvailableNode)
1823     {
1824         DPRINT1("Page heap: Unable to allocate virtual memory\n");
1825         DbgBreakPoint();
1826 
1827         /* Release the lock */
1828         RtlpDphPostProcessing(DphRoot);
1829 
1830         return NULL;
1831     }
1832     ASSERT(AvailableNode->nVirtualBlockSize >= AllocateSize);
1833 
1834     /* Set protection */
1835     Status = RtlpDphSetProtectionBeforeUse(DphRoot,
1836                                            AvailableNode->pVirtualBlock,
1837                                            AccessSize);
1838     if (!NT_SUCCESS(Status))
1839     {
1840         ASSERT(FALSE);
1841     }
1842 
1843     /* Save available node pointer */
1844     Ptr = AvailableNode->pVirtualBlock;
1845 
1846     /* Check node's size */
1847     if (AvailableNode->nVirtualBlockSize > AllocateSize)
1848     {
1849         /* The block contains too much free space, reduce it */
1850         AvailableNode->pVirtualBlock += AllocateSize;
1851         AvailableNode->nVirtualBlockSize -= AllocateSize;
1852         DphRoot->nAvailableAllocationBytesCommitted -= AllocateSize;
1853 
1854         /* Allocate a new node which will be our busy node */
1855         BusyNode = RtlpDphAllocateNode(DphRoot);
1856         ASSERT(BusyNode != NULL);
1857         BusyNode->pVirtualBlock = Ptr;
1858         BusyNode->nVirtualBlockSize = AllocateSize;
1859     }
1860     else
1861     {
1862         /* The block's size fits exactly */
1863         RtlpDphRemoveFromAvailableList(DphRoot, AvailableNode);
1864         BusyNode = AvailableNode;
1865     }
1866 
1867     /* Calculate actual user size  */
1868     if (DphRoot->HeapFlags & HEAP_NO_ALIGNMENT)
1869         UserActualSize = Size;
1870     else
1871         UserActualSize = ROUND_UP(Size, 8);
1872 
1873     /* Set up the block */
1874     BusyNode->nVirtualAccessSize = AccessSize;
1875     BusyNode->nUserActualSize = UserActualSize;
1876     BusyNode->nUserRequestedSize = Size;
1877 
1878     if (DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN)
1879         BusyNode->pUserAllocation = BusyNode->pVirtualBlock + PAGE_SIZE;
1880     else
1881         BusyNode->pUserAllocation = BusyNode->pVirtualBlock + BusyNode->nVirtualAccessSize - UserActualSize;
1882 
1883     BusyNode->UserValue = NULL;
1884     BusyNode->UserFlags = Flags & HEAP_SETTABLE_USER_FLAGS;
1885 
1886     // FIXME: Don't forget about stack traces if such flag was set
1887     BusyNode->StackTrace = NULL;
1888 
1889     /* Place it on busy list */
1890     RtlpDphPlaceOnBusyList(DphRoot, BusyNode);
1891 
1892     /* Zero or patter-fill memory depending on flags */
1893     if (Flags & HEAP_ZERO_MEMORY)
1894         RtlZeroMemory(BusyNode->pUserAllocation, Size);
1895     else
1896         RtlFillMemory(BusyNode->pUserAllocation, Size, DPH_FILL_INFIX);
1897 
1898     /* Write DPH info */
1899     if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
1900     {
1901         RtlpDphWritePageHeapBlockInformation(DphRoot,
1902                                              BusyNode->pUserAllocation,
1903                                              Size,
1904                                              AccessSize);
1905     }
1906 
1907     /* Finally allocation is done, perform validation again if required */
1908     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
1909     {
1910         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
1911     }
1912 
1913     /* Release the lock */
1914     RtlpDphPostProcessing(DphRoot);
1915 
1916     DPRINT("Allocated user block pointer: %p\n", BusyNode->pUserAllocation);
1917 
1918     /* Return pointer to user allocation */
1919     return BusyNode->pUserAllocation;
1920 }
1921 
1922 BOOLEAN NTAPI
1923 RtlpPageHeapFree(HANDLE HeapPtr,
1924                  ULONG Flags,
1925                  PVOID Ptr)
1926 {
1927     PDPH_HEAP_ROOT DphRoot;
1928     PDPH_HEAP_BLOCK Node;
1929     ULONG ValidationInfo;
1930     PDPH_BLOCK_INFORMATION Info;
1931 
1932     /* Check for a NULL pointer freeing */
1933     if (!Ptr)
1934     {
1935         if (RtlpDphBreakOptions & DPH_BREAK_ON_NULL_FREE)
1936         {
1937             DPRINT1("Page heap: freeing a null pointer\n");
1938             DbgBreakPoint();
1939         }
1940         return TRUE;
1941     }
1942 
1943     /* Get a pointer to the heap root */
1944     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
1945     if (!DphRoot) return FALSE;
1946 
1947     /* Acquire the heap lock */
1948     RtlpDphPreProcessing(DphRoot, Flags);
1949 
1950     /* Perform internal validation if specified by flags */
1951     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
1952         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
1953 
1954     /* Add heap flags */
1955     Flags |= DphRoot->HeapFlags;
1956 
1957     /* Find busy memory */
1958     Node = RtlpDphFindBusyMemory(DphRoot, Ptr);
1959 
1960     if (!Node)
1961     {
1962         /* This block was not found in page heap, try a normal heap instead */
1963         //RtlpDphNormalHeapFree();
1964         ASSERT(FALSE);
1965     }
1966 
1967     if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
1968     {
1969         /* Check and report corrupted block */
1970         if (!RtlpDphIsPageHeapBlock(DphRoot, Ptr, &ValidationInfo, TRUE))
1971         {
1972             RtlpDphReportCorruptedBlock(DphRoot, 1, Ptr, ValidationInfo);
1973         }
1974 
1975         // FIXME: Should go inside RtlpDphSetProtectionAfterUse
1976         if (Node->nVirtualAccessSize != 0)
1977         {
1978             /* Set stamps */
1979             Info = (PDPH_BLOCK_INFORMATION)Node->pUserAllocation - 1;
1980             Info->StartStamp = DPH_FILL_START_STAMP_2;
1981             Info->EndStamp = DPH_FILL_END_STAMP_2;
1982 
1983             RtlpDphProtectVm(Node->pVirtualBlock, Node->nVirtualAccessSize, PAGE_NOACCESS);
1984         }
1985     }
1986     else
1987     {
1988         // FIXME: Should go inside RtlpDphSetProtectionAfterUse
1989         if (Node->nVirtualAccessSize != 0)
1990             RtlpDphProtectVm(Node->pVirtualBlock + PAGE_SIZE, Node->nVirtualAccessSize, PAGE_NOACCESS);
1991     }
1992 
1993     /* Set new protection */
1994     //RtlpDphSetProtectionAfterUse(DphRoot, Node);
1995 
1996     /* Remove it from the list of busy nodes */
1997     RtlpDphRemoveFromBusyList(DphRoot, Node);
1998 
1999     /* And put it into the list of free nodes */
2000     RtlpDphPlaceOnFreeList(DphRoot, Node);
2001 
2002     //if (DphRoot->ExtraFlags & DPH_EXTRA_LOG_STACK_TRACES)
2003     //    Node->StackTrace = RtlpDphLogStackTrace(3);
2004     //else
2005         Node->StackTrace = NULL;
2006 
2007     /* Leave the heap lock */
2008     RtlpDphPostProcessing(DphRoot);
2009 
2010     /* Return success */
2011     return TRUE;
2012 }
2013 
2014 PVOID NTAPI
2015 RtlpPageHeapReAllocate(HANDLE HeapPtr,
2016                        ULONG Flags,
2017                        PVOID Ptr,
2018                        SIZE_T Size)
2019 {
2020     PDPH_HEAP_ROOT DphRoot;
2021     PDPH_HEAP_BLOCK Node = NULL, AllocatedNode;
2022     BOOLEAN Biased = FALSE, UseNormalHeap = FALSE, OldBlockPageHeap = TRUE;
2023     ULONG ValidationInfo;
2024     SIZE_T DataSize;
2025     PVOID NewAlloc = NULL;
2026 
2027     /* Check requested size */
2028     if (Size > 0x7FF00000)
2029     {
2030         DPRINT1("extreme size request\n");
2031 
2032         /* Generate an exception if needed */
2033         if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
2034 
2035         return NULL;
2036     }
2037 
2038     /* Unbias the pointer if necessary */
2039     if (IS_BIASED_POINTER(HeapPtr))
2040     {
2041         HeapPtr = (PVOID)POINTER_REMOVE_BIAS(HeapPtr);
2042         Biased = TRUE;
2043     }
2044 
2045     /* Get a pointer to the heap root */
2046     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
2047     if (!DphRoot) return NULL;
2048 
2049     /* Acquire the heap lock */
2050     RtlpDphPreProcessing(DphRoot, Flags);
2051 
2052     /* Perform internal validation if specified by flags */
2053     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
2054     {
2055         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
2056     }
2057 
2058     /* Add heap flags */
2059     Flags |= DphRoot->HeapFlags;
2060 
2061     /* Exit with NULL right away if inplace is specified */
2062     if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
2063     {
2064         /* Release the lock */
2065         RtlpDphPostProcessing(DphRoot);
2066 
2067         /* Generate an exception if needed */
2068         if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
2069 
2070         return NULL;
2071     }
2072 
2073     /* Try to get node of the allocated block */
2074     AllocatedNode = RtlpDphFindBusyMemory(DphRoot, Ptr);
2075 
2076     if (!AllocatedNode)
2077     {
2078         /* This block was not found in page heap, try a normal heap instead */
2079         //RtlpDphNormalHeapFree();
2080         ASSERT(FALSE);
2081         OldBlockPageHeap = FALSE;
2082     }
2083 
2084     /* Check the block */
2085     if (!(DphRoot->ExtraFlags & DPH_EXTRA_CHECK_UNDERRUN))
2086     {
2087         if (!RtlpDphIsPageHeapBlock(DphRoot, AllocatedNode->pUserAllocation, &ValidationInfo, TRUE))
2088         {
2089             RtlpDphReportCorruptedBlock(DphRoot, 3, AllocatedNode->pUserAllocation, ValidationInfo);
2090         }
2091     }
2092 
2093     /* Remove old one from the busy list */
2094     RtlpDphRemoveFromBusyList(DphRoot, AllocatedNode);
2095 
2096     if (!Biased && !RtlpDphShouldAllocateInPageHeap(DphRoot, Size))
2097     {
2098         // FIXME: Use normal heap
2099         ASSERT(FALSE);
2100         UseNormalHeap = TRUE;
2101     }
2102     else
2103     {
2104         /* Now do a trick: bias the pointer and call our allocate routine */
2105         NewAlloc = RtlpPageHeapAllocate((PVOID)POINTER_ADD_BIAS(HeapPtr), Flags, Size);
2106     }
2107 
2108     if (!NewAlloc)
2109     {
2110         /* New allocation failed, put the block back (if it was found in page heap) */
2111         RtlpDphPlaceOnBusyList(DphRoot, AllocatedNode);
2112 
2113         /* Release the lock */
2114         RtlpDphPostProcessing(DphRoot);
2115 
2116         /* Perform validation again if required */
2117         if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE)
2118         {
2119             RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
2120         }
2121 
2122         /* Generate an exception if needed */
2123         if (Flags & HEAP_GENERATE_EXCEPTIONS) RtlpDphRaiseException(STATUS_NO_MEMORY);
2124 
2125         return NULL;
2126     }
2127 
2128     /* Copy contents of the old block */
2129     if (AllocatedNode->nUserRequestedSize > Size)
2130         DataSize = Size;
2131     else
2132         DataSize = AllocatedNode->nUserRequestedSize;
2133 
2134     if (DataSize != 0) RtlCopyMemory(NewAlloc, Ptr, DataSize);
2135 
2136     /* Copy user flags and values */
2137     if (!UseNormalHeap)
2138     {
2139         /* Get the node of the new block */
2140         Node = RtlpDphFindBusyMemory(DphRoot, NewAlloc);
2141         ASSERT(Node != NULL);
2142 
2143         /* Set its values/flags */
2144         Node->UserValue = AllocatedNode->UserValue;
2145         if (Flags & HEAP_SETTABLE_USER_FLAGS)
2146             Node->UserFlags = Flags & HEAP_SETTABLE_USER_FLAGS;
2147         else
2148             Node->UserFlags = AllocatedNode->UserFlags;
2149     }
2150 
2151     if (!OldBlockPageHeap)
2152     {
2153         /* Weird scenario, investigate */
2154         ASSERT(FALSE);
2155     }
2156 
2157     /* Mark the old block as no access */
2158     if (AllocatedNode->nVirtualAccessSize != 0)
2159     {
2160         RtlpDphProtectVm(AllocatedNode->pVirtualBlock, AllocatedNode->nVirtualAccessSize, PAGE_NOACCESS);
2161     }
2162 
2163     /* And place it on the free list */
2164     RtlpDphPlaceOnFreeList(DphRoot, AllocatedNode);
2165 
2166     // FIXME: Capture stack traces if needed
2167     AllocatedNode->StackTrace = NULL;
2168 
2169     /* Finally allocation is done, perform validation again if required */
2170     if (RtlpDphDebugOptions & DPH_DEBUG_INTERNAL_VALIDATE && !Biased)
2171     {
2172         RtlpDphInternalValidatePageHeap(DphRoot, NULL, 0);
2173     }
2174 
2175     /* Release the lock */
2176     RtlpDphPostProcessing(DphRoot);
2177 
2178     DPRINT("Allocated new user block pointer: %p\n", NewAlloc);
2179 
2180     /* Return pointer to user allocation */
2181     return NewAlloc;
2182 }
2183 
2184 BOOLEAN NTAPI
2185 RtlpPageHeapGetUserInfo(PVOID HeapHandle,
2186                         ULONG Flags,
2187                         PVOID BaseAddress,
2188                         PVOID *UserValue,
2189                         PULONG UserFlags)
2190 {
2191     PDPH_HEAP_ROOT DphRoot;
2192     PDPH_HEAP_BLOCK Node;
2193 
2194     /* Get a pointer to the heap root */
2195     DphRoot = RtlpDphPointerFromHandle(HeapHandle);
2196     if (!DphRoot) return FALSE;
2197 
2198     /* Add heap flags */
2199     Flags |= DphRoot->HeapFlags;
2200 
2201     /* Acquire the heap lock */
2202     RtlpDphPreProcessing(DphRoot, Flags);
2203 
2204     /* Find busy memory */
2205     Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
2206 
2207     if (!Node)
2208     {
2209         /* This block was not found in page heap, try a normal heap instead */
2210         //RtlpDphNormalHeapGetUserInfo();
2211         ASSERT(FALSE);
2212         return FALSE;
2213     }
2214 
2215     /* Get user values and flags and store them in user provided pointers */
2216     if (UserValue) *UserValue = Node->UserValue;
2217     if (UserFlags) *UserFlags = Node->UserFlags;
2218 
2219     /* Leave the heap lock */
2220     RtlpDphPostProcessing(DphRoot);
2221 
2222     /* Return success */
2223     return TRUE;
2224 }
2225 
2226 BOOLEAN NTAPI
2227 RtlpPageHeapSetUserValue(PVOID HeapHandle,
2228                          ULONG Flags,
2229                          PVOID BaseAddress,
2230                          PVOID UserValue)
2231 {
2232     PDPH_HEAP_ROOT DphRoot;
2233     PDPH_HEAP_BLOCK Node;
2234 
2235     /* Get a pointer to the heap root */
2236     DphRoot = RtlpDphPointerFromHandle(HeapHandle);
2237     if (!DphRoot) return FALSE;
2238 
2239     /* Add heap flags */
2240     Flags |= DphRoot->HeapFlags;
2241 
2242     /* Acquire the heap lock */
2243     RtlpDphPreProcessing(DphRoot, Flags);
2244 
2245     /* Find busy memory */
2246     Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
2247 
2248     if (!Node)
2249     {
2250         /* This block was not found in page heap, try a normal heap instead */
2251         //RtlpDphNormalHeapSetUserValue();
2252         ASSERT(FALSE);
2253         return FALSE;
2254     }
2255 
2256     /* Get user values and flags and store them in user provided pointers */
2257     Node->UserValue = UserValue;
2258 
2259     /* Leave the heap lock */
2260     RtlpDphPostProcessing(DphRoot);
2261 
2262     /* Return success */
2263     return TRUE;
2264 }
2265 
2266 BOOLEAN
2267 NTAPI
2268 RtlpPageHeapSetUserFlags(PVOID HeapHandle,
2269                          ULONG Flags,
2270                          PVOID BaseAddress,
2271                          ULONG UserFlagsReset,
2272                          ULONG UserFlagsSet)
2273 {
2274     PDPH_HEAP_ROOT DphRoot;
2275     PDPH_HEAP_BLOCK Node;
2276 
2277     /* Get a pointer to the heap root */
2278     DphRoot = RtlpDphPointerFromHandle(HeapHandle);
2279     if (!DphRoot) return FALSE;
2280 
2281     /* Add heap flags */
2282     Flags |= DphRoot->HeapFlags;
2283 
2284     /* Acquire the heap lock */
2285     RtlpDphPreProcessing(DphRoot, Flags);
2286 
2287     /* Find busy memory */
2288     Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
2289 
2290     if (!Node)
2291     {
2292         /* This block was not found in page heap, try a normal heap instead */
2293         //RtlpDphNormalHeapSetUserFlags();
2294         ASSERT(FALSE);
2295         return FALSE;
2296     }
2297 
2298     /* Get user values and flags and store them in user provided pointers */
2299     Node->UserFlags &= ~(UserFlagsReset);
2300     Node->UserFlags |= UserFlagsSet;
2301 
2302     /* Leave the heap lock */
2303     RtlpDphPostProcessing(DphRoot);
2304 
2305     /* Return success */
2306     return TRUE;
2307 }
2308 
2309 SIZE_T NTAPI
2310 RtlpPageHeapSize(HANDLE HeapHandle,
2311                  ULONG Flags,
2312                  PVOID BaseAddress)
2313 {
2314     PDPH_HEAP_ROOT DphRoot;
2315     PDPH_HEAP_BLOCK Node;
2316     SIZE_T Size;
2317 
2318     /* Get a pointer to the heap root */
2319     DphRoot = RtlpDphPointerFromHandle(HeapHandle);
2320     if (!DphRoot) return -1;
2321 
2322     /* Add heap flags */
2323     Flags |= DphRoot->HeapFlags;
2324 
2325     /* Acquire the heap lock */
2326     RtlpDphPreProcessing(DphRoot, Flags);
2327 
2328     /* Find busy memory */
2329     Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
2330 
2331     if (!Node)
2332     {
2333         /* This block was not found in page heap, try a normal heap instead */
2334         //RtlpDphNormalHeapSize();
2335         ASSERT(FALSE);
2336         return -1;
2337     }
2338 
2339     /* Get heap block size */
2340     Size = Node->nUserRequestedSize;
2341 
2342     /* Leave the heap lock */
2343     RtlpDphPostProcessing(DphRoot);
2344 
2345     /* Return user requested size */
2346     return Size;
2347 }
2348 
2349 BOOLEAN
2350 NTAPI
2351 RtlpDebugPageHeapValidate(PVOID HeapHandle,
2352                           ULONG Flags,
2353                           PVOID BaseAddress)
2354 {
2355     PDPH_HEAP_ROOT DphRoot;
2356     PDPH_HEAP_BLOCK Node = NULL;
2357     BOOLEAN Valid = FALSE;
2358 
2359     /* Get a pointer to the heap root */
2360     DphRoot = RtlpDphPointerFromHandle(HeapHandle);
2361     if (!DphRoot) return -1;
2362 
2363     /* Add heap flags */
2364     Flags |= DphRoot->HeapFlags;
2365 
2366     /* Acquire the heap lock */
2367     RtlpDphPreProcessing(DphRoot, Flags);
2368 
2369     /* Find busy memory */
2370     if (BaseAddress)
2371         Node = RtlpDphFindBusyMemory(DphRoot, BaseAddress);
2372 
2373     if (!Node)
2374     {
2375         /* This block was not found in page heap, or the request is to validate all normal heap */
2376         Valid = RtlpDphNormalHeapValidate(DphRoot, Flags, BaseAddress);
2377     }
2378 
2379     /* Leave the heap lock */
2380     RtlpDphPostProcessing(DphRoot);
2381 
2382     /* Return result of a normal heap validation */
2383     if (BaseAddress && !Node)
2384         return Valid;
2385 
2386     /* Otherwise return our own result */
2387     if (!BaseAddress || Node) Valid = TRUE;
2388 
2389     return Valid;
2390 }
2391 
2392 BOOLEAN
2393 NTAPI
2394 RtlpDphNormalHeapValidate(PDPH_HEAP_ROOT DphRoot,
2395                           ULONG Flags,
2396                           PVOID BaseAddress)
2397 {
2398     PDPH_BLOCK_INFORMATION BlockInfo = (PDPH_BLOCK_INFORMATION)BaseAddress - 1;
2399     if (!BaseAddress)
2400     {
2401         /* Validate all normal heap */
2402         return RtlValidateHeap(DphRoot->NormalHeap, Flags, NULL);
2403     }
2404 
2405     // FIXME: Check is this a normal heap block
2406     /*if (!RtlpDphIsNormalHeapBlock(DphRoot, BaseAddress, &ValidationInfo))
2407     {
2408     }*/
2409 
2410     return RtlValidateHeap(DphRoot->NormalHeap, Flags, BlockInfo);
2411 }
2412 
2413 BOOLEAN
2414 NTAPI
2415 RtlpPageHeapLock(HANDLE HeapPtr)
2416 {
2417     PDPH_HEAP_ROOT DphRoot;
2418 
2419     /* Get pointer to the heap root */
2420     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
2421     if (!DphRoot) return FALSE;
2422 
2423     RtlpDphEnterCriticalSection(DphRoot, DphRoot->HeapFlags);
2424     return TRUE;
2425 }
2426 
2427 BOOLEAN
2428 NTAPI
2429 RtlpPageHeapUnlock(HANDLE HeapPtr)
2430 {
2431     PDPH_HEAP_ROOT DphRoot;
2432 
2433     /* Get pointer to the heap root */
2434     DphRoot = RtlpDphPointerFromHandle(HeapPtr);
2435     if (!DphRoot) return FALSE;
2436 
2437     RtlpDphLeaveCriticalSection(DphRoot);
2438     return TRUE;
2439 }
2440 
2441 /* EOF */
2442