| /* |
| * basic interfaces |
| * |
| * Copyright 1997 Marcus Meissner |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "ole2.h" |
| #include "winerror.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(olemalloc); |
| |
| /****************************************************************************** |
| * IMalloc32 implementation |
| * |
| * NOTES |
| * For supporting CoRegisterMallocSpy the IMalloc implementation must know if |
| * a given memory block was allocated with a spy active. |
| * |
| *****************************************************************************/ |
| /* set the vtable later */ |
| static const IMallocVtbl VT_IMalloc32; |
| |
| typedef struct { |
| IMalloc IMalloc_iface; |
| DWORD dummy; /* nothing, we are static */ |
| IMallocSpy * pSpy; /* the spy when active */ |
| DWORD SpyedAllocationsLeft; /* number of spyed allocations left */ |
| BOOL SpyReleasePending; /* CoRevokeMallocSpy called with spyed allocations left*/ |
| LPVOID * SpyedBlocks; /* root of the table */ |
| DWORD SpyedBlockTableLength;/* size of the table*/ |
| } _Malloc32; |
| |
| /* this is the static object instance */ |
| static _Malloc32 Malloc32 = {{&VT_IMalloc32}, 0, NULL, 0, 0, NULL, 0}; |
| |
| /* with a spy active all calls from pre to post methods are threadsave */ |
| static CRITICAL_SECTION IMalloc32_SpyCS; |
| static CRITICAL_SECTION_DEBUG critsect_debug = |
| { |
| 0, 0, &IMalloc32_SpyCS, |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": IMalloc32_SpyCS") } |
| }; |
| static CRITICAL_SECTION IMalloc32_SpyCS = { &critsect_debug, -1, 0, 0, 0, 0 }; |
| |
| /* resize the old table */ |
| static BOOL SetSpyedBlockTableLength ( DWORD NewLength ) |
| { |
| LPVOID *NewSpyedBlocks; |
| |
| if (!Malloc32.SpyedBlocks) NewSpyedBlocks = LocalAlloc(LMEM_ZEROINIT, NewLength * sizeof(PVOID)); |
| else NewSpyedBlocks = LocalReAlloc(Malloc32.SpyedBlocks, NewLength * sizeof(PVOID), LMEM_ZEROINIT | LMEM_MOVEABLE); |
| if (NewSpyedBlocks) { |
| Malloc32.SpyedBlocks = NewSpyedBlocks; |
| Malloc32.SpyedBlockTableLength = NewLength; |
| } |
| |
| return NewSpyedBlocks != NULL; |
| } |
| |
| /* add a location to the table */ |
| static BOOL AddMemoryLocation(LPVOID * pMem) |
| { |
| LPVOID * Current; |
| |
| /* allocate the table if not already allocated */ |
| if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000)) |
| return FALSE; |
| |
| /* find a free location */ |
| Current = Malloc32.SpyedBlocks; |
| while (*Current) { |
| Current++; |
| if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) { |
| /* no more space in table, grow it */ |
| DWORD old_length = Malloc32.SpyedBlockTableLength; |
| if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000)) |
| return FALSE; |
| Current = Malloc32.SpyedBlocks + old_length; |
| } |
| }; |
| |
| /* put the location in our table */ |
| *Current = pMem; |
| Malloc32.SpyedAllocationsLeft++; |
| /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ |
| return TRUE; |
| } |
| |
| static BOOL RemoveMemoryLocation(LPCVOID pMem) |
| { |
| LPVOID * Current; |
| |
| /* allocate the table if not already allocated */ |
| if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000)) |
| return FALSE; |
| |
| Current = Malloc32.SpyedBlocks; |
| |
| /* find the location */ |
| while (*Current != pMem) { |
| Current++; |
| if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) |
| return FALSE; /* not found */ |
| } |
| |
| /* location found */ |
| Malloc32.SpyedAllocationsLeft--; |
| /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ |
| *Current = NULL; |
| return TRUE; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_QueryInterface [VTABLE] |
| */ |
| static HRESULT WINAPI IMalloc_fnQueryInterface(IMalloc *iface, REFIID refiid, void **obj) |
| { |
| TRACE("(%s,%p)\n",debugstr_guid(refiid),obj); |
| |
| if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) { |
| *obj = &Malloc32; |
| return S_OK; |
| } |
| return E_NOINTERFACE; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_AddRefRelease [VTABLE] |
| */ |
| static ULONG WINAPI IMalloc_fnAddRefRelease(IMalloc *iface) |
| { |
| return 1; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_Alloc [VTABLE] |
| */ |
| static void * WINAPI IMalloc_fnAlloc(IMalloc *iface, SIZE_T cb) |
| { |
| void *addr; |
| |
| TRACE("(%ld)\n",cb); |
| |
| if(Malloc32.pSpy) { |
| SIZE_T preAllocResult; |
| |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| preAllocResult = IMallocSpy_PreAlloc(Malloc32.pSpy, cb); |
| if ((cb != 0) && (preAllocResult == 0)) { |
| /* PreAlloc can force Alloc to fail, but not if cb == 0 */ |
| TRACE("returning null\n"); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| return NULL; |
| } |
| } |
| |
| addr = HeapAlloc(GetProcessHeap(),0,cb); |
| |
| if(Malloc32.pSpy) { |
| addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr); |
| if (addr) AddMemoryLocation(addr); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| |
| TRACE("--(%p)\n",addr); |
| return addr; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_Realloc [VTABLE] |
| */ |
| static void * WINAPI IMalloc_fnRealloc(IMalloc *iface, void *pv, SIZE_T cb) |
| { |
| void *pNewMemory; |
| |
| TRACE("(%p,%ld)\n",pv,cb); |
| |
| if(Malloc32.pSpy) { |
| void *pRealMemory; |
| BOOL fSpyed; |
| |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| fSpyed = RemoveMemoryLocation(pv); |
| cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed); |
| |
| /* check if can release the spy */ |
| if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { |
| IMallocSpy_Release(Malloc32.pSpy); |
| Malloc32.SpyReleasePending = FALSE; |
| Malloc32.pSpy = NULL; |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| |
| if (0==cb) { |
| /* PreRealloc can force Realloc to fail */ |
| if (Malloc32.pSpy) |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| return NULL; |
| } |
| |
| pv = pRealMemory; |
| } |
| |
| if (!pv) pNewMemory = HeapAlloc(GetProcessHeap(),0,cb); |
| else if (cb) pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb); |
| else { |
| HeapFree(GetProcessHeap(),0,pv); |
| pNewMemory = NULL; |
| } |
| |
| if(Malloc32.pSpy) { |
| pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE); |
| if (pNewMemory) AddMemoryLocation(pNewMemory); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| |
| TRACE("--(%p)\n",pNewMemory); |
| return pNewMemory; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_Free [VTABLE] |
| */ |
| static void WINAPI IMalloc_fnFree(IMalloc *iface, void *pv) |
| { |
| BOOL fSpyed = FALSE; |
| |
| TRACE("(%p)\n",pv); |
| |
| if(!pv) |
| return; |
| |
| if(Malloc32.pSpy) { |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| fSpyed = RemoveMemoryLocation(pv); |
| pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed); |
| } |
| |
| HeapFree(GetProcessHeap(),0,pv); |
| |
| if(Malloc32.pSpy) { |
| IMallocSpy_PostFree(Malloc32.pSpy, fSpyed); |
| |
| /* check if can release the spy */ |
| if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { |
| IMallocSpy_Release(Malloc32.pSpy); |
| Malloc32.SpyReleasePending = FALSE; |
| Malloc32.pSpy = NULL; |
| } |
| |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_GetSize [VTABLE] |
| * |
| * NOTES |
| * FIXME returns: |
| * win95: size allocated (4 byte boundarys) |
| * win2k: size originally requested !!! (allocated on 8 byte boundarys) |
| */ |
| static SIZE_T WINAPI IMalloc_fnGetSize(IMalloc *iface, void *pv) |
| { |
| SIZE_T cb; |
| BOOL fSpyed = FALSE; |
| |
| TRACE("(%p)\n",pv); |
| |
| if(Malloc32.pSpy) { |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| pv = IMallocSpy_PreGetSize(Malloc32.pSpy, pv, fSpyed); |
| } |
| |
| cb = HeapSize(GetProcessHeap(),0,pv); |
| |
| if(Malloc32.pSpy) { |
| cb = IMallocSpy_PostGetSize(Malloc32.pSpy, cb, fSpyed); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| |
| return cb; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_DidAlloc [VTABLE] |
| */ |
| static INT WINAPI IMalloc_fnDidAlloc(IMalloc *iface, void *pv) |
| { |
| BOOL fSpyed = FALSE; |
| int didAlloc; |
| |
| TRACE("(%p)\n",pv); |
| |
| if(Malloc32.pSpy) { |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| pv = IMallocSpy_PreDidAlloc(Malloc32.pSpy, pv, fSpyed); |
| } |
| |
| didAlloc = -1; |
| |
| if(Malloc32.pSpy) { |
| didAlloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, pv, fSpyed, didAlloc); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| return didAlloc; |
| } |
| |
| /****************************************************************************** |
| * IMalloc32_HeapMinimize [VTABLE] |
| */ |
| static void WINAPI IMalloc_fnHeapMinimize(IMalloc *iface) |
| { |
| TRACE("()\n"); |
| |
| if(Malloc32.pSpy) { |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| IMallocSpy_PreHeapMinimize(Malloc32.pSpy); |
| } |
| |
| if(Malloc32.pSpy) { |
| IMallocSpy_PostHeapMinimize(Malloc32.pSpy); |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| } |
| } |
| |
| static const IMallocVtbl VT_IMalloc32 = |
| { |
| IMalloc_fnQueryInterface, |
| IMalloc_fnAddRefRelease, |
| IMalloc_fnAddRefRelease, |
| IMalloc_fnAlloc, |
| IMalloc_fnRealloc, |
| IMalloc_fnFree, |
| IMalloc_fnGetSize, |
| IMalloc_fnDidAlloc, |
| IMalloc_fnHeapMinimize |
| }; |
| |
| /****************************************************************************** |
| * CoGetMalloc [OLE32.@] |
| * |
| * Retrieves the current IMalloc interface for the process. |
| * |
| * PARAMS |
| * context [I] Should always be MEMCTX_TASK. |
| * imalloc [O] Address where memory allocator object will be stored. |
| * |
| * RETURNS |
| * Success: S_OK. |
| * Failure: HRESULT code. |
| */ |
| HRESULT WINAPI CoGetMalloc(DWORD context, IMalloc **imalloc) |
| { |
| if (context != MEMCTX_TASK) { |
| *imalloc = NULL; |
| return E_INVALIDARG; |
| } |
| |
| *imalloc = &Malloc32.IMalloc_iface; |
| return S_OK; |
| } |
| |
| /*********************************************************************** |
| * CoTaskMemAlloc [OLE32.@] |
| * |
| * Allocates memory using the current process memory allocator. |
| * |
| * PARAMS |
| * size [I] Size of the memory block to allocate. |
| * |
| * RETURNS |
| * Success: Pointer to newly allocated memory block. |
| * Failure: NULL. |
| */ |
| LPVOID WINAPI CoTaskMemAlloc(SIZE_T size) |
| { |
| return IMalloc_Alloc(&Malloc32.IMalloc_iface,size); |
| } |
| |
| /*********************************************************************** |
| * CoTaskMemFree [OLE32.@] |
| * |
| * Frees memory allocated from the current process memory allocator. |
| * |
| * PARAMS |
| * ptr [I] Memory block to free. |
| * |
| * RETURNS |
| * Nothing. |
| */ |
| VOID WINAPI CoTaskMemFree(LPVOID ptr) |
| { |
| IMalloc_Free(&Malloc32.IMalloc_iface, ptr); |
| } |
| |
| /*********************************************************************** |
| * CoTaskMemRealloc [OLE32.@] |
| * |
| * Allocates memory using the current process memory allocator. |
| * |
| * PARAMS |
| * pvOld [I] Pointer to old memory block. |
| * size [I] Size of the new memory block. |
| * |
| * RETURNS |
| * Success: Pointer to newly allocated memory block. |
| * Failure: NULL. |
| */ |
| LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, SIZE_T size) |
| { |
| return IMalloc_Realloc(&Malloc32.IMalloc_iface, pvOld, size); |
| } |
| |
| /*********************************************************************** |
| * CoRegisterMallocSpy [OLE32.@] |
| * |
| * Registers an object that receives notifications on memory allocations and |
| * frees. |
| * |
| * PARAMS |
| * pMallocSpy [I] New spy object. |
| * |
| * RETURNS |
| * Success: S_OK. |
| * Failure: HRESULT code. |
| * |
| * NOTES |
| * if a mallocspy is already registered, we can't do it again since |
| * only the spy knows, how to free a memory block |
| */ |
| HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy) |
| { |
| IMallocSpy* pSpy; |
| HRESULT hres = E_INVALIDARG; |
| |
| TRACE("%p\n", pMallocSpy); |
| |
| if(!pMallocSpy) return E_INVALIDARG; |
| |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| |
| if (Malloc32.pSpy) |
| hres = CO_E_OBJISREG; |
| else if (SUCCEEDED(IMallocSpy_QueryInterface(pMallocSpy, &IID_IMallocSpy, (void**)&pSpy))) { |
| Malloc32.pSpy = pSpy; |
| hres = S_OK; |
| } |
| |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| |
| return hres; |
| } |
| |
| /*********************************************************************** |
| * CoRevokeMallocSpy [OLE32.@] |
| * |
| * Revokes a previously registered object that receives notifications on memory |
| * allocations and frees. |
| * |
| * PARAMS |
| * pMallocSpy [I] New spy object. |
| * |
| * RETURNS |
| * Success: S_OK. |
| * Failure: HRESULT code. |
| * |
| * NOTES |
| * we can't revoke a malloc spy as long as memory blocks allocated with |
| * the spy are active since only the spy knows how to free them |
| */ |
| HRESULT WINAPI CoRevokeMallocSpy(void) |
| { |
| HRESULT hres = S_OK; |
| TRACE("\n"); |
| |
| EnterCriticalSection(&IMalloc32_SpyCS); |
| |
| if (!Malloc32.pSpy) |
| hres = CO_E_OBJNOTREG; |
| else if (Malloc32.SpyedAllocationsLeft) { |
| TRACE("SpyReleasePending with %u allocations left\n", Malloc32.SpyedAllocationsLeft); |
| Malloc32.SpyReleasePending = TRUE; |
| hres = E_ACCESSDENIED; |
| } else { |
| IMallocSpy_Release(Malloc32.pSpy); |
| Malloc32.pSpy = NULL; |
| } |
| LeaveCriticalSection(&IMalloc32_SpyCS); |
| |
| return hres; |
| } |
| |
| /****************************************************************************** |
| * IsValidInterface [OLE32.@] |
| * |
| * Determines whether a pointer is a valid interface. |
| * |
| * PARAMS |
| * punk [I] Interface to be tested. |
| * |
| * RETURNS |
| * TRUE, if the passed pointer is a valid interface, or FALSE otherwise. |
| */ |
| BOOL WINAPI IsValidInterface(LPUNKNOWN punk) |
| { |
| return !( |
| IsBadReadPtr(punk,4) || |
| IsBadReadPtr(punk->lpVtbl,4) || |
| IsBadReadPtr(punk->lpVtbl->QueryInterface,9) || |
| IsBadCodePtr((FARPROC)punk->lpVtbl->QueryInterface) |
| ); |
| } |