xref: /reactos/sdk/lib/rtl/heapdbg.c (revision 845faec4)
1 /*
2  * PROJECT:         ReactOS Runtime Library
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            lib/rtl/heapdbg.c
5  * PURPOSE:         Heap manager debug heap
6  * PROGRAMMERS:     Copyright 2010 Aleksey Bragin
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <rtl.h>
12 #include <heap.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* FUNCTIONS ******************************************************************/
18 
19 HANDLE NTAPI
20 RtlDebugCreateHeap(ULONG Flags,
21                    PVOID Addr,
22                    SIZE_T ReserveSize,
23                    SIZE_T CommitSize,
24                    PVOID Lock,
25                    PRTL_HEAP_PARAMETERS Parameters)
26 {
27     MEMORY_BASIC_INFORMATION MemoryInfo;
28     NTSTATUS Status;
29     PHEAP Heap;
30 
31     /* Validate parameters */
32     if (ReserveSize <= HEAP_ENTRY_SIZE)
33     {
34         DPRINT1("HEAP: Incorrect ReserveSize %x\n", ReserveSize);
35         return NULL;
36     }
37 
38     if (ReserveSize < CommitSize)
39     {
40         DPRINT1("HEAP: Incorrect CommitSize %x\n", CommitSize);
41         return NULL;
42     }
43 
44     if (Flags & HEAP_NO_SERIALIZE && Lock)
45     {
46         DPRINT1("HEAP: Can't specify Lock routine and have HEAP_NO_SERIALIZE flag set\n");
47         return NULL;
48     }
49 
50     /* If the address is specified, check it's virtual memory */
51     if (Addr)
52     {
53         Status = ZwQueryVirtualMemory(NtCurrentProcess(),
54                                       Addr,
55                                       MemoryBasicInformation,
56                                       &MemoryInfo,
57                                       sizeof(MemoryInfo),
58                                       NULL);
59 
60         if (!NT_SUCCESS(Status))
61         {
62             DPRINT1("HEAP: Specified heap base address %p is invalid, Status 0x%08X\n", Addr, Status);
63             return NULL;
64         }
65 
66         if (MemoryInfo.BaseAddress != Addr)
67         {
68             DPRINT1("HEAP: Specified heap base address %p is not really a base one %p\n", Addr, MemoryInfo.BaseAddress);
69             return NULL;
70         }
71 
72         if (MemoryInfo.State == MEM_FREE)
73         {
74             DPRINT1("HEAP: Specified heap base address %p is free\n", Addr);
75             return NULL;
76         }
77     }
78 
79     /* All validation performed, now call the real routine with skip validation check flag */
80     Flags |= HEAP_SKIP_VALIDATION_CHECKS |
81              HEAP_TAIL_CHECKING_ENABLED |
82              HEAP_FREE_CHECKING_ENABLED;
83 
84     Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters);
85     if (!Heap) return NULL;
86 
87     // FIXME: Capture stack backtrace
88 
89     RtlpValidateHeapHeaders(Heap, TRUE);
90 
91     return Heap;
92 }
93 
94 BOOLEAN NTAPI
95 RtlDebugDestroyHeap(HANDLE HeapPtr)
96 {
97     SIZE_T Size = 0;
98     PHEAP Heap = (PHEAP)HeapPtr;
99 
100     if (Heap == RtlGetCurrentPeb()->ProcessHeap)
101     {
102         DPRINT1("HEAP: It's forbidden delete process heap!");
103         return FALSE;
104     }
105 
106     if (Heap->Signature != HEAP_SIGNATURE)
107     {
108         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
109         return FALSE;
110     }
111 
112     if (!RtlpValidateHeap(Heap, FALSE)) return FALSE;
113 
114     /* Make heap invalid by zeroing its signature */
115     Heap->Signature = 0;
116 
117     /* Free validate headers copy if it was existing */
118     if (Heap->HeaderValidateCopy)
119     {
120         ZwFreeVirtualMemory(NtCurrentProcess(),
121                             &Heap->HeaderValidateCopy,
122                             &Size,
123                             MEM_RELEASE);
124     }
125 
126     return TRUE;
127 }
128 
129 PVOID NTAPI
130 RtlDebugAllocateHeap(PVOID HeapPtr,
131                      ULONG Flags,
132                      SIZE_T Size)
133 {
134     PHEAP Heap = (PHEAP)HeapPtr;
135     SIZE_T AllocSize = 1;
136     BOOLEAN HeapLocked = FALSE;
137     PVOID Result;
138 
139     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
140         return RtlpPageHeapAllocate(HeapPtr, Flags, Size);
141 
142     if (Heap->Signature != HEAP_SIGNATURE)
143     {
144         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
145         return NULL;
146     }
147 
148     /* Add settable user value flag */
149     Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
150 
151     /* Calculate size */
152     if (Size) AllocSize = Size;
153     AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA);
154 
155     /* Check if size didn't exceed max one */
156     if (AllocSize < Size ||
157         AllocSize > Heap->MaximumAllocationSize)
158     {
159         DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize);
160         return NULL;
161     }
162 
163     /* Lock the heap ourselves */
164     if (!(Flags & HEAP_NO_SERIALIZE))
165     {
166         RtlEnterHeapLock(Heap->LockVariable, TRUE);
167         HeapLocked = TRUE;
168 
169         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
170         Flags |= HEAP_NO_SERIALIZE;
171     }
172 
173     /* Validate the heap if necessary */
174     RtlpValidateHeap(Heap, FALSE);
175 
176     /* Call main routine to do the stuff */
177     Result = RtlAllocateHeap(HeapPtr, Flags, Size);
178 
179     /* Validate heap headers */
180     RtlpValidateHeapHeaders(Heap, TRUE);
181 
182     if (Result)
183     {
184         if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)
185             RtlpValidateHeap(Heap, FALSE);
186     }
187 
188     /* Release the lock */
189     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
190 
191     return Result;
192 }
193 
194 PVOID NTAPI
195 RtlDebugReAllocateHeap(HANDLE HeapPtr,
196                        ULONG Flags,
197                        PVOID Ptr,
198                        SIZE_T Size)
199 {
200     PHEAP Heap = (PHEAP)HeapPtr;
201     SIZE_T AllocSize = 1;
202     BOOLEAN HeapLocked = FALSE;
203     PVOID Result = NULL;
204     PHEAP_ENTRY HeapEntry;
205 
206     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
207         return RtlpPageHeapReAllocate(HeapPtr, Flags, Ptr, Size);
208 
209     if (Heap->Signature != HEAP_SIGNATURE)
210     {
211         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
212         return NULL;
213     }
214 
215     /* Add settable user value flag */
216     Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
217 
218     /* Calculate size */
219     if (Size) AllocSize = Size;
220     AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA);
221 
222     /* Check if size didn't exceed max one */
223     if (AllocSize < Size ||
224         AllocSize > Heap->MaximumAllocationSize)
225     {
226         DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize);
227         return NULL;
228     }
229 
230     /* Lock the heap ourselves */
231     if (!(Flags & HEAP_NO_SERIALIZE))
232     {
233         RtlEnterHeapLock(Heap->LockVariable, TRUE);
234         HeapLocked = TRUE;
235 
236         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
237         Flags |= HEAP_NO_SERIALIZE;
238     }
239 
240     /* Validate the heap if necessary */
241     RtlpValidateHeap(Heap, FALSE);
242 
243     /* Get the existing heap entry */
244     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
245 
246     /* Validate it */
247     if (RtlpValidateHeapEntry(Heap, HeapEntry))
248     {
249         /* Call main routine to do the stuff */
250         Result = RtlReAllocateHeap(HeapPtr, Flags, Ptr, Size);
251 
252         if (Result)
253         {
254             /* Validate heap headers and then heap itself */
255             RtlpValidateHeapHeaders(Heap, TRUE);
256             RtlpValidateHeap(Heap, FALSE);
257         }
258     }
259 
260     /* Release the lock */
261     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
262 
263     return Result;
264 }
265 
266 BOOLEAN NTAPI
267 RtlDebugFreeHeap(HANDLE HeapPtr,
268                  ULONG Flags,
269                  PVOID Ptr)
270 {
271     PHEAP Heap = (PHEAP)HeapPtr;
272     BOOLEAN HeapLocked = FALSE;
273     PHEAP_ENTRY HeapEntry;
274     BOOLEAN Result = FALSE;
275 
276     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
277         return RtlpPageHeapFree(HeapPtr, Flags, Ptr);
278 
279     if (Heap->Signature != HEAP_SIGNATURE)
280     {
281         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
282         return FALSE;
283     }
284 
285     /* Add skip validation flag */
286     Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
287 
288     /* Lock the heap ourselves */
289     if (!(Flags & HEAP_NO_SERIALIZE))
290     {
291         RtlEnterHeapLock(Heap->LockVariable, TRUE);
292         HeapLocked = TRUE;
293 
294         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
295         Flags |= HEAP_NO_SERIALIZE;
296     }
297 
298     /* Validate the heap if necessary */
299     RtlpValidateHeap(Heap, FALSE);
300 
301     /* Get the existing heap entry */
302     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
303 
304     /* Validate it */
305     if (RtlpValidateHeapEntry(Heap, HeapEntry))
306     {
307         /* If it succeeded - call the main routine */
308         Result = RtlFreeHeap(HeapPtr, Flags, Ptr);
309 
310         /* Validate heap headers and then heap itself */
311         RtlpValidateHeapHeaders(Heap, TRUE);
312         RtlpValidateHeap(Heap, FALSE);
313     }
314 
315     /* Release the lock */
316     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
317 
318     return Result;
319 }
320 
321 BOOLEAN NTAPI
322 RtlDebugGetUserInfoHeap(PVOID HeapHandle,
323                         ULONG Flags,
324                         PVOID BaseAddress,
325                         PVOID *UserValue,
326                         PULONG UserFlags)
327 {
328     PHEAP Heap = (PHEAP)HeapHandle;
329     BOOLEAN HeapLocked = FALSE;
330     PHEAP_ENTRY HeapEntry;
331     BOOLEAN Result = FALSE;
332 
333     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
334         return RtlpPageHeapGetUserInfo(HeapHandle, Flags, BaseAddress, UserValue, UserFlags);
335 
336     if (Heap->Signature != HEAP_SIGNATURE)
337     {
338         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
339         return FALSE;
340     }
341 
342     /* Add skip validation flag */
343     Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
344 
345     /* Lock the heap ourselves */
346     if (!(Flags & HEAP_NO_SERIALIZE))
347     {
348         RtlEnterHeapLock(Heap->LockVariable, TRUE);
349         HeapLocked = TRUE;
350 
351         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
352         Flags |= HEAP_NO_SERIALIZE;
353     }
354 
355     /* Validate the heap if necessary */
356     RtlpValidateHeap(Heap, FALSE);
357 
358     /* Get the existing heap entry */
359     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
360 
361     /* Validate it */
362     if (RtlpValidateHeapEntry(Heap, HeapEntry))
363     {
364         /* If it succeeded - call the main routine */
365         Result = RtlGetUserInfoHeap(HeapHandle, Flags, BaseAddress, UserValue, UserFlags);
366     }
367 
368     /* Release the lock */
369     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
370 
371     return Result;
372 }
373 
374 BOOLEAN NTAPI
375 RtlDebugSetUserValueHeap(PVOID HeapHandle,
376                          ULONG Flags,
377                          PVOID BaseAddress,
378                          PVOID UserValue)
379 {
380     PHEAP Heap = (PHEAP)HeapHandle;
381     BOOLEAN HeapLocked = FALSE;
382     PHEAP_ENTRY HeapEntry;
383     BOOLEAN Result = FALSE;
384 
385     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
386         return RtlpPageHeapSetUserValue(HeapHandle, Flags, BaseAddress, UserValue);
387 
388     if (Heap->Signature != HEAP_SIGNATURE)
389     {
390         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
391         return FALSE;
392     }
393 
394     /* Add skip validation flag */
395     Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
396 
397     /* Lock the heap ourselves */
398     if (!(Flags & HEAP_NO_SERIALIZE))
399     {
400         RtlEnterHeapLock(Heap->LockVariable, TRUE);
401         HeapLocked = TRUE;
402 
403         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
404         Flags |= HEAP_NO_SERIALIZE;
405     }
406 
407     /* Validate the heap if necessary */
408     RtlpValidateHeap(Heap, FALSE);
409 
410     /* Get the existing heap entry */
411     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
412 
413     /* Validate it */
414     if (RtlpValidateHeapEntry(Heap, HeapEntry))
415     {
416         /* If it succeeded - call the main routine */
417         Result = RtlSetUserValueHeap(HeapHandle, Flags, BaseAddress, UserValue);
418 
419         /* Validate the heap */
420         RtlpValidateHeap(Heap, FALSE);
421     }
422 
423     /* Release the lock */
424     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
425 
426     return Result;
427 }
428 
429 BOOLEAN
430 NTAPI
431 RtlDebugSetUserFlagsHeap(PVOID HeapHandle,
432                          ULONG Flags,
433                          PVOID BaseAddress,
434                          ULONG UserFlagsReset,
435                          ULONG UserFlagsSet)
436 {
437     PHEAP Heap = (PHEAP)HeapHandle;
438     BOOLEAN HeapLocked = FALSE;
439     PHEAP_ENTRY HeapEntry;
440     BOOLEAN Result = FALSE;
441 
442     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
443         return RtlpPageHeapSetUserFlags(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
444 
445     /* Check if this heap allows flags to be set at all */
446     if (UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS ||
447         UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS)
448     {
449         return FALSE;
450     }
451 
452     if (Heap->Signature != HEAP_SIGNATURE)
453     {
454         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
455         return FALSE;
456     }
457 
458     /* Add skip validation flag */
459     Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
460 
461     /* Lock the heap ourselves */
462     if (!(Flags & HEAP_NO_SERIALIZE))
463     {
464         RtlEnterHeapLock(Heap->LockVariable, TRUE);
465         HeapLocked = TRUE;
466 
467         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
468         Flags |= HEAP_NO_SERIALIZE;
469     }
470 
471     /* Validate the heap if necessary */
472     RtlpValidateHeap(Heap, FALSE);
473 
474     /* Get the existing heap entry */
475     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
476 
477     /* Validate it */
478     if (RtlpValidateHeapEntry(Heap, HeapEntry))
479     {
480         /* If it succeeded - call the main routine */
481         Result = RtlSetUserFlagsHeap(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
482 
483         /* Validate the heap */
484         RtlpValidateHeap(Heap, FALSE);
485     }
486 
487     /* Release the lock */
488     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
489 
490     return Result;
491 }
492 
493 SIZE_T NTAPI
494 RtlDebugSizeHeap(HANDLE HeapPtr,
495                  ULONG Flags,
496                  PVOID Ptr)
497 {
498     PHEAP Heap = (PHEAP)HeapPtr;
499     BOOLEAN HeapLocked = FALSE;
500     PHEAP_ENTRY HeapEntry;
501     SIZE_T Result = ~(SIZE_T)0;
502 
503     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
504         return RtlpPageHeapSize(HeapPtr, Flags, Ptr);
505 
506     /* Check heap signature */
507     if (Heap->Signature != HEAP_SIGNATURE)
508     {
509         DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
510         return FALSE;
511     }
512 
513     /* Add skip validation flag */
514     Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
515 
516     /* Lock the heap ourselves */
517     if (!(Flags & HEAP_NO_SERIALIZE))
518     {
519         RtlEnterHeapLock(Heap->LockVariable, TRUE);
520         HeapLocked = TRUE;
521 
522         /* Add no serialize flag so that the main routine won't try to acquire the lock again */
523         Flags |= HEAP_NO_SERIALIZE;
524     }
525 
526     /* Validate the heap if necessary */
527     RtlpValidateHeap(Heap, FALSE);
528 
529     /* Get the existing heap entry */
530     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
531 
532     /* Validate it */
533     if (RtlpValidateHeapEntry(Heap, HeapEntry))
534     {
535         /* If it succeeded - call the main routine */
536         Result = RtlSizeHeap(HeapPtr, Flags, Ptr);
537     }
538 
539     /* Release the lock */
540     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
541 
542     return Result;
543 }
544 
545 /* EOF */