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