reactos/lib/rtl/heapdbg.c

545 lines
15 KiB
C
Raw Normal View History

[RTL/HEAP] - First commit of a heap manager rewrite. It introduces a totally new heap manager, with the following features: * Actually resembles real NT's heap manager; * Based on data structures similar to Windows 2003 and Vista+'s heap structures; * Supporting advanced heap flags (e.g. useful for debugging); * Substantially lower fragmentation rates (and thus speed and reliability) than the existing Wine's implementation. It's going to be further enhanced by adding a frontend allocator (for example, as lookaside lists, or as a Low Fragmentation Heap alike frontend in Vista+ systems); * Real support for user-defined flags and native support for user-defined values; * Native support for a custom commit routine, which is very important for trunk's win32 subsystem; * Reserving, committing, decommitting and freeing on the fly, unlike existing heap manager which prefers to reserve and commit as much as possible, and doesn't decommit when it's no longer necessary; * Support for per process heaps, with a proper lock; * Reserved support for a special so-called debug heap allocator (to be implemented in heapdbg.c) which will be useful for finding heap corruptions. The committed code is a result of a month of work, and is a heavy work-in-progress one. It already implements the bare minimum required to boot to 3rd stage and run FireFox 3, however many rare codepaths are not implemented yet and there is some maintenance work to do (e.g. move structures and defines to a standalone header file). A list of used references is stated in the header of a source file for now. svn path=/trunk/; revision=49007
2010-10-05 21:43:48 +00:00
/*
* PROJECT: ReactOS Runtime Library
* LICENSE: GPL - See COPYING in the top level directory
* FILE: lib/rtl/heapdbg.c
* PURPOSE: Heap manager debug heap
* PROGRAMMERS: Copyright 2010 Aleksey Bragin
*/
/* INCLUDES ******************************************************************/
#include <rtl.h>
#include <heap.h>
#define NDEBUG
#include <debug.h>
[RTL/HEAP] - First commit of a heap manager rewrite. It introduces a totally new heap manager, with the following features: * Actually resembles real NT's heap manager; * Based on data structures similar to Windows 2003 and Vista+'s heap structures; * Supporting advanced heap flags (e.g. useful for debugging); * Substantially lower fragmentation rates (and thus speed and reliability) than the existing Wine's implementation. It's going to be further enhanced by adding a frontend allocator (for example, as lookaside lists, or as a Low Fragmentation Heap alike frontend in Vista+ systems); * Real support for user-defined flags and native support for user-defined values; * Native support for a custom commit routine, which is very important for trunk's win32 subsystem; * Reserving, committing, decommitting and freeing on the fly, unlike existing heap manager which prefers to reserve and commit as much as possible, and doesn't decommit when it's no longer necessary; * Support for per process heaps, with a proper lock; * Reserved support for a special so-called debug heap allocator (to be implemented in heapdbg.c) which will be useful for finding heap corruptions. The committed code is a result of a month of work, and is a heavy work-in-progress one. It already implements the bare minimum required to boot to 3rd stage and run FireFox 3, however many rare codepaths are not implemented yet and there is some maintenance work to do (e.g. move structures and defines to a standalone header file). A list of used references is stated in the header of a source file for now. svn path=/trunk/; revision=49007
2010-10-05 21:43:48 +00:00
/* FUNCTIONS ******************************************************************/
HANDLE NTAPI
RtlDebugCreateHeap(ULONG Flags,
PVOID Addr,
SIZE_T ReserveSize,
SIZE_T CommitSize,
PVOID Lock,
PRTL_HEAP_PARAMETERS Parameters)
{
MEMORY_BASIC_INFORMATION MemoryInfo;
NTSTATUS Status;
PHEAP Heap;
/* Validate parameters */
if (ReserveSize <= HEAP_ENTRY_SIZE)
{
DPRINT1("HEAP: Incorrect ReserveSize %x\n", ReserveSize);
return NULL;
}
if (ReserveSize < CommitSize)
{
DPRINT1("HEAP: Incorrect CommitSize %x\n", CommitSize);
return NULL;
}
if (Flags & HEAP_NO_SERIALIZE && Lock)
{
DPRINT1("HEAP: Can't specify Lock routine and have HEAP_NO_SERIALIZE flag set\n");
return NULL;
}
/* If the address is specified, check it's virtual memory */
if (Addr)
{
Status = ZwQueryVirtualMemory(NtCurrentProcess(),
Addr,
MemoryBasicInformation,
&MemoryInfo,
sizeof(MemoryInfo),
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("HEAP: Specified heap base address %p is invalid, Status 0x%08X\n", Addr, Status);
return NULL;
}
if (MemoryInfo.BaseAddress != Addr)
{
DPRINT1("HEAP: Specified heap base address %p is not really a base one %p\n", Addr, MemoryInfo.BaseAddress);
return NULL;
}
if (MemoryInfo.State == MEM_FREE)
{
DPRINT1("HEAP: Specified heap base address %p is free\n", Addr);
return NULL;
}
}
/* All validation performed, now call the real routine with skip validation check flag */
Flags |= HEAP_SKIP_VALIDATION_CHECKS |
HEAP_TAIL_CHECKING_ENABLED |
HEAP_FREE_CHECKING_ENABLED;
Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters);
if (!Heap) return NULL;
// FIXME: Capture stack backtrace
RtlpValidateHeapHeaders(Heap, TRUE);
return Heap;
}
BOOLEAN NTAPI
RtlDebugDestroyHeap(HANDLE HeapPtr)
{
SIZE_T Size = 0;
PHEAP Heap = (PHEAP)HeapPtr;
if (Heap == RtlGetCurrentPeb()->ProcessHeap)
{
DPRINT1("HEAP: It's forbidden delete process heap!");
return FALSE;
}
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
if (!RtlpValidateHeap(Heap, FALSE)) return FALSE;
/* Make heap invalid by zeroing its signature */
Heap->Signature = 0;
/* Free validate headers copy if it was existing */
if (Heap->HeaderValidateCopy)
{
ZwFreeVirtualMemory(NtCurrentProcess(),
&Heap->HeaderValidateCopy,
&Size,
MEM_RELEASE);
}
return TRUE;
}
PVOID NTAPI
RtlDebugAllocateHeap(PVOID HeapPtr,
ULONG Flags,
SIZE_T Size)
{
PHEAP Heap = (PHEAP)HeapPtr;
SIZE_T AllocSize = 1;
BOOLEAN HeapLocked = FALSE;
PVOID Result;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapAllocate(HeapPtr, Flags, Size);
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return NULL;
}
/* Add settable user value flag */
Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
/* Calculate size */
if (Size) AllocSize = Size;
AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA);
/* Check if size didn't exceed max one */
if (AllocSize < Size ||
AllocSize > Heap->MaximumAllocationSize)
{
DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize);
return NULL;
}
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Call main routine to do the stuff */
Result = RtlAllocateHeap(HeapPtr, Flags, Size);
/* Validate heap headers */
RtlpValidateHeapHeaders(Heap, TRUE);
if (Result)
{
if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)
RtlpValidateHeap(Heap, FALSE);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
PVOID NTAPI
RtlDebugReAllocateHeap(HANDLE HeapPtr,
ULONG Flags,
PVOID Ptr,
SIZE_T Size)
{
PHEAP Heap = (PHEAP)HeapPtr;
SIZE_T AllocSize = 1;
BOOLEAN HeapLocked = FALSE;
PVOID Result = NULL;
PHEAP_ENTRY HeapEntry;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapReAllocate(HeapPtr, Flags, Ptr, Size);
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return NULL;
}
/* Add settable user value flag */
Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
/* Calculate size */
if (Size) AllocSize = Size;
AllocSize = ((AllocSize + Heap->AlignRound) & Heap->AlignMask) + sizeof(HEAP_ENTRY_EXTRA);
/* Check if size didn't exceed max one */
if (AllocSize < Size ||
AllocSize > Heap->MaximumAllocationSize)
{
DPRINT1("HEAP: Too big allocation size %x (max allowed %x)\n", Size, Heap->MaximumAllocationSize);
return NULL;
}
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)Ptr - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* Call main routine to do the stuff */
Result = RtlReAllocateHeap(HeapPtr, Flags, Ptr, Size);
if (Result)
{
/* Validate heap headers and then heap itself */
RtlpValidateHeapHeaders(Heap, TRUE);
RtlpValidateHeap(Heap, FALSE);
}
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
BOOLEAN NTAPI
RtlDebugFreeHeap(HANDLE HeapPtr,
ULONG Flags,
PVOID Ptr)
{
PHEAP Heap = (PHEAP)HeapPtr;
BOOLEAN HeapLocked = FALSE;
PHEAP_ENTRY HeapEntry;
BOOLEAN Result = FALSE;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapFree(HeapPtr, Flags, Ptr);
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
/* Add skip validation flag */
Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)Ptr - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* If it succeeded - call the main routine */
Result = RtlFreeHeap(HeapPtr, Flags, Ptr);
/* Validate heap headers and then heap itself */
RtlpValidateHeapHeaders(Heap, TRUE);
RtlpValidateHeap(Heap, FALSE);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
BOOLEAN NTAPI
RtlDebugGetUserInfoHeap(PVOID HeapHandle,
ULONG Flags,
PVOID BaseAddress,
PVOID *UserValue,
PULONG UserFlags)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN HeapLocked = FALSE;
PHEAP_ENTRY HeapEntry;
BOOLEAN Result = FALSE;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapGetUserInfo(HeapHandle, Flags, BaseAddress, UserValue, UserFlags);
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
/* Add skip validation flag */
Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* If it succeeded - call the main routine */
Result = RtlGetUserInfoHeap(HeapHandle, Flags, BaseAddress, UserValue, UserFlags);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
BOOLEAN NTAPI
RtlDebugSetUserValueHeap(PVOID HeapHandle,
ULONG Flags,
PVOID BaseAddress,
PVOID UserValue)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN HeapLocked = FALSE;
PHEAP_ENTRY HeapEntry;
BOOLEAN Result = FALSE;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapSetUserValue(HeapHandle, Flags, BaseAddress, UserValue);
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
/* Add skip validation flag */
Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* If it succeeded - call the main routine */
Result = RtlSetUserValueHeap(HeapHandle, Flags, BaseAddress, UserValue);
/* Validate the heap */
RtlpValidateHeap(Heap, FALSE);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
BOOLEAN
NTAPI
RtlDebugSetUserFlagsHeap(PVOID HeapHandle,
ULONG Flags,
PVOID BaseAddress,
ULONG UserFlagsReset,
ULONG UserFlagsSet)
{
PHEAP Heap = (PHEAP)HeapHandle;
BOOLEAN HeapLocked = FALSE;
PHEAP_ENTRY HeapEntry;
BOOLEAN Result = FALSE;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapSetUserFlags(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
/* Check if this heap allows flags to be set at all */
if (UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS ||
UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS)
{
return FALSE;
}
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
/* Add skip validation flag */
Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* If it succeeded - call the main routine */
Result = RtlSetUserFlagsHeap(HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
/* Validate the heap */
RtlpValidateHeap(Heap, FALSE);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
SIZE_T NTAPI
RtlDebugSizeHeap(HANDLE HeapPtr,
ULONG Flags,
PVOID Ptr)
{
PHEAP Heap = (PHEAP)HeapPtr;
BOOLEAN HeapLocked = FALSE;
PHEAP_ENTRY HeapEntry;
SIZE_T Result = ~(SIZE_T)0;
if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
return RtlpPageHeapSize(HeapPtr, Flags, Ptr);
/* Check heap signature */
if (Heap->Signature != HEAP_SIGNATURE)
{
DPRINT1("HEAP: Invalid heap %p signature 0x%x\n", Heap, Heap->Signature);
return FALSE;
}
/* Add skip validation flag */
Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
/* Lock the heap ourselves */
if (!(Flags & HEAP_NO_SERIALIZE))
{
RtlEnterHeapLock(Heap->LockVariable, TRUE);
HeapLocked = TRUE;
/* Add no serialize flag so that the main routine won't try to acquire the lock again */
Flags |= HEAP_NO_SERIALIZE;
}
/* Validate the heap if necessary */
RtlpValidateHeap(Heap, FALSE);
/* Get the existing heap entry */
HeapEntry = (PHEAP_ENTRY)Ptr - 1;
/* Validate it */
if (RtlpValidateHeapEntry(Heap, HeapEntry))
{
/* If it succeeded - call the main routine */
Result = RtlSizeHeap(HeapPtr, Flags, Ptr);
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Result;
}
[RTL/HEAP] - First commit of a heap manager rewrite. It introduces a totally new heap manager, with the following features: * Actually resembles real NT's heap manager; * Based on data structures similar to Windows 2003 and Vista+'s heap structures; * Supporting advanced heap flags (e.g. useful for debugging); * Substantially lower fragmentation rates (and thus speed and reliability) than the existing Wine's implementation. It's going to be further enhanced by adding a frontend allocator (for example, as lookaside lists, or as a Low Fragmentation Heap alike frontend in Vista+ systems); * Real support for user-defined flags and native support for user-defined values; * Native support for a custom commit routine, which is very important for trunk's win32 subsystem; * Reserving, committing, decommitting and freeing on the fly, unlike existing heap manager which prefers to reserve and commit as much as possible, and doesn't decommit when it's no longer necessary; * Support for per process heaps, with a proper lock; * Reserved support for a special so-called debug heap allocator (to be implemented in heapdbg.c) which will be useful for finding heap corruptions. The committed code is a result of a month of work, and is a heavy work-in-progress one. It already implements the bare minimum required to boot to 3rd stage and run FireFox 3, however many rare codepaths are not implemented yet and there is some maintenance work to do (e.g. move structures and defines to a standalone header file). A list of used references is stated in the header of a source file for now. svn path=/trunk/; revision=49007
2010-10-05 21:43:48 +00:00
/* EOF */