windows-nt/Source/XPSP1/NT/windows/appcompat/shims/layer/emulateheap_lmem.c

2168 lines
55 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
// LMEM.C
//
// (C) Copyright Microsoft Corp., 1988-1994
//
// Win32 wrappers for heap functions (Local* and some Heap*)
//
// Origin: <Chicago>
//
// Change history:
//
// Date Who Description
// --------- --------- -------------------------------------------------
// BrianSm Local* and Heap* APIs
// AtsushiK Toolhelp
// 15-Feb-94 JonT Code cleanup and precompiled headers
#include <EmulateHeap_kernel32.h>
#pragma hdrstop("kernel32.pch")
#include <tlhelp32.h>
#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;
}