/* * Copyright (c) 1985 - 1999, Microsoft Corporation */ #include "precomp.h" #if DBG WIN32HEAP gWin32Heaps[MAX_HEAPS]; /* * These globals are used to fail gFail allocs and the succeed the next gSucceed * Set with Win32HeapStat */ DWORD gFail, gSucceed; DWORD gToFail, gToSucceed; /* * Support to keep records of pool free */ HEAPRECORD garrFreeHeapRecord[64]; CONST DWORD gdwFreeHeapRecords = ARRAY_SIZE(garrFreeHeapRecord); DWORD gdwFreeHeapRecordCrtIndex; DWORD gdwFreeHeapRecordTotalFrees; #ifdef _USERK_ FAST_MUTEX* gpHeapFastMutex; #define EnterHeapCrit() \ KeEnterCriticalRegion(); \ ExAcquireFastMutexUnsafe(pheap->pFastMutex); #define LeaveHeapCrit() \ ExReleaseFastMutexUnsafe(pheap->pFastMutex); \ KeLeaveCriticalRegion(); #define EnterGlobalHeapCrit() \ KeEnterCriticalRegion(); \ ExAcquireFastMutexUnsafe(gpHeapFastMutex); #define LeaveGlobalHeapCrit() \ ExReleaseFastMutexUnsafe(gpHeapFastMutex); \ KeLeaveCriticalRegion(); #else RTL_CRITICAL_SECTION gheapCritSec; #define EnterHeapCrit() RtlEnterCriticalSection(&pheap->critSec) #define LeaveHeapCrit() RtlLeaveCriticalSection(&pheap->critSec) #define EnterGlobalHeapCrit() RtlEnterCriticalSection(&gheapCritSec) #define LeaveGlobalHeapCrit() RtlLeaveCriticalSection(&gheapCritSec) #endif /***************************************************************************\ * InitWin32HeapStubs * * Initialize heap stub management * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL InitWin32HeapStubs( VOID) { #ifdef _USERK_ gpHeapFastMutex = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), 'yssU'); if (gpHeapFastMutex == NULL) { RIPMSG0(RIP_WARNING, "Fail to create fast mutex for heap allocations"); return FALSE; } ExInitializeFastMutex(gpHeapFastMutex); #else if (!NT_SUCCESS(RtlInitializeCriticalSection(&gheapCritSec))) { RIPMSG0(RIP_WARNING, "Fail to initialize critical section for heap allocations"); return FALSE; } #endif return TRUE; } /***************************************************************************\ * CleanupWin32HeapStubs * * Cleanup heap stub management * * History: * 11/10/98 CLupu Created \***************************************************************************/ VOID CleanupWin32HeapStubs( VOID) { #ifdef _USERK_ if (gpHeapFastMutex != NULL) { ExFreePool(gpHeapFastMutex); gpHeapFastMutex = NULL; } #else RtlDeleteCriticalSection(&gheapCritSec); #endif } /***************************************************************************\ * Win32HeapGetHandle * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ PVOID Win32HeapGetHandle( PWIN32HEAP pheap) { UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); return pheap->heap; } /***************************************************************************\ * Win32HeapCreateTag * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ ULONG Win32HeapCreateTag( PWIN32HEAP pheap, ULONG Flags, PWSTR TagPrefix, PWSTR TagNames) { #ifndef _USERK_ UserAssert(pheap->dwFlags & WIN32_HEAP_INUSE); pheap->dwFlags |= WIN32_HEAP_USE_HM_TAGS; return RtlCreateTagHeap(pheap->heap, Flags, TagPrefix, TagNames); #endif // _USERK_ return 0; UNREFERENCED_PARAMETER(Flags); UNREFERENCED_PARAMETER(TagPrefix); UNREFERENCED_PARAMETER(TagNames); } /***************************************************************************\ * Win32HeapCreate * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ PWIN32HEAP Win32HeapCreate( char* pszHead, char* pszTail, ULONG Flags, PVOID HeapBase, SIZE_T ReserveSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters) { int ind; PWIN32HEAP pheap = NULL; UserAssert(strlen(pszHead) == HEAP_CHECK_SIZE - sizeof(char)); UserAssert(strlen(pszTail) == HEAP_CHECK_SIZE - sizeof(char)); EnterGlobalHeapCrit(); /* * Walk the global array of heaps to get an empty spot */ for (ind = 0; ind < MAX_HEAPS; ind++) { if (gWin32Heaps[ind].dwFlags & WIN32_HEAP_INUSE) continue; /* * Found an empty spot */ break; } if (ind >= MAX_HEAPS) { RIPMSG1(RIP_ERROR, "Too many heaps created %d", ind); goto Exit; } pheap = &gWin32Heaps[ind]; #ifdef _USERK_ /* * Initialize the fast mutex that will protect the memory * allocations for this heap */ pheap->pFastMutex = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), 'yssU'); if (pheap->pFastMutex == NULL) { RIPMSG0(RIP_WARNING, "Fail to create fast mutex for heap allocations"); pheap = NULL; goto Exit; } ExInitializeFastMutex(pheap->pFastMutex); #else /* * Initialize the critical section that will protect the memory * allocations for this heap */ if (!NT_SUCCESS(RtlInitializeCriticalSection(&pheap->critSec))) { RIPMSG0(RIP_WARNING, "Fail to initialize critical section for heap allocations"); pheap = NULL; goto Exit; } #endif /* * Create the heap */ pheap->heap = RtlCreateHeap(Flags, HeapBase, ReserveSize, CommitSize, Lock, Parameters); if (pheap->heap == NULL) { RIPMSG0(RIP_WARNING, "Fail to create heap"); #ifdef _USERK_ ExFreePool(pheap->pFastMutex); pheap->pFastMutex = NULL; #else RtlDeleteCriticalSection(&pheap->critSec); #endif pheap = NULL; goto Exit; } pheap->dwFlags = (WIN32_HEAP_INUSE | WIN32_HEAP_USE_GUARDS); pheap->heapReserveSize = ReserveSize; RtlCopyMemory(pheap->szHead, pszHead, HEAP_CHECK_SIZE); RtlCopyMemory(pheap->szTail, pszTail, HEAP_CHECK_SIZE); Exit: LeaveGlobalHeapCrit(); return pheap; } /***************************************************************************\ * Win32HeapDestroy * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL Win32HeapDestroy( PWIN32HEAP pheap) { UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); EnterGlobalHeapCrit(); /* * Mark the spot available */ pheap->dwFlags = 0; RtlDestroyHeap(pheap->heap); #ifdef _USERK_ ExFreePool(pheap->pFastMutex); pheap->pFastMutex = NULL; #else RtlDeleteCriticalSection(&pheap->critSec); #endif RtlZeroMemory(pheap, sizeof(WIN32HEAP)); LeaveGlobalHeapCrit(); return TRUE; } /***************************************************************************\ * Win32HeapSize * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ SIZE_T Win32HeapSize( PWIN32HEAP pheap, PVOID p) { PDbgHeapHead ph; if (!Win32HeapCheckAlloc(pheap, p)) { return 0; } ph = (PDbgHeapHead)p - 1; return ph->size; } /***************************************************************************\ * Win32HeapAlloc * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ PVOID Win32HeapAlloc( PWIN32HEAP pheap, SIZE_T uSize, DWORD tag, ULONG Flags) { PVOID p = NULL; PDbgHeapHead ph; SIZE_T uRealSize = uSize; #ifdef HEAP_ALLOC_TRACE ULONG hash; #endif EnterHeapCrit(); UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); /* * Fail if this heap has WIN32_HEAP_FAIL_ALLOC set. */ if (pheap->dwFlags & WIN32_HEAP_FAIL_ALLOC) { RIPMSG3(RIP_WARNING, "Heap allocation failed because of global restriction. heap %#p, size 0x%x tag %d", pheap, uSize, tag); goto Exit; } /* * Fail if gToFail is set. */ if (gToFail) { if (--gToFail == 0) { gToSucceed = gSucceed; } RIPMSG3(RIP_WARNING, "Heap allocation failed on temporary restriction. heap %#p, size 0x%x tag %d", pheap, uSize, tag); goto Exit; } if (gToSucceed) { if (--gToSucceed) { gToFail = gFail; } } /* * Calculate the size that we actually are going to allocate. */ uSize += sizeof(DbgHeapHead); if (pheap->dwFlags & WIN32_HEAP_USE_GUARDS) { uSize += sizeof(pheap->szHead) + sizeof(pheap->szTail); } p = RtlAllocateHeap(pheap->heap, Flags, uSize); if (p == NULL) { RIPMSG3(RIP_WARNING, "Heap allocation failed. heap %#p, size 0x%x tag %d", pheap, uSize, tag); goto Exit; } /* * Copy the secure strings in the head and tail of the allocation. */ if (pheap->dwFlags & WIN32_HEAP_USE_GUARDS) { RtlCopyMemory((PBYTE)p, pheap->szHead, sizeof(pheap->szHead)); RtlCopyMemory((PBYTE)p + uSize - sizeof(pheap->szTail), pheap->szTail, sizeof(pheap->szTail)); ph = (PDbgHeapHead)((PBYTE)p + sizeof(pheap->szHead)); } else { ph = (PDbgHeapHead)p; } /* * Zero out the header */ RtlZeroMemory(ph, sizeof(DbgHeapHead)); ph->mark = HEAP_ALLOC_MARK; ph->pheap = pheap; ph->size = uRealSize; ph->tag = tag; #ifdef _USERK_ ph->pid = HandleToUlong(PsGetCurrentProcessId()); #else // !_USERK_ ph->pid = GetCurrentProcessId(); #endif // _USERK_ #ifdef HEAP_ALLOC_TRACE RtlZeroMemory(ph->trace, HEAP_ALLOC_TRACE_SIZE * sizeof(PVOID)); RtlCaptureStackBackTrace( 1, HEAP_ALLOC_TRACE_SIZE, ph->trace, &hash); #endif // HEAP_ALLOC_TRACE /* * Now link it into the list for this tag (if any). */ ph->pPrev = NULL; ph->pNext = pheap->pFirstAlloc; (pheap->crtAllocations)++; pheap->crtMemory += uRealSize; if (pheap->maxAllocations < pheap->crtAllocations) { pheap->maxAllocations = pheap->crtAllocations; } if (pheap->maxMemory < pheap->crtMemory) { pheap->maxMemory = pheap->crtMemory; } if (pheap->pFirstAlloc != NULL) { pheap->pFirstAlloc->pPrev = ph; } pheap->pFirstAlloc = ph; p = (PVOID)(ph + 1); Exit: LeaveHeapCrit(); return p; } /***************************************************************************\ * RecordFreeHeap * * Records free heap * * 3-24-99 CLupu Created. \***************************************************************************/ VOID RecordFreeHeap( PWIN32HEAP pheap, PVOID p, SIZE_T size) { garrFreeHeapRecord[gdwFreeHeapRecordCrtIndex].p = p; garrFreeHeapRecord[gdwFreeHeapRecordCrtIndex].size = size; garrFreeHeapRecord[gdwFreeHeapRecordCrtIndex].pheap = pheap; gdwFreeHeapRecordTotalFrees++; RtlZeroMemory(garrFreeHeapRecord[gdwFreeHeapRecordCrtIndex].trace, RECORD_HEAP_STACK_TRACE_SIZE * sizeof(PVOID)); RtlWalkFrameChain(garrFreeHeapRecord[gdwFreeHeapRecordCrtIndex].trace, RECORD_HEAP_STACK_TRACE_SIZE, 0); gdwFreeHeapRecordCrtIndex++; if (gdwFreeHeapRecordCrtIndex >= gdwFreeHeapRecords) { gdwFreeHeapRecordCrtIndex = 0; } } /***************************************************************************\ * Win32HeapReAlloc * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ PVOID Win32HeapReAlloc( PWIN32HEAP pheap, PVOID p, SIZE_T uSize, ULONG Flags) { PVOID pdest; PDbgHeapHead ph; if (!Win32HeapCheckAlloc(pheap, p)) { return NULL; } ph = (PDbgHeapHead)p - 1; pdest = Win32HeapAlloc(pheap, uSize, ph->tag, Flags); if (pdest != NULL) { /* * If the block is shrinking, don't copy too many bytes. */ if (ph->size < uSize) { uSize = ph->size; } RtlCopyMemory(pdest, p, uSize); Win32HeapFree(pheap, p); } return pdest; } /***************************************************************************\ * Win32HeapFree * * stub routine for Heap management * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL Win32HeapFree( PWIN32HEAP pheap, PVOID p) { PDbgHeapHead ph; SIZE_T uSize; BOOL bRet; EnterHeapCrit(); UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); ph = (PDbgHeapHead)p - 1; /* * Validate always on free */ Win32HeapCheckAlloc(pheap, p); UserAssert((pheap->crtAllocations > 1 && pheap->crtMemory > ph->size) || (pheap->crtAllocations == 1 && pheap->crtMemory == ph->size)); (pheap->crtAllocations)--; pheap->crtMemory -= ph->size; /* * now, remove it from the linked list */ if (ph->pPrev == NULL) { if (ph->pNext == NULL) { pheap->pFirstAlloc = NULL; } else { ph->pNext->pPrev = NULL; pheap->pFirstAlloc = ph->pNext; } } else { ph->pPrev->pNext = ph->pNext; if (ph->pNext != NULL) { ph->pNext->pPrev = ph->pPrev; } } uSize = ph->size + sizeof(DbgHeapHead); if (pheap->dwFlags & WIN32_HEAP_USE_GUARDS) { p = (PVOID)((BYTE*)ph - sizeof(pheap->szHead)); uSize += sizeof(pheap->szHead) + sizeof(pheap->szTail); } else { p = (PVOID)ph; } RecordFreeHeap(pheap, p, ph->size); /* * Fill the allocation with a pattern that we can recognize * after the free takes place. */ RtlFillMemoryUlong(p, uSize, (0xCACA0000 | ph->tag)); /* * Free the allocation */ bRet = RtlFreeHeap(pheap->heap, 0, p); LeaveHeapCrit(); return bRet; } /***************************************************************************\ * Win32HeapCheckAlloc * * validates heap allocations in a heap * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL Win32HeapCheckAllocHeader( PWIN32HEAP pheap, PDbgHeapHead ph) { PBYTE pb; if (ph == NULL) { RIPMSG0(RIP_ERROR, "NULL pointer passed to Win32HeapCheckAllocHeader"); return FALSE; } UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); /* * Make sure it's one of our heap allocations */ if (ph->mark != HEAP_ALLOC_MARK) { RIPMSG2(RIP_ERROR, "%#p invalid heap allocation for pheap %#p", ph, pheap); return FALSE; } /* * Make sure it belongs to this heap */ if (ph->pheap != pheap) { RIPMSG3(RIP_ERROR, "%#p heap allocation for heap %#p belongs to a different heap %#p", ph, pheap, ph->pheap); return FALSE; } if (pheap->dwFlags & WIN32_HEAP_USE_GUARDS) { pb = (BYTE*)ph - sizeof(pheap->szHead); if (!RtlEqualMemory(pb, pheap->szHead, sizeof(pheap->szHead))) { RIPMSG2(RIP_ERROR, "head corrupted for heap %#p allocation %#p", pheap, ph); return FALSE; } pb = (BYTE*)ph + ph->size + sizeof(*ph); if (!RtlEqualMemory(pb, pheap->szTail, sizeof(pheap->szTail))) { RIPMSG2(RIP_ERROR, "tail corrupted for heap %#p allocation %#p", pheap, ph); return FALSE; } } return TRUE; } /***************************************************************************\ * Win32HeapCheckAlloc * * validates heap allocations in a heap * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL Win32HeapCheckAlloc( PWIN32HEAP pheap, PVOID p) { PDbgHeapHead ph; UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); if (p == NULL) { RIPMSG0(RIP_ERROR, "NULL pointer passed to Win32HeapCheckAlloc"); return FALSE; } ph = (PDbgHeapHead)p - 1; return Win32HeapCheckAllocHeader(pheap, ph); } /***************************************************************************\ * Win32HeapValidate * * validates all the heap allocations in a heap * * History: * 11/10/98 CLupu Created \***************************************************************************/ BOOL Win32HeapValidate( PWIN32HEAP pheap) { PDbgHeapHead ph; UserAssert(pheap != NULL && (pheap->dwFlags & WIN32_HEAP_INUSE)); EnterHeapCrit(); ph = pheap->pFirstAlloc; while (ph != NULL) { Win32HeapCheckAllocHeader(pheap, ph); ph = ph->pNext; } LeaveHeapCrit(); #ifndef _USERK_ return RtlValidateHeap(pheap->heap, 0, NULL); #else return TRUE; #endif } /***************************************************************************\ * Win32HeapDump * * dump heap allocations in a heap * * History: * 11/10/98 CLupu Created \***************************************************************************/ VOID Win32HeapDump( PWIN32HEAP pheap) { PDbgHeapHead pAlloc = pheap->pFirstAlloc; if (pAlloc == NULL) { return; } UserAssert(pAlloc->pPrev == NULL); RIPMSG1(RIP_WARNING, "-- Dumping heap allocations for heap %#p... --", pheap); while (pAlloc != NULL) { DbgPrint("tag %04d size %08d\n", pAlloc->tag, pAlloc->size); pAlloc = pAlloc->pNext; } RIPMSG0(RIP_WARNING, "--- End Dump ---"); } /***************************************************************************\ * Win32HeapFailAllocations * * fails the heap allocations * * History: * 11/10/98 CLupu Created \***************************************************************************/ VOID Win32HeapFailAllocations( BOOL bFail) { PWIN32HEAP pheap; int ind; EnterGlobalHeapCrit(); for (ind = 0; ind < MAX_HEAPS; ind++) { pheap = &gWin32Heaps[ind]; if (!(pheap->dwFlags & WIN32_HEAP_INUSE)) continue; EnterHeapCrit(); if (bFail) { pheap->dwFlags |= WIN32_HEAP_FAIL_ALLOC; RIPMSG1(RIP_WARNING, "Heap allocations for heap %#p will fail !", pheap); } else { pheap->dwFlags &= ~WIN32_HEAP_FAIL_ALLOC; } LeaveHeapCrit(); } LeaveGlobalHeapCrit(); } /***************************************************************************\ * Win32HeapStat * * Retrieves the heap statistics. * dwLen is the size of phs in bytes. If there are more tags in this module * than what the caller requested, a higher buffer is asked for by returning * the total number of tags in use. * winsrv uses the MAKE_TAG macro to construct their tags, so it needs to pass * TRUE for bNeedTagShift * * If there is only one structure passed in, the phs->dwSize will hold gXFail * and phs->dwCount will hold gYFail to be used to fail gXFail allocs and then * succeed the next gYFail allocations. * * History: * 02/25/99 MCostea Created \***************************************************************************/ DWORD Win32HeapStat( PDBGHEAPSTAT phs, DWORD dwLen, BOOL bNeedTagShift) { PWIN32HEAP pheap; PDbgHeapHead pAlloc; UINT ind, maxTagExpected, maxTag, currentTag; /* * We need at least one structure to do anything */ if (dwLen < sizeof(DBGHEAPSTAT)) { return 0; } if (dwLen == sizeof(DBGHEAPSTAT)) { /* * This call is actually intended to set the gXFail and gYFail parameters */ gFail = phs->dwSize; gSucceed = phs->dwCount; gToFail = gFail; return 1; } RtlZeroMemory(phs, dwLen); maxTagExpected = dwLen/sizeof(DBGHEAPSTAT) - 1; maxTag = 0; EnterGlobalHeapCrit(); for (ind = 0; ind < MAX_HEAPS; ind++) { pheap = &gWin32Heaps[ind]; if (!(pheap->dwFlags & WIN32_HEAP_INUSE)) continue; EnterHeapCrit(); pAlloc = pheap->pFirstAlloc; while (pAlloc != NULL) { currentTag = pAlloc->tag; if (bNeedTagShift) { currentTag >>= HEAP_TAG_SHIFT; } if (maxTag < currentTag) { maxTag = currentTag; } if (currentTag <= maxTagExpected) { phs[currentTag].dwSize += (DWORD)(pAlloc->size); phs[currentTag].dwCount++; } pAlloc = pAlloc->pNext; } LeaveHeapCrit(); } LeaveGlobalHeapCrit(); /* * Now fill the dwTag for tags that have allocations */ for (ind = 0; ind < maxTagExpected; ind++) { if (phs[ind].dwCount) { phs[ind].dwTag = ind; } } return maxTag; } #endif // DBG