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
RtlpDphRaiseException(NTSTATUS Status)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
RtlpDphPointerFromHandle(PVOID Handle)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
RtlpDphHeapFromPointer(PDPH_HEAP_ROOT DphHeap)240 RtlpDphHeapFromPointer(PDPH_HEAP_ROOT DphHeap)
241 {
242 return ((PUCHAR)DphHeap) - PAGE_SIZE;
243 }
244
245 ULONG NTAPI
RtlpDphGetBlockSizeFromCorruptedBlock(PVOID Block)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
RtlpDphEnterCriticalSection(PDPH_HEAP_ROOT DphRoot,ULONG Flags)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
RtlpDphLeaveCriticalSection(PDPH_HEAP_ROOT DphRoot)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
RtlpDphPreProcessing(PDPH_HEAP_ROOT DphRoot,ULONG Flags)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
RtlpDphPostProcessing(PDPH_HEAP_ROOT DphRoot)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
RtlpSecMemFreeVirtualMemory(HANDLE Process,PVOID * Base,PSIZE_T Size,ULONG Type)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
RtlpDphAllocateVm(PVOID * Base,SIZE_T Size,ULONG Type,ULONG Protection)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
RtlpDphFreeVm(PVOID Base,SIZE_T Size,ULONG Type)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
RtlpDphProtectVm(PVOID Base,SIZE_T Size,ULONG Protection)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
RtlpDphWritePageHeapBlockInformation(PDPH_HEAP_ROOT DphRoot,PVOID UserAllocation,SIZE_T Size,SIZE_T UserSize)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
RtlpDphPlaceOnBusyList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK DphNode)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
RtlpDphPlaceOnFreeList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphPlaceOnPoolList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphPlaceOnVirtualList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphTakeNodeFromUnusedList(PDPH_HEAP_ROOT DphRoot)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
RtlpDphReturnNodeToUnusedList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphRemoveFromAvailableList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphRemoveFromBusyList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphRemoveFromFreeList(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node,PDPH_HEAP_BLOCK Prev)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
RtlpDphCoalesceNodeIntoAvailable(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphCoalesceFreeIntoAvailable(PDPH_HEAP_ROOT DphRoot,ULONG LeaveOnFreeList)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
RtlpDphAddNewPool(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK NodeBlock,PVOID Virtual,SIZE_T Size,BOOLEAN PlaceOnPool)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
RtlpDphSearchAvailableMemoryListForBestFit(PDPH_HEAP_ROOT DphRoot,SIZE_T Size)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
RtlpDphFindAvailableMemory(PDPH_HEAP_ROOT DphRoot,SIZE_T Size,BOOLEAN Grow)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
RtlpDphFindBusyMemory(PDPH_HEAP_ROOT DphRoot,PVOID pUserMem)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
RtlpDphSetProtectionBeforeUse(PDPH_HEAP_ROOT DphRoot,PUCHAR VirtualBlock,ULONG UserSize)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
RtlpDphSetProtectionAfterUse(PDPH_HEAP_ROOT DphRoot,PDPH_HEAP_BLOCK Node)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
RtlpDphAllocateNode(PDPH_HEAP_ROOT DphRoot)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
RtlpDphGrowVirtual(PDPH_HEAP_ROOT DphRoot,SIZE_T Size)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
RtlpDphCompareNodeForTable(IN PRTL_AVL_TABLE Table,IN PVOID FirstStruct,IN PVOID SecondStruct)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
RtlpDphAllocateNodeForTable(IN PRTL_AVL_TABLE Table,IN CLONG ByteSize)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
RtlpDphFreeNodeForTable(IN PRTL_AVL_TABLE Table,IN PVOID Buffer)1223 RtlpDphFreeNodeForTable(IN PRTL_AVL_TABLE Table,
1224 IN PVOID Buffer)
1225 {
1226 /* Nothing */
1227 }
1228
1229 NTSTATUS NTAPI
RtlpDphInitializeDelayedFreeQueue(VOID)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
RtlpDphFreeDelayedBlocksFromHeap(PDPH_HEAP_ROOT DphRoot,PHEAP NormalHeap)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
RtlpDphTargetDllsLogicInitialize(VOID)1307 RtlpDphTargetDllsLogicInitialize(VOID)
1308 {
1309 UNIMPLEMENTED;
1310 return STATUS_SUCCESS;
1311 }
1312
1313 VOID NTAPI
RtlpDphInternalValidatePageHeap(PDPH_HEAP_ROOT DphRoot,PVOID Address,ULONG Value)1314 RtlpDphInternalValidatePageHeap(PDPH_HEAP_ROOT DphRoot, PVOID Address, ULONG Value)
1315 {
1316 UNIMPLEMENTED;
1317 }
1318
1319 VOID NTAPI
RtlpDphVerifyIntegrity(PDPH_HEAP_ROOT DphRoot)1320 RtlpDphVerifyIntegrity(PDPH_HEAP_ROOT DphRoot)
1321 {
1322 UNIMPLEMENTED;
1323 }
1324
1325 VOID NTAPI
RtlpDphReportCorruptedBlock(_In_ PDPH_HEAP_ROOT DphRoot,_In_ ULONG Reserved,_In_ PVOID Block,_In_ ULONG ValidationInfo)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
RtlpDphIsPageHeapBlock(PDPH_HEAP_ROOT DphRoot,PVOID Block,PULONG ValidationInformation,BOOLEAN CheckFillers)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
RtlpDphIsNormalFreeHeapBlock(PVOID Block,PULONG ValidationInformation,BOOLEAN CheckFillers)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
RtlpDphProcessStartupInitialization(VOID)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
RtlpDphShouldAllocateInPageHeap(PDPH_HEAP_ROOT DphRoot,SIZE_T Size)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
RtlpPageHeapCreate(ULONG Flags,PVOID Addr,SIZE_T TotalSize,SIZE_T CommitSize,PVOID Lock,PRTL_HEAP_PARAMETERS Parameters)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
RtlpPageHeapDestroy(HANDLE HeapPtr)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
RtlpPageHeapAllocate(IN PVOID HeapPtr,IN ULONG Flags,IN SIZE_T Size)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
RtlpPageHeapFree(HANDLE HeapPtr,ULONG Flags,PVOID Ptr)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
RtlpPageHeapReAllocate(HANDLE HeapPtr,ULONG Flags,PVOID Ptr,SIZE_T Size)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
RtlpPageHeapGetUserInfo(PVOID HeapHandle,ULONG Flags,PVOID BaseAddress,PVOID * UserValue,PULONG UserFlags)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
RtlpPageHeapSetUserValue(PVOID HeapHandle,ULONG Flags,PVOID BaseAddress,PVOID UserValue)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
RtlpPageHeapSetUserFlags(PVOID HeapHandle,ULONG Flags,PVOID BaseAddress,ULONG UserFlagsReset,ULONG UserFlagsSet)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
RtlpPageHeapSize(HANDLE HeapHandle,ULONG Flags,PVOID BaseAddress)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
RtlpDebugPageHeapValidate(PVOID HeapHandle,ULONG Flags,PVOID BaseAddress)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
RtlpDphNormalHeapValidate(PDPH_HEAP_ROOT DphRoot,ULONG Flags,PVOID BaseAddress)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
RtlpPageHeapLock(HANDLE HeapPtr)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
RtlpPageHeapUnlock(HANDLE HeapPtr)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