You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1326 lines
26 KiB
1326 lines
26 KiB
/*
|
|
* memmgr.c - Memory manager module.
|
|
*/
|
|
|
|
/*
|
|
|
|
The memory manager implementation in this module uses either a private
|
|
shared heap (if PRIVATE_HEAP is #defined) or the non-shared process heap (if
|
|
PRIVATE_HEAP is not #defined). Thde debug implementation of this memory
|
|
manager keeps track of memory blocks allocated from the heap using a
|
|
doubly-linked list of heap element nodes. Each node describes one allocated
|
|
heap element.
|
|
|
|
Debug heap elements are allocated with extra space at the beginning and end
|
|
of the element. Prefix and suffix sentinels surround each allocated heap
|
|
element. New heap elements are filled with UNINITIALIZED_BYTE_VALUE. Freed
|
|
heap elements are filled with FREED_BYTE_VALUE. The new tails of grown heap
|
|
elements are filled with UNINITIALIZED_BYTE_VALUE.
|
|
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/* Constants
|
|
************/
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
/* undocumented flag for HeapCreate() from kernel32.h */
|
|
|
|
|
|
#define HEAP_SHARED (0x04000000)
|
|
|
|
/*
|
|
* Set maximum shared heap size used for CreateHeap() to 0 since we don't know
|
|
* how big the heap may get, and we don't want to restrict its size
|
|
* artificially. BrianSm says this is ok.
|
|
*/
|
|
|
|
#define MAX_SHARED_HEAP_SIZE (0)
|
|
|
|
#endif /* PRIVATE_HEAP */
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* heap element byte fill values */
|
|
|
|
#define UNINITIALIZED_BYTE_VALUE (0xcc)
|
|
#define FREED_BYTE_VALUE (0xdd)
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* Macros
|
|
*********/
|
|
|
|
/* atomic memory management function wrappers for translation */
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
#define GetHeap() (Mhheap)
|
|
#define MEMALLOCATE(size) HeapAlloc(GetHeap(), 0, (size))
|
|
#define MEMREALLOCATE(pv, size) HeapReAlloc(GetHeap(), 0, (pv), (size))
|
|
#define MEMFREE(pv) HeapFree(GetHeap(), 0, (pv))
|
|
#define MEMSIZE(pv) (DWORD)HeapSize(GetHeap(), 0, (pv))
|
|
|
|
#else
|
|
|
|
#define MEMALLOCATE(size) LocalAlloc(LMEM_FIXED, (size))
|
|
#define MEMREALLOCATE(pv, size) LocalReAlloc((pv), (size), 0)
|
|
#define MEMFREE(pv) (! LocalFree(pv))
|
|
#define MEMSIZE(pv) (DWORD)LocalSize(pv)
|
|
|
|
#endif
|
|
|
|
|
|
/* Types
|
|
********/
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* heap element descriptor structure */
|
|
|
|
typedef struct _heapelemdesc
|
|
{
|
|
TCHAR rgchSize[6]; /* enough for 99,999 lines */
|
|
TCHAR rgchFile[24];
|
|
ULONG ulLine;
|
|
}
|
|
HEAPELEMDESC;
|
|
DECLARE_STANDARD_TYPES(HEAPELEMDESC);
|
|
|
|
/* heap node */
|
|
|
|
typedef struct _heapnode
|
|
{
|
|
PCVOID pcv;
|
|
DWORD dwcbSize;
|
|
struct _heapnode *phnPrev;
|
|
struct _heapnode *phnNext;
|
|
HEAPELEMDESC hed;
|
|
}
|
|
HEAPNODE;
|
|
DECLARE_STANDARD_TYPES(HEAPNODE);
|
|
|
|
/* heap */
|
|
|
|
typedef struct _heap
|
|
{
|
|
HEAPNODE hnHead;
|
|
}
|
|
HEAP;
|
|
DECLARE_STANDARD_TYPES(HEAP);
|
|
|
|
/* heap summary filled in by AnalyzeHeap() */
|
|
|
|
typedef struct _heapsummary
|
|
{
|
|
ULONG ulcUsedElements;
|
|
DWORD dwcbUsedSize;
|
|
}
|
|
HEAPSUMMARY;
|
|
DECLARE_STANDARD_TYPES(HEAPSUMMARY);
|
|
|
|
/* debug flags */
|
|
|
|
typedef enum _memmgrdebugflags
|
|
{
|
|
MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY = 0x0001,
|
|
|
|
MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT = 0x0002,
|
|
|
|
ALL_MEMMGR_DFLAGS = (MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY |
|
|
MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT)
|
|
}
|
|
MEMMGRDEBUGFLAGS;
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* Global Variables
|
|
*******************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* parameters used by debug AllocateMemory() macro */
|
|
|
|
PUBLIC_DATA LPCTSTR GpcszElemHdrSize = NULL;
|
|
PUBLIC_DATA LPCTSTR GpcszElemHdrFile = NULL;
|
|
PUBLIC_DATA ULONG GulElemHdrLine = 0;
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* Module Variables
|
|
*******************/
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
/* handle to global shared heap */
|
|
|
|
PRIVATE_DATA HANDLE Mhheap = NULL;
|
|
|
|
#endif /* PRIVATE_HEAP */
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* heap */
|
|
|
|
PRIVATE_DATA PHEAP Mpheap = NULL;
|
|
|
|
/* debug flags */
|
|
|
|
PRIVATE_DATA DWORD MdwMemoryManagerModuleFlags = 0;
|
|
|
|
/* heap element sentinels */
|
|
|
|
PRIVATE_DATA CONST struct
|
|
{
|
|
BYTE rgbyte[4];
|
|
}
|
|
MchsPrefix =
|
|
{
|
|
{ TEXT('H'), TEXT('E'), TEXT('A'), TEXT('D') }
|
|
};
|
|
|
|
PRIVATE_DATA CONST struct
|
|
{
|
|
BYTE rgbyte[4];
|
|
}
|
|
MchsSuffix =
|
|
{
|
|
{ TEXT('T'), TEXT('A'), TEXT('I'), TEXT('L') }
|
|
};
|
|
|
|
/* .ini file switch descriptions */
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisValidateHeapOnEntry =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("ValidateHeapOnEntry"),
|
|
&MdwMemoryManagerModuleFlags,
|
|
MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY
|
|
};
|
|
|
|
PRIVATE_DATA CBOOLINISWITCH cbisValidateHeapOnExit =
|
|
{
|
|
IST_BOOL,
|
|
TEXT("ValidateHeapOnExit"),
|
|
&MdwMemoryManagerModuleFlags,
|
|
MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT
|
|
};
|
|
|
|
PRIVATE_DATA const PCVOID MrgcpcvisMemoryManagerModule[] =
|
|
{
|
|
&cbisValidateHeapOnEntry,
|
|
&cbisValidateHeapOnExit
|
|
};
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
PRIVATE_CODE DWORD CalculatePrivateSize(DWORD);
|
|
PRIVATE_CODE PVOID GetPrivateHeapPtr(PVOID);
|
|
PRIVATE_CODE PVOID GetPublicHeapPtr(PVOID);
|
|
PRIVATE_CODE DWORD GetHeapSize(PCVOID);
|
|
PRIVATE_CODE BOOL AddHeapElement(PCVOID, DWORD);
|
|
PRIVATE_CODE void RemoveHeapElement(PCVOID);
|
|
PRIVATE_CODE void ModifyHeapElement(PCVOID, PCVOID, DWORD);
|
|
PRIVATE_CODE BOOL FindHeapElement(PCVOID, PHEAPNODE *);
|
|
PRIVATE_CODE void FillNewMemory(PBYTE, DWORD, DWORD);
|
|
PRIVATE_CODE void FillFreedMemory(PBYTE, DWORD);
|
|
PRIVATE_CODE void FillGrownMemory(PBYTE, DWORD, DWORD, DWORD);
|
|
PRIVATE_CODE void FillShrunkenMemory(PBYTE, DWORD, DWORD, DWORD);
|
|
PRIVATE_CODE BOOL IsValidHeapPtr(PCVOID);
|
|
PRIVATE_CODE BOOL IsHeapOK(void);
|
|
PRIVATE_CODE BOOL IsValidPCHEAPNODE(PCHEAPNODE);
|
|
PRIVATE_CODE BOOL IsValidPCHEAPELEMDESC(PCHEAPELEMDESC);
|
|
PRIVATE_CODE BOOL IsValidHeapElement(PCBYTE, DWORD, DWORD);
|
|
PRIVATE_CODE void SpewHeapElementInfo(PCHEAPNODE);
|
|
PRIVATE_CODE void AnalyzeHeap(PHEAPSUMMARY, DWORD);
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
/*
|
|
** InitPrivateHeapModule()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL InitPrivateHeapModule(void)
|
|
{
|
|
BOOL bResult;
|
|
SYSTEM_INFO si;
|
|
|
|
ASSERT(! Mhheap);
|
|
|
|
/* Create shared heap. */
|
|
|
|
GetSystemInfo(&si);
|
|
|
|
Mhheap = HeapCreate(0, si.dwPageSize, MAX_SHARED_HEAP_SIZE);
|
|
|
|
if (Mhheap)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
ASSERT(! Mpheap);
|
|
|
|
Mpheap = MEMALLOCATE(sizeof(*Mpheap));
|
|
|
|
if (Mpheap)
|
|
{
|
|
FillMemory(Mpheap, sizeof(*Mpheap), 0);
|
|
bResult = TRUE;
|
|
|
|
TRACE_OUT((TEXT("InitMemoryManagerModule(): Created shared heap, initial size == %lu, maximum size == %lu."),
|
|
si.dwPageSize,
|
|
MAX_SHARED_HEAP_SIZE));
|
|
}
|
|
else
|
|
{
|
|
EVAL(HeapDestroy(Mhheap));
|
|
Mhheap = NULL;
|
|
bResult = FALSE;
|
|
|
|
WARNING_OUT((TEXT("InitMemoryManagerModule(): Failed to create shared heap head.")));
|
|
}
|
|
|
|
#else /* DEBUG */
|
|
|
|
bResult = TRUE;
|
|
|
|
#endif /* DEBUG */
|
|
|
|
}
|
|
else
|
|
{
|
|
bResult = FALSE;
|
|
|
|
WARNING_OUT((TEXT("InitMemoryManagerModule(): Failed to create shared heap.")));
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
#else /* PRIVATE_HEAP */
|
|
|
|
|
|
/*
|
|
** InitHeapModule()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL InitHeapModule(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
#ifdef DEBUG
|
|
|
|
ASSERT(! Mpheap);
|
|
|
|
Mpheap = MEMALLOCATE(sizeof(*Mpheap));
|
|
|
|
if (Mpheap)
|
|
{
|
|
FillMemory(Mpheap, sizeof(*Mpheap), 0);
|
|
|
|
TRACE_OUT((TEXT("InitMemoryManagerModule(): Created heap.")));
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("InitMemoryManagerModule(): Failed to create heap head.")));
|
|
|
|
bResult = (Mpheap != NULL);
|
|
|
|
#else
|
|
|
|
bResult = TRUE;
|
|
|
|
#endif
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
#endif /* PRIVATE_HEAP */
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** CalculatePrivateSize()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE DWORD CalculatePrivateSize(DWORD dwcbPublicSize)
|
|
{
|
|
ASSERT(dwcbPublicSize <= DWORD_MAX - sizeof(MchsPrefix) - sizeof(MchsSuffix));
|
|
|
|
return(dwcbPublicSize + sizeof(MchsPrefix) + sizeof(MchsSuffix));
|
|
}
|
|
|
|
|
|
/*
|
|
** GetPrivateHeapPtr()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE PVOID GetPrivateHeapPtr(PVOID pvPublic)
|
|
{
|
|
ASSERT((ULONG_PTR)pvPublic > sizeof(MchsPrefix));
|
|
|
|
return((PBYTE)pvPublic - sizeof(MchsPrefix));
|
|
}
|
|
|
|
|
|
/*
|
|
** GetPublicHeapPtr()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE PVOID GetPublicHeapPtr(PVOID pvPrivate)
|
|
{
|
|
ASSERT((PCBYTE)pvPrivate <= (PCBYTE)PTR_MAX - sizeof(MchsPrefix));
|
|
|
|
return((PBYTE)pvPrivate + sizeof(MchsPrefix));
|
|
}
|
|
|
|
|
|
/*
|
|
** GetHeapSize()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE DWORD GetHeapSize(PCVOID pcv)
|
|
{
|
|
PHEAPNODE phn;
|
|
DWORD dwcbSize;
|
|
|
|
if (EVAL(FindHeapElement(pcv, &phn)))
|
|
dwcbSize = phn->dwcbSize;
|
|
else
|
|
dwcbSize = 0;
|
|
|
|
return(dwcbSize);
|
|
}
|
|
|
|
|
|
/*
|
|
** AddHeapElement()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** Assumes that the global variables GpcszElemHdrSize, GpcszElemHdrFile, and
|
|
** GulElemHdrLine are filled in.
|
|
*/
|
|
PRIVATE_CODE BOOL AddHeapElement(PCVOID pcvNew, DWORD dwcbSize)
|
|
{
|
|
PHEAPNODE phnNew;
|
|
|
|
/* Is the new heap element already in the list? */
|
|
|
|
ASSERT(! FindHeapElement(pcvNew, &phnNew));
|
|
|
|
if (Mpheap)
|
|
{
|
|
/* Create new heap node. */
|
|
|
|
phnNew = MEMALLOCATE(sizeof(*phnNew));
|
|
|
|
if (phnNew)
|
|
{
|
|
/* Fill in heap node fields. */
|
|
|
|
phnNew->pcv = pcvNew;
|
|
phnNew->dwcbSize = dwcbSize;
|
|
|
|
/* Insert heap node at front of list. */
|
|
|
|
phnNew->phnNext = Mpheap->hnHead.phnNext;
|
|
phnNew->phnPrev = &(Mpheap->hnHead);
|
|
Mpheap->hnHead.phnNext = phnNew;
|
|
|
|
if (phnNew->phnNext)
|
|
phnNew->phnNext->phnPrev = phnNew;
|
|
|
|
/* Fill in heap element descriptor fields. */
|
|
|
|
MyLStrCpyN(phnNew->hed.rgchSize, GpcszElemHdrSize, ARRAYSIZE(phnNew->hed.rgchSize));
|
|
MyLStrCpyN(phnNew->hed.rgchFile, GpcszElemHdrFile, ARRAYSIZE(phnNew->hed.rgchFile));
|
|
phnNew->hed.ulLine = GulElemHdrLine;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(phnNew, CHEAPNODE));
|
|
}
|
|
}
|
|
else
|
|
phnNew = NULL;
|
|
|
|
return(phnNew != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
** RemoveHeapElement()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void RemoveHeapElement(PCVOID pcvOld)
|
|
{
|
|
PHEAPNODE phnOld;
|
|
|
|
if (EVAL(FindHeapElement(pcvOld, &phnOld)))
|
|
{
|
|
/* Remove heap node from list. */
|
|
|
|
phnOld->phnPrev->phnNext = phnOld->phnNext;
|
|
|
|
if (phnOld->phnNext)
|
|
phnOld->phnNext->phnPrev = phnOld->phnPrev;
|
|
|
|
MEMFREE(phnOld);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** ModifyHeapElement()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void ModifyHeapElement(PCVOID pcvOld, PCVOID pcvNew, DWORD dwcbNewSize)
|
|
{
|
|
PHEAPNODE phn;
|
|
|
|
if (EVAL(FindHeapElement(pcvOld, &phn)))
|
|
{
|
|
phn->pcv = pcvNew;
|
|
phn->dwcbSize = dwcbNewSize;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** FindHeapElement()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL FindHeapElement(PCVOID pcvTarget, PHEAPNODE *pphn)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
PHEAPNODE phn;
|
|
|
|
ASSERT(IS_VALID_WRITE_PTR(pphn, PHEAPNODE));
|
|
|
|
if (Mpheap)
|
|
{
|
|
for (phn = Mpheap->hnHead.phnNext;
|
|
phn;
|
|
phn = phn->phnNext)
|
|
{
|
|
/*
|
|
* Verify each HEAPNODE structure carefully. We may be in the middle of
|
|
* a ModifyHeapElement() call, in which case just the target HEAPNODE may
|
|
* be invalid, e.g., after MEMREALLOCATE() in ReallocateMemory().
|
|
*/
|
|
|
|
ASSERT((IS_VALID_READ_PTR(phn, CHEAPNODE) && phn->pcv == pcvTarget) ||
|
|
IS_VALID_STRUCT_PTR(phn, CHEAPNODE));
|
|
|
|
if (phn->pcv == pcvTarget)
|
|
{
|
|
*pphn = phn;
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(bFound);
|
|
}
|
|
|
|
|
|
/*
|
|
** FillNewMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void FillNewMemory(PBYTE pbyte, DWORD dwcbRequestedSize,
|
|
DWORD dwcbAllocatedSize)
|
|
{
|
|
ASSERT(dwcbRequestedSize >= sizeof(MchsPrefix) + sizeof(MchsSuffix));
|
|
ASSERT(dwcbAllocatedSize >= dwcbRequestedSize);
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pbyte, BYTE, (UINT)dwcbAllocatedSize));
|
|
|
|
/* Fill new heap element with the uninitialized byte value. */
|
|
|
|
FillMemory(pbyte, dwcbAllocatedSize, UNINITIALIZED_BYTE_VALUE);
|
|
|
|
/* Copy prefix and suffix heap element sentinels. */
|
|
|
|
CopyMemory(pbyte, &MchsPrefix, sizeof(MchsPrefix));
|
|
CopyMemory(pbyte + dwcbRequestedSize - sizeof(MchsSuffix), &MchsSuffix,
|
|
sizeof(MchsSuffix));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** FillFreedMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void FillFreedMemory(PBYTE pbyte, DWORD dwcbAllocatedSize)
|
|
{
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pbyte, BYTE, (UINT)dwcbAllocatedSize));
|
|
|
|
/* Fill old heap element with the freed byte value. */
|
|
|
|
FillMemory(pbyte, dwcbAllocatedSize, FREED_BYTE_VALUE);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** FillGrownMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void FillGrownMemory(PBYTE pbyte, DWORD dwcbOldRequestedSize,
|
|
DWORD dwcbNewRequestedSize,
|
|
DWORD dwcbNewAllocatedSize)
|
|
{
|
|
ASSERT(dwcbOldRequestedSize >= sizeof(MchsPrefix) + sizeof(MchsSuffix));
|
|
ASSERT(dwcbNewRequestedSize > dwcbOldRequestedSize);
|
|
ASSERT(dwcbNewAllocatedSize >= dwcbNewRequestedSize);
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pbyte, BYTE, (UINT)dwcbNewAllocatedSize));
|
|
|
|
ASSERT(MyMemComp(pbyte, &MchsPrefix, sizeof(MchsPrefix)) == CR_EQUAL);
|
|
|
|
/* Fill new heap element tail with the uninitialized byte value. */
|
|
|
|
FillMemory(pbyte + dwcbOldRequestedSize - sizeof(MchsSuffix),
|
|
dwcbNewRequestedSize - dwcbOldRequestedSize,
|
|
UNINITIALIZED_BYTE_VALUE);
|
|
|
|
/* Copy suffix heap element sentinel. */
|
|
|
|
CopyMemory(pbyte + dwcbNewRequestedSize - sizeof(MchsSuffix), &MchsSuffix,
|
|
sizeof(MchsSuffix));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** FillShrunkenMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void FillShrunkenMemory(PBYTE pbyte, DWORD dwcbOldRequestedSize,
|
|
DWORD dwcbNewRequestedSize,
|
|
DWORD dwcbNewAllocatedSize)
|
|
{
|
|
ASSERT(dwcbNewRequestedSize >= sizeof(MchsPrefix) + sizeof(MchsSuffix));
|
|
ASSERT(dwcbNewRequestedSize < dwcbOldRequestedSize);
|
|
ASSERT(dwcbNewAllocatedSize >= dwcbNewRequestedSize);
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pbyte, BYTE, (UINT)dwcbNewAllocatedSize));
|
|
|
|
ASSERT(MyMemComp(pbyte, &MchsPrefix, sizeof(MchsPrefix)) == CR_EQUAL);
|
|
|
|
/* Fill old heap element tail with the freed byte value. */
|
|
|
|
FillMemory(pbyte + dwcbNewRequestedSize,
|
|
dwcbOldRequestedSize - dwcbNewRequestedSize, FREED_BYTE_VALUE);
|
|
|
|
/* Copy suffix heap element sentinel. */
|
|
|
|
CopyMemory(pbyte + dwcbNewRequestedSize - sizeof(MchsSuffix), &MchsSuffix,
|
|
sizeof(MchsSuffix));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidHeapPtr()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidHeapPtr(PCVOID pcv)
|
|
{
|
|
PHEAPNODE phnUnused;
|
|
|
|
return(FindHeapElement(pcv, &phnUnused));
|
|
}
|
|
|
|
|
|
/*
|
|
** IsHeapOK()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsHeapOK(void)
|
|
{
|
|
PHEAPNODE phn;
|
|
|
|
if (Mpheap)
|
|
{
|
|
for (phn = Mpheap->hnHead.phnNext;
|
|
phn && IS_VALID_STRUCT_PTR(phn, CHEAPNODE);
|
|
phn = phn->phnNext)
|
|
;
|
|
}
|
|
else
|
|
phn = (PHEAPNODE)0xFFFF;
|
|
|
|
return(phn == NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCHEAPNODE()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCHEAPNODE(PCHEAPNODE pchn)
|
|
{
|
|
BOOL bResult;
|
|
|
|
if (IS_VALID_READ_PTR(pchn, CHEAPNODE) &&
|
|
IS_VALID_READ_PTR(pchn->phnPrev, CHEAPNODE) &&
|
|
EVAL(pchn->phnPrev->phnNext == pchn) &&
|
|
EVAL(! pchn->phnNext ||
|
|
(IS_VALID_READ_PTR(pchn->phnNext, CHEAPNODE) &&
|
|
EVAL(pchn->phnNext->phnPrev == pchn))) &&
|
|
EVAL(IsValidHeapElement(pchn->pcv, pchn->dwcbSize, MEMSIZE((PVOID)(pchn->pcv)))) &&
|
|
IS_VALID_STRUCT_PTR(&(pchn->hed), CHEAPELEMDESC))
|
|
bResult = TRUE;
|
|
else
|
|
bResult = FALSE;
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCHEAPELEMDESC()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCHEAPELEMDESC(PCHEAPELEMDESC pched)
|
|
{
|
|
BOOL bResult;
|
|
|
|
/* Any value for pched->ulLine is valid. */
|
|
|
|
if (IS_VALID_READ_PTR(pched, CHEAPELEMDESC))
|
|
bResult = TRUE;
|
|
else
|
|
bResult = FALSE;
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidHeapElement()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidHeapElement(PCBYTE pcbyte, DWORD dwcbRequestedSize,
|
|
DWORD dwcbAllocatedSize)
|
|
{
|
|
BOOL bResult;
|
|
|
|
if (EVAL(dwcbRequestedSize >= sizeof(MchsPrefix) + sizeof(MchsSuffix)) &&
|
|
EVAL(dwcbAllocatedSize >= dwcbRequestedSize) &&
|
|
IS_VALID_READ_PTR(pcbyte, dwcbAllocatedSize) &&
|
|
EVAL(MyMemComp(pcbyte, &MchsPrefix, sizeof(MchsPrefix)) == CR_EQUAL) &&
|
|
EVAL(MyMemComp(pcbyte + dwcbRequestedSize - sizeof(MchsSuffix), &MchsSuffix, sizeof(MchsSuffix)) == CR_EQUAL))
|
|
bResult = TRUE;
|
|
else
|
|
bResult = FALSE;
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** SpewHeapElementInfo()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void SpewHeapElementInfo(PCHEAPNODE pchn)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pchn, CHEAPNODE));
|
|
|
|
TRACE_OUT((TEXT("Used heap element at %#lx:\r\n")
|
|
TEXT(" %lu bytes requested\r\n")
|
|
TEXT(" %lu bytes allocated\r\n")
|
|
TEXT(" originally allocated as '%s' bytes in file %s at line %lu"),
|
|
pchn->pcv,
|
|
pchn->dwcbSize,
|
|
MEMSIZE((PVOID)(pchn->pcv)),
|
|
pchn->hed.rgchSize,
|
|
pchn->hed.rgchFile,
|
|
pchn->hed.ulLine));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** AnalyzeHeap()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void AnalyzeHeap(PHEAPSUMMARY phs, DWORD dwFlags)
|
|
{
|
|
PCHEAPNODE pchn;
|
|
ULONG ulcHeapElements = 0;
|
|
DWORD dwcbUsed = 0;
|
|
|
|
ASSERT(IS_VALID_WRITE_PTR(phs, HEAPSUMMARY));
|
|
ASSERT(FLAGS_ARE_VALID(dwFlags, SHS_FL_SPEW_USED_INFO));
|
|
|
|
ASSERT(IsHeapOK());
|
|
|
|
TRACE_OUT((TEXT("Starting private heap analysis.")));
|
|
|
|
if (Mpheap)
|
|
{
|
|
for (pchn = Mpheap->hnHead.phnNext;
|
|
pchn;
|
|
pchn = pchn->phnNext)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pchn, CHEAPNODE));
|
|
|
|
ASSERT(ulcHeapElements < ULONG_MAX);
|
|
ulcHeapElements++;
|
|
|
|
ASSERT(dwcbUsed < DWORD_MAX - pchn->dwcbSize);
|
|
dwcbUsed += pchn->dwcbSize;
|
|
|
|
if (IS_FLAG_SET(dwFlags, SHS_FL_SPEW_USED_INFO))
|
|
SpewHeapElementInfo(pchn);
|
|
}
|
|
|
|
phs->ulcUsedElements = ulcHeapElements;
|
|
phs->dwcbUsedSize = dwcbUsed;
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("Private heap not allocated!")));
|
|
|
|
TRACE_OUT((TEXT("Private heap analysis complete.")));
|
|
|
|
return;
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
/*
|
|
** InitMemoryManagerModule()
|
|
**
|
|
** When PRIVATE_HEAP is defined, this function should be called only
|
|
** once, when the DLL is being first initialized. When PRIVATE_HEAP
|
|
** is not defined, this function should be called for every
|
|
** DLL_PROCESS_ATTACH.
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL InitMemoryManagerModule(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
bResult = InitPrivateHeapModule();
|
|
|
|
#else /* PRIVATE_HEAP */
|
|
|
|
bResult = InitHeapModule();
|
|
|
|
#endif
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
/*
|
|
** ExitMemoryManagerModule()
|
|
**
|
|
** When PRIVATE_HEAP is defined, this function should be called only
|
|
** once, when the DLL is finally being terminated. When PRIVATE_HEAP
|
|
** is not defined, this function should be called for every
|
|
** DLL_PROCESS_DETACH.
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void ExitMemoryManagerModule(void)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (Mpheap)
|
|
{
|
|
MEMFREE(Mpheap);
|
|
Mpheap = NULL;
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("ExitMemoryManagerModule() called when Mpheap is NULL.")));
|
|
|
|
#endif
|
|
|
|
#ifdef PRIVATE_HEAP
|
|
|
|
if (Mhheap)
|
|
{
|
|
EVAL(HeapDestroy(Mhheap));
|
|
Mhheap = NULL;
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("ExitMemoryManagerModule() called when Mhheap is NULL.")));
|
|
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** MyMemComp()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE COMPARISONRESULT MyMemComp(PCVOID pcv1, PCVOID pcv2, DWORD dwcbSize)
|
|
{
|
|
int nResult = 0;
|
|
PCBYTE pcbyte1 = pcv1;
|
|
PCBYTE pcbyte2 = pcv2;
|
|
|
|
ASSERT(IS_VALID_READ_BUFFER_PTR(pcv1, BYTE, (UINT)dwcbSize));
|
|
ASSERT(IS_VALID_READ_BUFFER_PTR(pcv2, BYTE, (UINT)dwcbSize));
|
|
|
|
while (dwcbSize > 0 &&
|
|
! (nResult = *pcbyte1 - *pcbyte2))
|
|
{
|
|
pcbyte1++;
|
|
pcbyte2++;
|
|
dwcbSize--;
|
|
}
|
|
|
|
return(MapIntToComparisonResult(nResult));
|
|
}
|
|
|
|
|
|
/*
|
|
** MyAllocateMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL MyAllocateMemory(DWORD dwcbSize, PVOID *ppvNew)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
DWORD dwcbRequestedSize = dwcbSize;
|
|
|
|
// ASSERT(dwcbSize >= 0); // DWORDs are nonnegative by definition
|
|
ASSERT(IS_VALID_WRITE_PTR(ppvNew, PVOID));
|
|
|
|
dwcbSize = CalculatePrivateSize(dwcbSize);
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY))
|
|
ASSERT(IsHeapOK());
|
|
|
|
#endif
|
|
|
|
*ppvNew = MEMALLOCATE(dwcbSize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (*ppvNew)
|
|
{
|
|
FillNewMemory(*ppvNew, dwcbSize, MEMSIZE(*ppvNew));
|
|
|
|
if (AddHeapElement(*ppvNew, dwcbSize))
|
|
{
|
|
*ppvNew = GetPublicHeapPtr(*ppvNew);
|
|
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(*ppvNew, BYTE, (UINT)dwcbRequestedSize));
|
|
}
|
|
else
|
|
{
|
|
EVAL(MEMFREE(*ppvNew));
|
|
*ppvNew = NULL;
|
|
}
|
|
}
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT))
|
|
ASSERT(IsHeapOK());
|
|
|
|
#endif
|
|
|
|
return(*ppvNew != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
** FreeMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void FreeMemory(PVOID pvOld)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY))
|
|
ASSERT(IsHeapOK());
|
|
|
|
pvOld = GetPrivateHeapPtr(pvOld);
|
|
|
|
ASSERT(IsValidHeapPtr(pvOld));
|
|
|
|
RemoveHeapElement(pvOld);
|
|
|
|
FillFreedMemory(pvOld, MEMSIZE(pvOld));
|
|
|
|
#endif
|
|
|
|
EVAL(MEMFREE(pvOld));
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT))
|
|
ASSERT(IsHeapOK());
|
|
|
|
#endif /* DEBUG */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** ReallocateMemory()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL ReallocateMemory(PVOID pvOld, DWORD dwcbNewSize, PVOID *ppvNew)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
DWORD dwcbRequestedSize = dwcbNewSize;
|
|
DWORD dwcbOldSize;
|
|
|
|
ASSERT(IS_VALID_WRITE_PTR(ppvNew, PVOID));
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_ENTRY))
|
|
ASSERT(IsHeapOK());
|
|
|
|
pvOld = GetPrivateHeapPtr(pvOld);
|
|
|
|
ASSERT(IsValidHeapPtr(pvOld));
|
|
|
|
dwcbNewSize = CalculatePrivateSize(dwcbNewSize);
|
|
|
|
dwcbOldSize = GetHeapSize(pvOld);
|
|
|
|
if (dwcbNewSize == dwcbOldSize)
|
|
WARNING_OUT((TEXT("ReallocateMemory(): Size of heap element %#lx is already %lu bytes."),
|
|
GetPublicHeapPtr(pvOld),
|
|
dwcbNewSize));
|
|
|
|
#endif
|
|
|
|
*ppvNew = MEMREALLOCATE(pvOld, dwcbNewSize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (*ppvNew)
|
|
{
|
|
/* Bigger or smaller? */
|
|
|
|
if (dwcbNewSize > dwcbOldSize)
|
|
/* Bigger. */
|
|
FillGrownMemory(*ppvNew, dwcbOldSize, dwcbNewSize, MEMSIZE(*ppvNew));
|
|
else
|
|
/* Smaller. */
|
|
FillShrunkenMemory(*ppvNew, dwcbOldSize, dwcbNewSize, MEMSIZE(*ppvNew));
|
|
|
|
ModifyHeapElement(pvOld, *ppvNew, dwcbNewSize);
|
|
|
|
*ppvNew = GetPublicHeapPtr(*ppvNew);
|
|
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(*ppvNew, BYTE, (UINT)dwcbRequestedSize));
|
|
}
|
|
|
|
if (IS_FLAG_SET(MdwMemoryManagerModuleFlags, MEMMGR_DFL_VALIDATE_HEAP_ON_EXIT))
|
|
ASSERT(IsHeapOK());
|
|
|
|
#endif
|
|
|
|
return(*ppvNew != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
** GetMemorySize()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE DWORD GetMemorySize(PVOID pv)
|
|
{
|
|
ASSERT(IsValidHeapPtr(GetPrivateHeapPtr(pv)));
|
|
|
|
return(MEMSIZE(pv));
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** SetMemoryManagerModuleIniSwitches()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL SetMemoryManagerModuleIniSwitches(void)
|
|
{
|
|
BOOL bResult;
|
|
|
|
bResult = SetIniSwitches(MrgcpcvisMemoryManagerModule,
|
|
ARRAY_ELEMENTS(MrgcpcvisMemoryManagerModule));
|
|
|
|
ASSERT(FLAGS_ARE_VALID(MdwMemoryManagerModuleFlags, ALL_MEMMGR_DFLAGS));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** SpewHeapSummary()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void SpewHeapSummary(DWORD dwFlags)
|
|
{
|
|
HEAPSUMMARY hs;
|
|
|
|
ASSERT(FLAGS_ARE_VALID(dwFlags, SHS_FL_SPEW_USED_INFO));
|
|
|
|
AnalyzeHeap(&hs, dwFlags);
|
|
|
|
TRACE_OUT((TEXT("Heap summary: %lu bytes in %lu used elements."),
|
|
hs.dwcbUsedSize,
|
|
hs.ulcUsedElements));
|
|
|
|
return;
|
|
}
|
|
|
|
#endif /* DEBUG */
|