// LMEM.C // // (C) Copyright Microsoft Corp., 1988-1994 // // Win32 wrappers for heap functions (Local* and some Heap*) // // Origin: // // Change history: // // Date Who Description // --------- --------- ------------------------------------------------- // BrianSm Local* and Heap* APIs // AtsushiK Toolhelp // 15-Feb-94 JonT Code cleanup and precompiled headers #include #pragma hdrstop("kernel32.pch") #include #define GACF_HEAPSLACK 0x400000 // Copied from windows.h (16-bit) SetFile(); /* * Structure and equates for LocalAlloc handle management. Some things * to remember: * * When a handle is returned to the user, we really pass him the address * of the lh_pdata field because some bad apps like Excel just dereference the * handle to find the pointer, rather than call LocalLock. * * It is important that the handle value returned also be word aligned but not * dword aligned (ending in a 2,6,a, or e). We use the 0x2 bit to detect * that a value is a handle and not a pointer (which will always be dword * aligned). * * If the data block get discarded, the lh_pdata field will be set to 0. * * Free handles are kept on a free list linked through the lh_freelink * field which overlays some other fields. You can tell if a handle is free * and has a valid freelink by checking that lh_sig == LH_FREESIG * * The handles themselves are kept in heap blocks layed out as a * lharray_s. We link these blocks on a per-process list so that * the heap-walking functions can enumerate them. */ #pragma pack(1) struct lhandle_s { unsigned short lh_signature; /* signature (LH_BUSYSIG or LH_FREESIG)*/ void *lh_pdata; /* pointer to data for heap block */ unsigned char lh_flags; /* flags (LH_DISCARDABLE) */ unsigned char lh_clock; /* lock count */ }; #define lh_freelink lh_pdata /* free list overlays first field */ /* if LH_FREE is set in lh_flags */ #define LH_BUSYSIG 'SB' /* signature for allocated handle */ #define LH_FREESIG 'SF' /* signature for free handle */ #define LH_DISCARDABLE 0x02 /* lh_flags value for discardable mem */ #define LH_CLOCKMAX 0xff /* maximum possible lock count */ #define LH_HANDLEBIT 2 /* bit that is set on handles but not */ /* pointers */ #define CLHGROW 8 #define CBLHGROW (sizeof(struct lhandle_s) * CLHGROW) struct lharray_s { unsigned short lha_signature; /* signature (LHA_SIG) */ unsigned short lha_membercount; /* position in linked list (for detecting loops) */ struct lharray_s *lha_next; /* ptr to next lharray_s */ //!!! This array *must* be dword aligned so that the handles will be // *not* dword-aligned. struct lhandle_s lha_lh[CLHGROW]; }; #define LHA_SIGNATURE 'AL' /* signature for lhaarray_s blocks */ #define TH32_MEMBUFFERSIZE (max(CBLHGROW,1024)) // A pointer to this private block of state info is kept in the dwResvd // field of the HEAPENTRY32 structure. typedef struct { CRST *pcrst; // Pointer to critical section (unencoded) // !!! pcrst must be the first field!!! PDB *ppdb; // PDB of process HHEAP hHeap; // Real Heap handle DWORD lpbMin; // Lowest allowed address for a heap block DWORD nlocalHnd; // # of lhandle_s structures allocated in heap struct heapinfo_s hi; // Snapshot of heapinfo_s DWORD nSuppAvail; // size of lpdwSuppress array in dwords DWORD nSuppUsed; // # of lpdwSuppress array dwords used. DWORD *lpdwSuppress; // Either NULL or a pointer to a NULL-terminated // array of heap blocks to suppress. DWORD dwMode; // Current mode DWORD nNextLH; // 0 based index of next lhandle to read in curlha (THM_LHANDLES) DWORD lpHBlock; // Address of next heap block to read (THM_FIXEDHANDLES) DWORD dwBlkAddr; // Address of start of block data DWORD dwBlkSize; // Size of heap block (including header) DWORD dwBlkFlags; // HP_ flags. DWORD curlhaaddr; // Actual base address of curlha. struct lharray_s curlha; // Snapshot of current lharray_s } THSTATE, *LPTHSTATE; #define THM_INIT 0 //Init state #define THM_LHANDLES 1 //Next object is an lhandle #define THM_FIXEDHANDLES 2 //Next object is a fixed handle #define THM_DONE 3 //Normal end #define THM_ERROR 4 //Found heap error in previous advance /* * these externs are needed to know whether we should destroy or dispose heap * critical sections */ extern HANDLE hheapKernel; /* heap handle for the kernel heap */ VOID APIENTRY MakeCriticalSectionGlobal( LPCRITICAL_SECTION lpcsCriticalSection ); /* * The HP_* flags and LMEM_* flags should be interchangeable */ #if ((HP_ZEROINIT - LMEM_ZEROINIT) || (HP_MOVEABLE - LMEM_MOVEABLE) || (HP_FIXED - LMEM_FIXED)) # error Equates busted #endif extern ULONG INTERNAL VerifyOnHeap(HHEAP hheap, PVOID p); extern KERNENTRY HouseCleanLogicallyDeadHandles(VOID); extern BOOL KERNENTRY ReadProcessMemoryFromPDB(PPDB ppdb, LPVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); extern DWORD KERNENTRY GetAppCompatFlags(VOID); extern HANDLE _GetProcessHeap(void); /* Utility function to check the local memory handle */ BOOL _IsValidHandle(HANDLE hMem) { BOOL bRet = FALSE; struct lhandle_s *plh; plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); /* * Do our own little parameter validation here because the normal * validation layer can't handle the odd-ball error return of hMem */ { volatile UCHAR tryerror = 0; _try { tryerror &= (plh->lh_clock + (UCHAR)plh->lh_signature); } _except (EXCEPTION_EXECUTE_HANDLER) { tryerror = 1; } if (tryerror) { goto error; } } if ((plh->lh_signature != LH_BUSYSIG) && (plh->lh_signature != LH_FREESIG)){ goto error; } // Set the return value to TRUE bRet = TRUE; error: return bRet; } /* Utility function to check whether the passed memory is in the memory range. Uses VerifyOnHeap function. */ BOOL _IsOnOurHeap(LPCVOID lpMem) { HANDLE hHeap = _GetProcessHeap(); return (VerifyOnHeap(hHeap, (PVOID)lpMem)); } /* Utility function to check the local memory handle and the memory range. Uses VerifyOnHeap function. */ BOOL _IsOurLocalHeap(HANDLE hMem) { BOOL bRet = FALSE; HANDLE hHeap = _GetProcessHeap(); if ((ULONG)hMem & LH_HANDLEBIT) { // This is a handle bRet = (VerifyOnHeap(hHeap, hMem)) && (_IsValidHandle(hMem)); } else { bRet = VerifyOnHeap(hHeap, hMem); } return bRet; } /***EP LocalAllocNG - allocate a block from the current process's default heap * * ENTRY: flags - LMEM_FIXED, LMEM_MOVEABLE, LMEM_DISCARDABLE, LMEM_ZEROINIT * dwBytes - counts of bytes to allocate * EXIT: flat pointer to block allocated, or 0 if failure * * Special entry point used by the handle-grouping code to avoid unwanted * recursion. */ HANDLE APIENTRY LocalAllocNG(UINT dwFlags, UINT dwBytes) { void *pmem; struct lhandle_s *plh; struct lhandle_s *plhend; dwFlags &= ~( ((DWORD)GMEM_DDESHARE) | ((DWORD)GMEM_NOTIFY) | ((DWORD)GMEM_NOT_BANKED) ); /* * Enter the heap critical section which serializes access to the handle * tables as well as the heap. */ hpEnterCriticalSection(((*pppdbCur)->hheapLocal)); /* * Make sure there are no extra flags */ if (dwFlags & ~(LMEM_MOVEABLE | LMEM_DISCARDABLE | LMEM_ZEROINIT | LMEM_NOCOMPACT | LMEM_NODISCARD)) { mmError(ERROR_INVALID_PARAMETER, "LocalAlloc: invalid flags\n"); goto error; } /* * If they want moveable memory, adjust dwBytes to leave room for a back * pointer to the handle structure and allocate a handle structure. */ if (dwFlags & LMEM_MOVEABLE) { /* * Allocate a handle structure. If there aren't any on the free * list, allocate another block of memory to hold some more handles. */ if ((*pppdbCur)->plhFree == 0) { struct lharray_s *plha; if ((plha = HPAlloc((HHEAP)(*pppdbCur)->hheapLocal, sizeof(struct lharray_s), HP_NOSERIALIZE)) == 0) { goto error; } plha->lha_signature = LHA_SIGNATURE; plha->lha_membercount = (*pppdbCur)->plhBlock ? (*pppdbCur)->plhBlock->lha_membercount + 1 : 0; plh = &(plha->lha_lh[0]); /* * If the allocation worked, put the handle structures on the free * list and null terminate the list. Actually, we put all of the * new blocks on the list but one, who is the guy we are trying * to allocate (he will be in plh when we are done). */ (*pppdbCur)->plhFree = plh; for (plhend = plh + CLHGROW - 1; plh < plhend; plh++) { plh->lh_freelink = plh + 1; plh->lh_signature = LH_FREESIG; } (plh-1)->lh_freelink = 0; plha->lha_next = (*pppdbCur)->plhBlock; (*pppdbCur)->plhBlock = plha; /* * If there is something on the free list, just take the guy off of it */ } else { plh = (*pppdbCur)->plhFree; mmAssert(plh->lh_signature == LH_FREESIG, "LocalAlloc: bad handle free list 2\n"); (*pppdbCur)->plhFree = plh->lh_freelink; } /* * Initialize the handle structure */ plh->lh_clock = 0; plh->lh_signature = LH_BUSYSIG; plh->lh_flags = (dwFlags & LMEM_DISCARDABLE) ? LH_DISCARDABLE : 0; /* * Now actually allocate the memory unless the caller wanted the * block initially discarded (dwBytes == 0) */ if (dwBytes != 0) { /* * Need to check for wacky size here to make sure adding on * the 4 bytes below to the size doesn't bring it from negative * to positive. */ if (dwBytes > hpMAXALLOC) { mmError(ERROR_NOT_ENOUGH_MEMORY, "LocalAlloc: requested size too big\n"); goto errorfreehandle; } if ((pmem = HPAlloc((HHEAP)(*pppdbCur)->hheapLocal, dwBytes+sizeof(struct lhandle_s *), dwFlags | HP_NOSERIALIZE)) == 0) { goto errorfreehandle; } plh->lh_pdata = (char *)pmem + sizeof(struct lhandle_s *); /* * Initialize the back pointer to the handle structure at the * front of the data block. */ *((struct lhandle_s **)pmem) = plh; } else { plh->lh_pdata = 0; } /* * Set "pmem" (the return value) to the lh_pdata field in the * handle structure. * * When a handle is returned to the user, we really pass him the address * of the lh_pdata field because some bad apps like Excel just dereference the * handle to find the pointer, rather than call LocalLock. * * It is important that the handle value returned also be word aligned but not * dword aligned (ending in a 2,6,a, or e). We use the 0x2 bit to detect * that a value is a handle and not a pointer (which will always be dword * aligned). */ pmem = &plh->lh_pdata; mmAssert(((ULONG)pmem & LH_HANDLEBIT), "LocalAlloc: handle value w/o LH_HANDLEBIT set\n"); /* * For fixed memory, just allocate the sucker */ } else { if ((pmem = HPAlloc((HHEAP)(*pppdbCur)->hheapLocal, dwBytes, dwFlags | HP_NOSERIALIZE)) == 0) { goto errorfreehandle; } mmAssert(((ULONG)pmem & LH_HANDLEBIT) == 0, "LocalAlloc: pointer value w/ LH_HANDLEBIT set\n"); } exit: hpLeaveCriticalSection(((*pppdbCur)->hheapLocal)); return(pmem); /* * Error paths. */ errorfreehandle: if (dwFlags & LMEM_MOVEABLE) { plh->lh_freelink = (*pppdbCur)->plhFree; (*pppdbCur)->plhFree = plh; plh->lh_signature = LH_FREESIG; } error: pmem = 0; goto exit; } /***EP LocalReAlloc - resize a memory block on the default heap * * ENTRY: hMem - pointer to block to resize * dwBytes - new size requested * dwFlags - LMEM_MOVEABLE: ok to move the block if needed * EXIT: flat pointer to resized block, or 0 if failure * */ HANDLE APIENTRY LocalReAlloc(HANDLE hMem, UINT dwBytes, UINT dwFlags) { struct heapinfo_s *hheap; struct lhandle_s *plh; void *pmem; dwFlags &= ~((DWORD)GMEM_DDESHARE); HouseCleanLogicallyDeadHandles(); hheap = (*pppdbCur)->hheapLocal; /* * Enter the heap critical section which serializes access to the handle * tables as well as the heap. */ hpEnterCriticalSection(hheap); /* * Make sure there are no extra flags */ if ((dwFlags & ~(LMEM_MOVEABLE | LMEM_DISCARDABLE | LMEM_ZEROINIT | LMEM_NOCOMPACT | LMEM_MODIFY)) || ((dwFlags & LMEM_DISCARDABLE) && (dwFlags & LMEM_MODIFY) == 0)) { mmError(ERROR_INVALID_PARAMETER, "LocalReAlloc: invalid flags\n"); goto error; } /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * The handle value is aligned like a handle, but is it really one? * Verify it by making sure it is within the address range of the heap * and that it's signature is set right. HPReAlloc will verify things * more by checking that the pmem is valid. */ if (VerifyOnHeap(hheap, hMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalReAlloc: hMem out of range\n"); goto error; } plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalReAlloc: invalid hMem, bad signature\n"); goto error; } pmem = (char *)plh->lh_pdata - sizeof(struct lhandle_s *); /* * If the caller just wanted to change the flags for the block, * do it here. */ if (dwFlags & LMEM_MODIFY) { plh->lh_flags &= ~LH_DISCARDABLE; plh->lh_flags |= (dwFlags & LMEM_DISCARDABLE) ? LH_DISCARDABLE : 0; /* * If someone wants to realloc the block to size 0 (meaning discard the * sucker) do so here. For discarding, we free the actual heap block * and store null in the lh_pdata field. */ } else if (dwBytes == 0) { /* * If the lock count is not zero, you aren't allow to discard */ if (plh->lh_clock != 0) { mmError(ERROR_INVALID_HANDLE, "LocalReAlloc: discard of locked block\n"); goto error; } /* * Don't bother discarding the block if it is already discarded */ if (plh->lh_pdata != 0) { if (HeapFree(hheap, HP_NOSERIALIZE, pmem) == 0) { goto error; } plh->lh_pdata = 0; } /* * If we get here, the caller actually wanted to reallocate the block */ } else { dwBytes += sizeof(struct lhandle_s *); /* * If the block is currently discarded, then we need to allocate * a new memory chunk for it, otherwise, do a realloc */ if (plh->lh_pdata == 0) { if (dwBytes != 0) { if ((pmem = HPAlloc(hheap, dwBytes, dwFlags | HP_NOSERIALIZE)) == 0) { goto error; } *((struct lhandle_s **)pmem) = plh; } } else { if (plh->lh_clock == 0) { dwFlags |= LMEM_MOVEABLE; } if ((pmem = HPReAlloc(hheap, pmem, dwBytes, dwFlags | HP_NOSERIALIZE)) == 0) { goto error; } } /* * Update the lh_pdata field in the handle to point to the new * memory. */ plh->lh_pdata = (char *)pmem + sizeof(struct lhandle_s *); } /* * The caller did not pass in a handle. Treat the value as a pointer. * HPReAlloc will do parameter validation on it. */ } else if ((dwFlags & LMEM_MODIFY) == 0) { hMem = HPReAlloc(hheap, hMem, dwBytes, dwFlags | HP_NOSERIALIZE); } else { mmError(ERROR_INVALID_PARAMETER, "LocalReAlloc: can't use LMEM_MODIFY on fixed block\n"); goto error; } exit: hpLeaveCriticalSection(hheap); return(hMem); error: hMem = 0; goto exit; } /***EP LocalLock - lock a local memory handle on the default heap * * ENTRY: hMem - handle to block * EXIT: flat pointer to block or 0 if error */ LPVOID APIENTRY LocalLock(HANDLE hMem) { LPSTR pmem; struct heapinfo_s *hheap; struct lhandle_s *plh; hheap = (*pppdbCur)->hheapLocal; hpEnterCriticalSection(hheap); /* * Verify hMem is within the address range of the heap */ if (VerifyOnHeap(hheap, hMem) == 0) { /* * We don't want this error to break into the debugger by default * user can call this with random address in some dialog routine * that it doesn't know if it has a handle or a pointer */ DebugOut((DEB_WARN, "LocalLock: hMem out of range")); SetError(ERROR_INVALID_HANDLE); // mmError(ERROR_INVALID_HANDLE, "LocalLock: hMem out of range\n"); goto error; } /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * The handle value is aligned like a handle, but is it really one? * Verify it by checking the signature. */ plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalLock: invalid hMem, bad signature\n"); goto error; } /* * Increment the lock count unless we are already at the max */ #ifdef HPDEBUG if (plh->lh_clock == LH_CLOCKMAX - 1) { dprintf(("LocalLock: lock count overflow, handle cannot be unlocked\n")); } #endif if (plh->lh_clock != LH_CLOCKMAX) { plh->lh_clock++; } pmem = plh->lh_pdata; /* * If the hMem passed in isn't a handle, it is supposed to be the * base address of a fixed block. We should validate that more, but NT * doesn't and I would hate to be incompatible. So instead, just * return the parameter except for the obvious error case of the block * being free. */ } else { if (hpIsFreeSignatureValid((struct freeheap_s *) (((struct busyheap_s *)hMem) - 1))) { mmError(ERROR_INVALID_HANDLE, "LocalLock: hMem is pointer to free block\n"); goto error; } pmem = hMem; } exit: hpLeaveCriticalSection(hheap); return(pmem); error: pmem = 0; goto exit; } /*** LocalCompact - obsolete function * * ENTRY: uMinFree - ignored * EXIT: 0 */ UINT APIENTRY LocalCompact(UINT uMinFree) { return(0); } /*** LocalShrink - obsolete function * * ENTRY: hMem - ignored * cbNewSize - ignored * EXIT: reserved size of the local heap */ UINT APIENTRY LocalShrink(HANDLE hMem, UINT cbNewSize) { return((*pppdbCur)->hheapLocal->hi_cbreserve); } /*** LocalUnlock - unlock a local memory handle on the default heap * * ENTRY: hMem - handle to block * EXIT: 0 if unlocked or 1 is still locked */ BOOL APIENTRY LocalUnlock(HANDLE hMem) { struct lhandle_s *plh; struct heapinfo_s *hheap; BOOL rc = 0; hheap = (*pppdbCur)->hheapLocal; hpEnterCriticalSection(hheap); /* * Verify hMem is within the address range of the heap */ if (VerifyOnHeap(hheap, hMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalUnlock: hMem out of range\n"); goto exit; } /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * Validate handle signature */ plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalUnlock: invalid hMem, bad signature\n"); goto exit; } /* * Decrement the lock count unless we are at the max */ if (plh->lh_clock != LH_CLOCKMAX) { if (plh->lh_clock == 0) { /* * Just do a DebugOut since this is not an error per se, * though it probably indicates a bug in the app. */ DebugOut((DEB_WARN, "LocalUnlock: not locked")); goto exit; } if (--plh->lh_clock != 0) { rc++; } } } exit: hpLeaveCriticalSection(hheap); return(rc); } /*** LocalSize - return the size of a memory block on the default heap * * ENTRY: hMem - handle (pointer) to block * EXIT: size in bytesof the block (not including header) or 0 if error */ UINT APIENTRY LocalSize(HANDLE hMem) { struct heapinfo_s *hheap; struct lhandle_s *plh; DWORD rc = 0; DWORD delta = 0; hheap = (*pppdbCur)->hheapLocal; hpEnterCriticalSection(hheap); /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * Verify hMem is within the address range of the heap */ if (VerifyOnHeap(hheap, hMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalSize: hMem out of range\n"); goto error; } /* * Validate handle signature */ plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalSize: invalid hMem, bad signature\n"); goto error; } /* * Discarded handles have no size */ if (plh->lh_pdata == 0) { goto error; } /* * Load up hMem with pointer to data for HeapSize call below */ delta = sizeof(struct lhandle_s *); hMem = (char *)plh->lh_pdata - sizeof(struct lhandle_s *); } /* * Either this is a fixed block or we just loaded up the data address * above if it was moveable. Call HeapSize to do the real work. */ rc = HeapSize(hheap, HP_NOSERIALIZE, hMem); /* * If this was a moveable block, subtract the 4 bytes for the back pointer */ rc -= delta; exit: hpLeaveCriticalSection(hheap); return(rc); error: rc = 0; goto exit; } /*** LocalFlags - return the flags and lock count of block of def heap * * ENTRY: hMem - handle (pointer) to block on default heap * EXIT: flags in high 3 bytes, lock count in low byte (always 1) */ UINT APIENTRY LocalFlags(HANDLE hMem) { struct heapinfo_s *hheap; struct lhandle_s *plh; DWORD rc = LMEM_INVALID_HANDLE; hheap = (*pppdbCur)->hheapLocal; hpEnterCriticalSection(hheap); /* * Verify hMem is within the address range of the heap */ if (VerifyOnHeap(hheap, hMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalFlags: hMem out of range\n"); goto exit; } /* * We have to do our own pointer validation because the normal validation * layer doesn't support returning LMEM_INVALID_HANDLE for errors. */ _try { /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * Validate handle signature */ plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalFlags: invalid hMem, bad signature\n"); } else { rc = (ULONG)plh->lh_clock; if (plh->lh_pdata == 0) { rc |= LMEM_DISCARDED; } if (plh->lh_flags & LH_DISCARDABLE) { rc |= LMEM_DISCARDABLE; } } /* * For fixed blocks, validate the signature. NT always returns * 0 for most fixed-like values even if they aren't really * the start of blocks. If this causes an incompatibility we * can change this later. */ } else { if (hpIsBusySignatureValid(((struct busyheap_s *)hMem) - 1)) { rc = 0; } else { mmError(ERROR_INVALID_HANDLE, "LocalFlags: invalid hMem\n"); } } } _except (EXCEPTION_EXECUTE_HANDLER) { mmError(ERROR_INVALID_HANDLE, "LocalFlags: bad hMem"); } exit: hpLeaveCriticalSection(hheap); return(rc); } /*** LocalHandle - return the handle for a block given its start address * * ENTRY: pMem - pointer to block on default heap * EXIT: handle for the block */ HANDLE APIENTRY LocalHandle(PVOID pMem) { struct heapinfo_s *hheap; struct busyheap_s *pbh; unsigned long prevdword; struct lhandle_s *plh; HANDLE rc; hheap = (*pppdbCur)->hheapLocal; hpEnterCriticalSection(hheap); /* * Verify pMem is within the address range of the heap and aligned like * a heap block should be. */ if (VerifyOnHeap(hheap, pMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalHandle: pMem out of range\n"); goto error; } /* * Figure out if this is a moveable block by seeing if the previous * dword points back to a handle. */ prevdword = *(((unsigned long *)pMem) - 1); if (VerifyOnHeap(hheap, (PVOID)prevdword) != 0) { if (((struct lhandle_s *)prevdword)->lh_signature == LH_BUSYSIG) { /* * This sure looks like a moveable block with a handle. Return it. */ rc = (HANDLE)(prevdword + LH_HANDLEBIT); goto exit; } } /* * Did they pass in a Handle??? */ if ((ULONG)pMem & LH_HANDLEBIT) { plh = (struct lhandle_s *)((char *)pMem - LH_HANDLEBIT); if (plh->lh_signature == LH_BUSYSIG) { rc = (HANDLE)pMem; SetError(ERROR_INVALID_HANDLE); /* NT Compat */ goto exit; } } /* * If we get to here, the block is not preceded by a handle back pointer. * So either it is an invalid address or a fixed block. */ pbh = (struct busyheap_s *)pMem - 1; if (hpIsBusySignatureValid(pbh) == 0) { /* * Not a heap block. Return error. */ mmError(ERROR_INVALID_HANDLE, "LocalHandle: address not a heap block\n"); goto error; /* * If we get here, we passed all the tests. Looks like we have a fixed * heap block, so just return the pointer as the handle. */ } else { rc = pMem; } exit: hpLeaveCriticalSection(hheap); return(rc); error: rc = 0; goto exit; } extern WINBASEAPI BOOL WINAPI vHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); /***EP LocalFreeNG - free a block on the default heap * * ENTRY: hMem - handle (pointer) to block to free * EXIT: NULL if success, else hMem if failure * * Special entry point used by the handle-grouping code to avoid unwanted * recursion. */ HANDLE APIENTRY LocalFreeNG(HANDLE hMem) { struct heapinfo_s *hheap; struct lhandle_s *plh; void *pmem; /* * The spec says to ignore null pointers */ if (hMem == 0) { goto exit; } hheap = (*pppdbCur)->hheapLocal; /* * Enter the heap critical section which serializes access to the handle * tables as well as the heap. */ hpEnterCriticalSection(hheap); /* * Figure out if this is a handle by checking if the adress is aligned * in the right (wrong) way. */ if ((ULONG)hMem & LH_HANDLEBIT) { /* * The handle value is aligned like a handle, but is it really one? * Verify it by making sure it is within the address range of the heap * and that it's signature is set right. HeapFree will verify things * more by checking that the pmem is valid. */ if (VerifyOnHeap(hheap, hMem) == 0) { mmError(ERROR_INVALID_HANDLE, "LocalFree: hMem out of range\n"); goto error; } plh = (struct lhandle_s *)((char *)hMem - LH_HANDLEBIT); /* * Do our own little parameter validation here because the normal * validation layer can't handle the odd-ball error return of hMem */ { volatile UCHAR tryerror = 0; _try { tryerror &= (plh->lh_clock + (UCHAR)plh->lh_signature); } _except (EXCEPTION_EXECUTE_HANDLER) { tryerror = 1; } if (tryerror) { mmError(ERROR_INVALID_HANDLE, "LocalFree: invalid handle"); goto error; } } if (plh->lh_signature != LH_BUSYSIG) { mmError(ERROR_INVALID_HANDLE, "LocalFree: invalid hMem, bad signature\n"); goto error; } /* * You can't free a locked block */ // Commenting out to keep MFC apps from ripping under debug. // Not that I'm a fan of shooting the messenger, but this particular // case seems to happen a lot because of the way Win3.x defined // GlobalLock. See Win95C:#12103 for the non-technical reasons for // this being a pri-1. // #if 0 #ifdef HPDEBUG if (plh->lh_clock) { mmError(ERROR_INVALID_HANDLE, "LocalFree: locked\n"); } #endif #endif /* * Don't bother freeing the block if it is already discarded. * When freeing we zero out the back pointer to the handle so * we don't get confused if someone tried to free a block twice. */ if (plh->lh_pdata != 0) { pmem = (char *)plh->lh_pdata - sizeof(struct lhandle_s *); /* * Under some conditions with Office, this pointer can get trashed. We * need to make sure we don't AV */ if (!IsBadWritePtr(pmem, sizeof(unsigned long))) { *((unsigned long *)pmem) = 0; if (HeapFree(hheap, HP_NOSERIALIZE, pmem) == 0) { goto error; } } } /* * Now free the handle structure and we are done. */ plh->lh_freelink = (*pppdbCur)->plhFree; (*pppdbCur)->plhFree = plh; plh->lh_signature = LH_FREESIG; /* * The caller did not pass in a handle. Treat the value as a pointer. * HeapFree will do parameter validation on it. */ } else { if (vHeapFree(hheap, HP_NOSERIALIZE, hMem) == 0) { goto error; } } hMem = 0; /* success */ error: hpLeaveCriticalSection(hheap); exit: return(hMem); } /***EP HeapCreate - initialize a memory block as a flat heap * * ENTRY: flOptions - HEAP_NO_SERIALIZE: don't serialize access within process * (caller MUST) * HEAP_LOCKED: make memory fixed * HEAP_SHARED: put it in shared arena * dwInitialSize - initial committed memory in heap * dwMaximumSize - reserved size of heap memory * EXIT: handle to new heap, or 0 if error */ HANDLE APIENTRY HeapCreate(DWORD flOptions, DWORD dwInitialSize, DWORD dwMaximumSize) { char *pmem; ULONG rc = 0; /* assume failure */ /* * Don't allowed shared heaps - this only works on Win9x because there is a shared arena. */ if (flOptions & HEAP_SHARED) { flOptions &= ~HEAP_SHARED; } /* * Although we don't really use InitialSize any more (except in growable * heaps) we should still enforce its sanity so apps don't get lazy */ if (dwInitialSize > dwMaximumSize && dwMaximumSize != 0) { mmError(ERROR_INVALID_PARAMETER, "HeapCreate: dwInitialSize > dwMaximumSize\n"); goto exit; } /* * Round the sizes up to the nearest page boundary */ dwMaximumSize = (dwMaximumSize + PAGEMASK) & ~PAGEMASK; /* * A maximum size of 0 means growable. Start him out with 1meg, but allow * more. */ if (dwMaximumSize == 0) { flOptions |= HP_GROWABLE; dwMaximumSize = 1*1024*1024 + (dwInitialSize & ~PAGEMASK); } /* * Allocate memory for the heap. Use PageCommit etc... rather than * VirtualAlloc for committing so we don't get zero-initialized stuff * and also we can commit fixed pages and reserve shared memory. */ if (((ULONG)pmem = PageReserve((flOptions & HEAP_SHARED) ? PR_SHARED : PR_PRIVATE, dwMaximumSize / PAGESIZE, PR_STATIC | ((flOptions & HEAP_LOCKED) ? PR_FIXED : 0))) == -1) { mmError(ERROR_NOT_ENOUGH_MEMORY, "HeapCreate: reserve failed\n"); goto exit; } /* * Call HPInit to initialize the heap structures within the new memory */ #if HEAP_NO_SERIALIZE - HP_NOSERIALIZE # error HEAP_NO_SERIALIZE != HP_NOSERIALIZE #endif #if HEAP_GENERATE_EXCEPTIONS - HP_EXCEPT # error HEAP_GENERATE_EXCEPTIONS != HP_EXCEPT #endif if (((PVOID)rc = HPInit(pmem, pmem, dwMaximumSize, (flOptions & (HP_EXCEPT|HP_NOSERIALIZE|HP_GROWABLE)))) == 0) { goto free; } // if this is a shared heap and not the kernel heap, we don't // want the critical section to go away until the heap is destroyed if ( (flOptions & HEAP_SHARED) && hheapKernel ) { MakeCriticalSectionGlobal( (CRITICAL_SECTION *)(&(((HHEAP)pmem)->hi_critsec)) ); } /* * Link private heaps onto the per-process heap list. */ if ((flOptions & HEAP_SHARED) == 0) { mmAssert(pppdbCur, "HeapCreate: private heap created too early"); ((struct heapinfo_s *)pmem)->hi_procnext = GetCurrentPdb()->hhi_procfirst; GetCurrentPdb()->hhi_procfirst = (struct heapinfo_s *)pmem; } exit: return((HANDLE)rc); free: PageFree(pmem, PR_STATIC); goto exit; } /***EP HeapDestroy - free a heap allocated with HeapCreate * * ENTRY: hHeap - handle to heap to free * EXIT: non-0 if success, or 0 if failure */ BOOL APIENTRY HeapDestroy(HHEAP hHeap) { ULONG rc; struct heapinfo_s **ppheap; struct heapseg_s *pseg; struct heapseg_s *psegnext; EnterMustComplete(); if ((rc = hpTakeSem(hHeap, 0, 0)) == 0) { goto exit; } /* * We now hold the heap's semaphore. Quickly clear the semaphore and * delete the semaphore. If someone comes in and blocks on the semaphore * between the time we clear it and destroy it, tough luck. They will * probably fault in a second. */ hpClearSem(hHeap, 0); if ((hHeap->hi_flags & HP_NOSERIALIZE) == 0) { if (hHeap == hheapKernel) { DestroyCrst(hHeap->hi_pcritsec); } else { Assert(hHeap->hi_pcritsec->typObj == typObjCrst); if (hHeap->hi_pcritsec->typObj == typObjCrst) { DisposeCrst(hHeap->hi_pcritsec); } } } /* * For private heaps, find it on the per-process heap list and remove it. */ if ((ULONG)hHeap < MAXPRIVATELADDR) { ppheap = &(GetCurrentPdb()->hhi_procfirst); for (; *ppheap != hHeap; ppheap = &((*ppheap)->hi_procnext)) { mmAssert(*ppheap != 0, "HeapDestroy: heap not on list"); } *ppheap = hHeap->hi_procnext; /* remove from list */ } /* * Free the heap memory */ pseg = (struct heapseg_s *)hHeap; do { psegnext = pseg->hs_psegnext; PageFree(pseg, PR_STATIC); pseg = psegnext; } while (pseg != 0); exit: LeaveMustComplete(); return(rc); } /***EP HeapAlloc - allocate a fixed/zero-init'ed block from the specified heap * * ENTRY: hHeap - heap handle (pointer to base of heap) * dwFlags - HEAP_ZERO_MEMORY * dwBytes - count of bytes to allocate * EXIT: pointer to block or 0 if failure */ LPVOID APIENTRY HeapAlloc(HANDLE hHeap, DWORD dwFlags, DWORD dwBytes) { // WordArt (32) overwrites some of his local heap blocks. So // we pad his allocations some. Slacker. if (GetAppCompatFlags() & GACF_HEAPSLACK) { if (hHeap == GetCurrentPdb()->hheapLocal) { dwBytes += 16; } } return(HPAlloc((HHEAP)hHeap, dwBytes, (dwFlags & HEAP_GENERATE_EXCEPTIONS) | ((dwFlags & HEAP_ZERO_MEMORY) ? HP_ZEROINIT : 0))); } /***EP HeapReAlloc - resize a memory block on a specified heap * * ENTRY: hHeap - heap handle (pointer to base of heap) * dwFlags - HEAP_REALLOC_IN_PLACE_ONLY * HEAP_ZERO_MEMORY * lpMem - pointer to block to resize * dwBytes - new size requested * EXIT: flat pointer to resized block, or 0 if failure */ LPVOID APIENTRY HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPSTR lpMem, DWORD dwBytes) { return((HANDLE)HPReAlloc((HHEAP)hHeap, lpMem, dwBytes, (dwFlags & (HEAP_NO_SERIALIZE | HP_EXCEPT)) | ((dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) ? 0 : HP_MOVEABLE) | ((dwFlags & HEAP_ZERO_MEMORY) ? HP_ZEROINIT : 0))); } //-------------------------------------------------------------------------- // ToolHelp32 heapwalking code. //-------------------------------------------------------------------------- /*--------------------------------------------------------------------------- * BOOL SafeReadProcessMemory(PPDB ppdb, * LPVOID lpBuffer, * DWORD cbSizeOfBuffer, * DWORD cbBytesToRead); * * Reads memory from another process's context. *---------------------------------------------------------------------------*/ BOOL KERNENTRY SafeReadProcessMemory(PPDB ppdb, DWORD dwBaseAddr, LPVOID lpBuffer, DWORD cbSizeOfBuffer, DWORD cbBytesToRead) { BOOL fRes; #ifdef DEBUG if (cbSizeOfBuffer != 0) { FillBytes(lpBuffer, cbSizeOfBuffer, 0xcc); } if (cbSizeOfBuffer < cbBytesToRead) { DebugOut((DEB_ERR, "SafeReadProcessMemory: Input buffer too small.")); return FALSE; } #endif if (!(fRes = ReadProcessMemoryFromPDB(ppdb, (LPVOID)dwBaseAddr, lpBuffer, cbBytesToRead, NULL))) { #ifdef DEBUG DebugOut((DEB_WARN, "SafeReadProcessMemory: Failed ReadProcessMemory()")); #endif return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Make sure the caller initialized HEAPENTRY32 properly. *---------------------------------------------------------------------------*/ BOOL KERNENTRY ValidateHeapEntry32(LPHEAPENTRY32 lphe32) { if ((lphe32 == NULL) || (lphe32->dwSize != sizeof(HEAPENTRY32))) { DebugOut((DEB_ERR, "HEAPENTRY32: Wrong version or dwSize.")); return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Test if a linear address could plausibly be the start of a block header. *---------------------------------------------------------------------------*/ BOOL KERNENTRY IsValidBlockHdrAddr(LPHEAPENTRY32 lphe32, DWORD dwAddr) { LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); /* * A good block is always in the user address space and dword aligned */ if ((dwAddr & 0x3) || dwAddr < MINPRIVATELADDR || dwAddr >= MAXSHAREDLADDR) { return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Test if a linear address could plausibly be the start of block data. *---------------------------------------------------------------------------*/ BOOL KERNENTRY IsValidBlockDataAddr(LPHEAPENTRY32 lphe32, DWORD dwAddr) { return(IsValidBlockHdrAddr(lphe32, dwAddr)); } /*--------------------------------------------------------------------------- * Read in and validate a lharray_s. *---------------------------------------------------------------------------*/ BOOL KERNENTRY SafeRdCurLHA(LPHEAPENTRY32 lphe32, DWORD dwBaseAddr) { LPTHSTATE lpts; struct lharray_s lha; if (!(ValidateHeapEntry32(lphe32))) { return FALSE; } lpts = (LPTHSTATE)(lphe32->dwResvd); if (!IsValidBlockDataAddr(lphe32, dwBaseAddr)) { return FALSE; } if (!SafeReadProcessMemory(lpts->ppdb, dwBaseAddr, &lha, sizeof(lha), sizeof(lha))) { return FALSE; } // Check signature. if (lha.lha_signature != LHA_SIGNATURE) { DebugOut((DEB_WARN, "lharray_s (%lx) has bad signature.", dwBaseAddr)); return FALSE; } if (lha.lha_next && !IsValidBlockDataAddr(lphe32, (DWORD)lha.lha_next)) { DebugOut((DEB_WARN, "lharray_s (%lx) has bad next link.", dwBaseAddr)); return FALSE; } lpts->curlha = lha; lpts->curlhaaddr = dwBaseAddr; return TRUE; } /*--------------------------------------------------------------------------- * Insert a handle value to be suppressed when reading fixed blocks later. *---------------------------------------------------------------------------*/ BOOL KERNENTRY InsertSuppress(LPHEAPENTRY32 lphe32, DWORD dwSupp) { LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); if (!(lpts->lpdwSuppress)) { #ifdef DEBUG DebugOut((DEB_ERR, "Internal error: lpdwSuppress == NULL.")); #endif return FALSE; } if (lpts->nSuppUsed >= lpts->nSuppAvail) { #ifdef DEBUG DebugOut((DEB_ERR, "Internal error: lpdwSuppress too small.")); #endif return FALSE; } lpts->lpdwSuppress[lpts->nSuppUsed++] = dwSupp; return TRUE; } /*--------------------------------------------------------------------------- * Validate and decode a heap block header. *---------------------------------------------------------------------------*/ BOOL KERNENTRY DissectBlockHdr(LPHEAPENTRY32 lphe32, DWORD dwAddr, DWORD *lpdwSize, DWORD *lpdwFlags, DWORD *lpdwAddr) { DWORD dwHdr; LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); if (!IsValidBlockHdrAddr(lphe32, dwAddr)) { return FALSE; } *lpdwFlags = HP_SIGNATURE ^ ((DWORD)0xffffffff); if (!SafeReadProcessMemory(lpts->ppdb, dwAddr, &dwHdr, sizeof(dwHdr), sizeof(DWORD))) { return FALSE; } if ( (dwHdr & HP_SIGBITS) != HP_SIGNATURE ) { return FALSE; } *lpdwSize = dwHdr & HP_SIZE; *lpdwFlags = dwHdr & HP_FLAGS; *lpdwAddr = dwAddr + ( (dwHdr & HP_FREE) ? sizeof(struct freeheap_s) : sizeof(struct busyheap_s) ); if (*lpdwSize != 0 && !IsValidBlockHdrAddr(lphe32, dwAddr + (*lpdwSize))) { return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Check if we're at the end of the heap (heap is terminated by a * busy block of size 0). *---------------------------------------------------------------------------*/ BOOL KERNENTRY AtEndOfHeap32(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); if (lpts->dwMode != THM_FIXEDHANDLES) { return FALSE; } return (!((lpts->dwBlkFlags) & HP_FREE) && (lpts->dwBlkSize) == 0); } /*--------------------------------------------------------------------------- * Internal routine (maybe make it an api?). Deallocate all internal * state used for heap-walking. *---------------------------------------------------------------------------*/ VOID KERNENTRY RealHeap32End(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; if (!(ValidateHeapEntry32(lphe32))) { return; } lpts = (LPTHSTATE)(lphe32->dwResvd); // In case someone calls this after they've fallen off the end. if (lpts == NULL) { return; } EnterMustComplete(); if (lpts->pcrst) { DisposeCrst(lpts->pcrst); lpts->pcrst = NULL; } LeaveMustComplete(); if (lpts->lpdwSuppress) { FKernelFree(lpts->lpdwSuppress); lpts->lpdwSuppress = NULL; } FKernelFree(lpts); lphe32->dwResvd = 0; FillBytes(( (char*)lphe32 ) + 4, sizeof(HEAPENTRY32) - 4, 0); } /*--------------------------------------------------------------------------- * Copy current heap object into HEAPENTRY32 for caller's consumption. * To skip this object, set *pfInteresting to FALSE. *---------------------------------------------------------------------------*/ BOOL KERNENTRY CopyIntoHeap32Entry(LPHEAPENTRY32 lphe32, BOOL *pfInteresting) { LPTHSTATE lpts; *pfInteresting = TRUE; lpts = (LPTHSTATE)(lphe32->dwResvd); switch (lpts->dwMode) { case THM_LHANDLES: { DWORD dwSize; DWORD dwFlags; DWORD dwAddr; DWORD dwHnd; struct lhandle_s *plh; plh = &(lpts->curlha.lha_lh[lpts->nNextLH]); if (plh->lh_signature == LH_FREESIG) { *pfInteresting = FALSE; return TRUE; } if (plh->lh_signature != LH_BUSYSIG) { DebugOut((DEB_WARN, "lhandle_s has bad signature.")); return FALSE; } dwHnd = ( (DWORD)(&(plh->lh_pdata)) ) - ( (DWORD)(&(lpts->curlha)) ) + lpts->curlhaaddr; if (!plh->lh_pdata) { // Discarded handle. lphe32->hHandle = (HANDLE)dwHnd; lphe32->dwAddress = 0; lphe32->dwBlockSize = 0; lphe32->dwFlags = LF32_MOVEABLE; lphe32->dwLockCount = (DWORD)(plh->lh_clock); return TRUE; } if (!DissectBlockHdr(lphe32, ( (DWORD)(plh->lh_pdata) ) - 4 - sizeof(struct busyheap_s), &dwSize, &dwFlags, &dwAddr )) { return FALSE; // This will be caught someplace else. } if (dwFlags & HP_FREE) { DebugOut((DEB_WARN, "Local handle points to freed block!")); return FALSE; } if (!InsertSuppress(lphe32, dwAddr-sizeof(struct busyheap_s))) { return FALSE; } lphe32->hHandle = (HANDLE)dwHnd; lphe32->dwAddress = dwAddr + 4; lphe32->dwBlockSize = dwSize - sizeof(struct busyheap_s) - 4; lphe32->dwFlags = LF32_MOVEABLE; lphe32->dwLockCount = (DWORD)(plh->lh_clock); return TRUE; } case THM_FIXEDHANDLES: { if ((lpts->dwBlkFlags) & HP_FREE) { lphe32->hHandle = NULL; lphe32->dwAddress = lpts->dwBlkAddr; lphe32->dwBlockSize = lpts->dwBlkSize - sizeof(struct freeheap_s); lphe32->dwFlags = LF32_FREE; lphe32->dwLockCount = 0; } else { // Supress if it's a lharray_s or the target of // an lhandle. Opt: we could check the first dword // to rule out lots of blocks. if (lpts->lpdwSuppress) { DWORD *lpdw, *lpdwEnd; DWORD dwHdrAddr = lpts->lpHBlock; lpdwEnd = &(lpts->lpdwSuppress[lpts->nSuppUsed]); for (lpdw = lpts->lpdwSuppress; lpdw < lpdwEnd; lpdw++) { if (dwHdrAddr == *lpdw) { *pfInteresting = FALSE; return TRUE; } } } lphe32->hHandle = (HANDLE)(lpts->dwBlkAddr); lphe32->dwAddress = lpts->dwBlkAddr; lphe32->dwBlockSize = lpts->dwBlkSize - sizeof(struct busyheap_s); lphe32->dwFlags = LF32_FIXED; lphe32->dwLockCount = 0; } return TRUE; } case THM_ERROR: DebugOut((DEB_ERR, "Internal error: Can't get here")); return FALSE; case THM_DONE: DebugOut((DEB_ERR, "Internal error: Can't get here")); return FALSE; default: DebugOut((DEB_ERR, "Internal error: Bad lpthstate.dwmode")); return FALSE; } } /*--------------------------------------------------------------------------- * Worker routine for AdvanceHeap32(): handles the init case. * * If the heap is the owning pdb's default heap (determined by * comparing hHeap with ppdb->hHeapLocal), point the state to * the first lharray_s. Otherwise, point the state to the first heap block. * *---------------------------------------------------------------------------*/ BOOL KERNENTRY AdvanceHeap32Init(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; struct lharray_s *lpha; DWORD dwNumSupp; lpts = (LPTHSTATE)(lphe32->dwResvd); lpha = lpts->ppdb->plhBlock; if (lpts->ppdb->hheapLocal != lpts->hHeap || lpha == NULL) { lpts->dwMode = THM_FIXEDHANDLES; lpts->lpHBlock = lpts->lpbMin; if (!DissectBlockHdr(lphe32, lpts->lpHBlock, &(lpts->dwBlkSize), &(lpts->dwBlkFlags), &(lpts->dwBlkAddr))) { return FALSE; } return TRUE; } if (!SafeRdCurLHA(lphe32, (DWORD)lpha)) { return FALSE; } dwNumSupp = (lpts->curlha.lha_membercount + 1) * (1 + CLHGROW); if (!(lpts->lpdwSuppress = PvKernelAlloc0(dwNumSupp * sizeof(DWORD)))) { return FALSE; } lpts->nSuppAvail = dwNumSupp * sizeof(DWORD); lpts->nSuppUsed = 0; if (!(InsertSuppress(lphe32, ((DWORD)lpha) - sizeof(struct busyheap_s)))) { return FALSE; } lpts->nNextLH = 0; lpts->dwMode = THM_LHANDLES; return TRUE; } /*--------------------------------------------------------------------------- * Worker routine for AdvanceHeap32(): handles the lhandle case. *---------------------------------------------------------------------------*/ BOOL KERNENTRY AdvanceHeap32Movable(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; WORD wOldMemberCnt; DWORD dwAddrNext; lpts = (LPTHSTATE)(lphe32->dwResvd); if (lpts->nNextLH < CLHGROW-1) { lpts->nNextLH++; return TRUE; } // End of current lhandle clump reached. Any new ones? if (lpts->curlha.lha_next == NULL) { // Nope. Go on to fixed handles. lpts->dwMode = THM_FIXEDHANDLES; lpts->lpHBlock = lpts->lpbMin; if (!DissectBlockHdr(lphe32, lpts->lpHBlock, &(lpts->dwBlkSize), &(lpts->dwBlkFlags), &(lpts->dwBlkAddr))) { return FALSE; } return TRUE; } // Get next lhandle clump. wOldMemberCnt = lpts->curlha.lha_membercount; dwAddrNext = (DWORD)(lpts->curlha.lha_next); if (!SafeRdCurLHA(lphe32, dwAddrNext)) { return FALSE; } if (lpts->curlha.lha_membercount >= wOldMemberCnt) { DebugOut((DEB_WARN, "lha_array clusters in wrong order.")); return FALSE; } lpts->nNextLH = 0; return TRUE; } /*--------------------------------------------------------------------------- * Worker routine for AdvanceHeap32(): handles the fixed block case. *---------------------------------------------------------------------------*/ BOOL KERNENTRY AdvanceHeap32Fixed(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); // Diassect block has already checked monotonocity and range. lpts->lpHBlock += lpts->dwBlkSize; if (!DissectBlockHdr(lphe32, lpts->lpHBlock, &(lpts->dwBlkSize), &(lpts->dwBlkFlags), &(lpts->dwBlkAddr) )) { return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Advance the internal state to the next heap object. Validate the * next heap object. *---------------------------------------------------------------------------*/ BOOL KERNENTRY AdvanceHeap32(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; lpts = (LPTHSTATE)(lphe32->dwResvd); switch (lpts->dwMode) { case THM_INIT: return AdvanceHeap32Init(lphe32); case THM_LHANDLES: return AdvanceHeap32Movable(lphe32); case THM_FIXEDHANDLES: return AdvanceHeap32Fixed(lphe32); default: DebugOut((DEB_ERR, "Illegal or unexpected THM mode.")); return FALSE; } } /*--------------------------------------------------------------------------- * Does the real work of heap32next(). *---------------------------------------------------------------------------*/ VOID KERNENTRY Heap32NextWorker(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; BOOL fInteresting; lpts = (LPTHSTATE)(lphe32->dwResvd); do { if (!AdvanceHeap32(lphe32)) { goto rh_error; } if (AtEndOfHeap32(lphe32)) { /* * We might be at the end of the heap, or just at the end of * this heap segment. If there is another segment, read its * header in and process its blocks. */ if (lpts->hi.hi_psegnext) { lpts->lpbMin = ((DWORD)lpts->hi.hi_psegnext) + sizeof(struct heapseg_s); /* * Read in the next heap segment header and setup our bounds to * refer to it */ if (!(SafeReadProcessMemory(lpts->ppdb, (DWORD)lpts->hi.hi_psegnext, &(lpts->hi), sizeof(struct heapseg_s), sizeof(struct heapseg_s)))) { #ifdef DEBUG DebugOut((DEB_WARN, "Heap32NextWorker(): Invalid or corrupt psegnext: %lx\n", lpts->hi.hi_psegnext)); #endif goto rh_error; } if (lpts->hi.hi_cbreserve > hpMAXALLOC || ((lpts->hi.hi_cbreserve) & PAGEMASK)) { #ifdef DEBUG DebugOut((DEB_WARN, "Heap32NextWorker(): Invalid or corrupt psegnext (3): %lx\n", lpts->lpbMin - sizeof(struct heapseg_s))); #endif goto rh_error; } /* * Setup first block on new segment */ lpts->lpHBlock = lpts->lpbMin; if (!DissectBlockHdr(lphe32, lpts->lpHBlock, &(lpts->dwBlkSize), &(lpts->dwBlkFlags), &(lpts->dwBlkAddr))) { goto rh_error; } /* * If we really are at the end of the heap, we are all done */ } else { lpts->dwMode = THM_DONE; return; } } fInteresting = TRUE; if (!CopyIntoHeap32Entry(lphe32, &fInteresting)) { goto rh_error; } } while (!fInteresting); return; rh_error: lpts->dwMode = THM_ERROR; return; } /*--------------------------------------------------------------------------- * Does the real work of Heap32Next(). *---------------------------------------------------------------------------*/ BOOL KERNENTRY RealHeap32Next(LPHEAPENTRY32 lphe32) { LPTHSTATE lpts; DWORD dwMode; if (!(ValidateHeapEntry32(lphe32))) { SetError(ERROR_INVALID_PARAMETER); return FALSE; } lpts = (LPTHSTATE)(lphe32->dwResvd); // In case someone calls this after they've fallen off the end. if (lpts == NULL) { SetError(ERROR_INVALID_PARAMETER); return FALSE; } EnterCrst(lpts->pcrst); Heap32NextWorker(lphe32); dwMode = lpts->dwMode; LeaveCrst(lpts->pcrst); if (dwMode == THM_ERROR || dwMode == THM_DONE) { if (dwMode == THM_ERROR) { DebugOut((DEB_WARN, "Heap32Next detected corrupted or moving heap. Bailing.")); SetError(ERROR_INVALID_DATA); } else { SetError(ERROR_NO_MORE_FILES); } RealHeap32End(lphe32); return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- * Create the internal state used inside HEAPENTRY32. *---------------------------------------------------------------------------*/ BOOL KERNENTRY InitHeapEntry32(PPDB ppdb, HANDLE hHeap, LPHEAPENTRY32 lphe32) { LPTHSTATE lpts = NULL; CRST *pcrst = NULL; if (!ValidateHeapEntry32(lphe32)) { return FALSE; } EnterMustComplete(); if (!(lphe32->dwResvd = (DWORD)PvKernelAlloc0(sizeof(THSTATE)))) { goto ih_error; } lpts = (LPTHSTATE)(lphe32->dwResvd); if (!(pcrst = lpts->pcrst = NewCrst())) { goto ih_error; } lpts->ppdb = ppdb; lpts->hHeap = hHeap; if (!(SafeReadProcessMemory(ppdb, (DWORD)hHeap, &(lpts->hi), sizeof(lpts->hi), sizeof(struct heapinfo_s)))) { #ifdef DEBUG DebugOut((DEB_WARN, "Heap32First(): Invalid hHeap: %lx\n", hHeap)); #endif goto ih_error; } if (lpts->hi.hi_signature != HI_SIGNATURE) { #ifdef DEBUG DebugOut((DEB_WARN, "Heap32First(): Invalid or corrupt hHeap: %lx\n", hHeap)); #endif goto ih_error; } lpts->lpbMin = ( (DWORD)hHeap ) + sizeof(struct heapinfo_s); if (lpts->hi.hi_cbreserve > hpMAXALLOC || ((lpts->hi.hi_cbreserve) & PAGEMASK)) { #ifdef DEBUG DebugOut((DEB_WARN, "Heap32First(): Invalid or corrupt hHeap: %lx\n", hHeap)); #endif goto ih_error; } lpts->dwMode = THM_INIT; LeaveMustComplete(); return TRUE; ih_error: if (lpts) { FKernelFree(lpts); } if (pcrst) { DisposeCrst(pcrst); } lphe32->dwResvd = 0; LeaveMustComplete(); return FALSE; } /***LP VerifyOnHeap - verifies a given address is on a given heap * * Note that no validation is done on the given address except * to check that it is in the range of the heap. * * ENTRY: hheap - heap handle * p - address to verify * EXIT: 0 if not within specified heap, non-zero if on */ ULONG INTERNAL VerifyOnHeap(HHEAP hheap, PVOID p) { struct heapseg_s *pseg; /* * Loop through each heap segment and see if the specified address * is within it. */ pseg = (struct heapseg_s *)hheap; do { if ((unsigned)p > (unsigned)pseg && (unsigned)p < (unsigned)pseg + pseg->hs_cbreserve) { return(1); /* found it */ } pseg = pseg->hs_psegnext; } while (pseg != 0); return(0); /* didn't find it */ } /***LP CheckHeapFreeAppHack - See if CVPACK app-hack applies * * Check to see if an absolutely sick, disgusting and vomit-inducing * app-hack for link.exe (msvc 1.5) is needed. msvc 1.5. Link.exe * uses the contents of a heap block after it has freed it. * This routine stack-traces and reads the caller's code * to see if it matches the offending profile. This part is written * in C so we can use try-except. */ BOOL KERNENTRY CheckHeapFreeAppHack(DWORD *lpdwESP, DWORD *lpdwEBP, DWORD dwESI) { BOOL fDoAppHack = FALSE; _try { DWORD *lpdwEIPCaller; lpdwEIPCaller = (DWORD*)(*lpdwESP); if (0xc35de58b == *lpdwEIPCaller) { // "mov esp,ebp;pop ebp; retd" DWORD *lpdwEIPCallersCaller; lpdwEIPCallersCaller = (DWORD*)(*(lpdwEBP + 1)); if (0x8b04c483 == *lpdwEIPCallersCaller && 0xf60b0876 == *(lpdwEIPCallersCaller+1)) { //"add esp,4; mov esi, [esi+8]; or esi,esi" if (dwESI == *(lpdwESP+3)) { fDoAppHack = TRUE; } } } } _except (EXCEPTION_EXECUTE_HANDLER) { } return fDoAppHack; }