1 /* 2 * basic interfaces 3 * 4 * Copyright 1997 Marcus Meissner 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <ctype.h> 22 #include <stdarg.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <assert.h> 26 27 #define COBJMACROS 28 29 #include "windef.h" 30 #include "winbase.h" 31 #include "winuser.h" 32 #include "ole2.h" 33 #include "winerror.h" 34 35 #include "wine/debug.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(olemalloc); 38 39 /****************************************************************************** 40 * IMalloc32 implementation 41 * 42 * NOTES 43 * For supporting CoRegisterMallocSpy the IMalloc implementation must know if 44 * a given memory block was allocated with a spy active. 45 * 46 *****************************************************************************/ 47 /* set the vtable later */ 48 static const IMallocVtbl VT_IMalloc32; 49 50 typedef struct { 51 IMalloc IMalloc_iface; 52 DWORD dummy; /* nothing, we are static */ 53 IMallocSpy * pSpy; /* the spy when active */ 54 DWORD SpyedAllocationsLeft; /* number of spyed allocations left */ 55 BOOL SpyReleasePending; /* CoRevokeMallocSpy called with spyed allocations left*/ 56 LPVOID * SpyedBlocks; /* root of the table */ 57 DWORD SpyedBlockTableLength;/* size of the table*/ 58 } _Malloc32; 59 60 /* this is the static object instance */ 61 static _Malloc32 Malloc32 = {{&VT_IMalloc32}, 0, NULL, 0, 0, NULL, 0}; 62 63 /* with a spy active all calls from pre to post methods are threadsave */ 64 static CRITICAL_SECTION IMalloc32_SpyCS; 65 static CRITICAL_SECTION_DEBUG critsect_debug = 66 { 67 0, 0, &IMalloc32_SpyCS, 68 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 69 0, 0, { (DWORD_PTR)(__FILE__ ": IMalloc32_SpyCS") } 70 }; 71 static CRITICAL_SECTION IMalloc32_SpyCS = { &critsect_debug, -1, 0, 0, 0, 0 }; 72 73 /* resize the old table */ 74 static BOOL SetSpyedBlockTableLength ( DWORD NewLength ) 75 { 76 LPVOID *NewSpyedBlocks; 77 78 if (!Malloc32.SpyedBlocks) NewSpyedBlocks = LocalAlloc(LMEM_ZEROINIT, NewLength * sizeof(PVOID)); 79 else NewSpyedBlocks = LocalReAlloc(Malloc32.SpyedBlocks, NewLength * sizeof(PVOID), LMEM_ZEROINIT | LMEM_MOVEABLE); 80 if (NewSpyedBlocks) { 81 Malloc32.SpyedBlocks = NewSpyedBlocks; 82 Malloc32.SpyedBlockTableLength = NewLength; 83 } 84 85 return NewSpyedBlocks != NULL; 86 } 87 88 /* add a location to the table */ 89 static BOOL AddMemoryLocation(LPVOID * pMem) 90 { 91 LPVOID * Current; 92 93 /* allocate the table if not already allocated */ 94 if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000)) 95 return FALSE; 96 97 /* find a free location */ 98 Current = Malloc32.SpyedBlocks; 99 while (*Current) { 100 Current++; 101 if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) { 102 /* no more space in table, grow it */ 103 DWORD old_length = Malloc32.SpyedBlockTableLength; 104 if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000)) 105 return FALSE; 106 Current = Malloc32.SpyedBlocks + old_length; 107 } 108 }; 109 110 /* put the location in our table */ 111 *Current = pMem; 112 Malloc32.SpyedAllocationsLeft++; 113 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ 114 return TRUE; 115 } 116 117 static void** mallocspy_is_allocation_spyed(const void *mem) 118 { 119 void **current = Malloc32.SpyedBlocks; 120 121 while (*current != mem) 122 { 123 current++; 124 if (current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) 125 return NULL; 126 } 127 128 return current; 129 } 130 131 static BOOL RemoveMemoryLocation(LPCVOID pMem) 132 { 133 LPVOID * Current; 134 135 /* allocate the table if not already allocated */ 136 if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000)) 137 return FALSE; 138 139 if (!(Current = mallocspy_is_allocation_spyed(pMem))) 140 return FALSE; 141 142 /* location found */ 143 Malloc32.SpyedAllocationsLeft--; 144 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ 145 *Current = NULL; 146 return TRUE; 147 } 148 149 /****************************************************************************** 150 * IMalloc32_QueryInterface [VTABLE] 151 */ 152 static HRESULT WINAPI IMalloc_fnQueryInterface(IMalloc *iface, REFIID refiid, void **obj) 153 { 154 TRACE("(%s,%p)\n",debugstr_guid(refiid),obj); 155 156 if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) { 157 *obj = &Malloc32; 158 return S_OK; 159 } 160 return E_NOINTERFACE; 161 } 162 163 /****************************************************************************** 164 * IMalloc32_AddRefRelease [VTABLE] 165 */ 166 static ULONG WINAPI IMalloc_fnAddRefRelease(IMalloc *iface) 167 { 168 return 1; 169 } 170 171 /****************************************************************************** 172 * IMalloc32_Alloc [VTABLE] 173 */ 174 static void * WINAPI IMalloc_fnAlloc(IMalloc *iface, SIZE_T cb) 175 { 176 void *addr; 177 178 TRACE("(%ld)\n",cb); 179 180 if(Malloc32.pSpy) { 181 SIZE_T preAllocResult; 182 183 EnterCriticalSection(&IMalloc32_SpyCS); 184 preAllocResult = IMallocSpy_PreAlloc(Malloc32.pSpy, cb); 185 if ((cb != 0) && (preAllocResult == 0)) { 186 /* PreAlloc can force Alloc to fail, but not if cb == 0 */ 187 TRACE("returning null\n"); 188 LeaveCriticalSection(&IMalloc32_SpyCS); 189 return NULL; 190 } 191 } 192 193 addr = HeapAlloc(GetProcessHeap(),0,cb); 194 195 if(Malloc32.pSpy) { 196 addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr); 197 if (addr) AddMemoryLocation(addr); 198 LeaveCriticalSection(&IMalloc32_SpyCS); 199 } 200 201 TRACE("--(%p)\n",addr); 202 return addr; 203 } 204 205 /****************************************************************************** 206 * IMalloc32_Realloc [VTABLE] 207 */ 208 static void * WINAPI IMalloc_fnRealloc(IMalloc *iface, void *pv, SIZE_T cb) 209 { 210 void *pNewMemory; 211 212 TRACE("(%p,%ld)\n",pv,cb); 213 214 if(Malloc32.pSpy) { 215 void *pRealMemory; 216 BOOL fSpyed; 217 218 EnterCriticalSection(&IMalloc32_SpyCS); 219 fSpyed = RemoveMemoryLocation(pv); 220 cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed); 221 222 /* check if can release the spy */ 223 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { 224 IMallocSpy_Release(Malloc32.pSpy); 225 Malloc32.SpyReleasePending = FALSE; 226 Malloc32.pSpy = NULL; 227 LeaveCriticalSection(&IMalloc32_SpyCS); 228 } 229 230 if (0==cb) { 231 /* PreRealloc can force Realloc to fail */ 232 if (Malloc32.pSpy) 233 LeaveCriticalSection(&IMalloc32_SpyCS); 234 return NULL; 235 } 236 237 pv = pRealMemory; 238 } 239 240 if (!pv) pNewMemory = HeapAlloc(GetProcessHeap(),0,cb); 241 else if (cb) pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb); 242 else { 243 HeapFree(GetProcessHeap(),0,pv); 244 pNewMemory = NULL; 245 } 246 247 if(Malloc32.pSpy) { 248 pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE); 249 if (pNewMemory) AddMemoryLocation(pNewMemory); 250 LeaveCriticalSection(&IMalloc32_SpyCS); 251 } 252 253 TRACE("--(%p)\n",pNewMemory); 254 return pNewMemory; 255 } 256 257 /****************************************************************************** 258 * IMalloc32_Free [VTABLE] 259 */ 260 static void WINAPI IMalloc_fnFree(IMalloc *iface, void *pv) 261 { 262 BOOL fSpyed = FALSE; 263 264 TRACE("(%p)\n",pv); 265 266 if(!pv) 267 return; 268 269 if(Malloc32.pSpy) { 270 EnterCriticalSection(&IMalloc32_SpyCS); 271 fSpyed = RemoveMemoryLocation(pv); 272 pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed); 273 } 274 275 HeapFree(GetProcessHeap(),0,pv); 276 277 if(Malloc32.pSpy) { 278 IMallocSpy_PostFree(Malloc32.pSpy, fSpyed); 279 280 /* check if can release the spy */ 281 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { 282 IMallocSpy_Release(Malloc32.pSpy); 283 Malloc32.SpyReleasePending = FALSE; 284 Malloc32.pSpy = NULL; 285 } 286 287 LeaveCriticalSection(&IMalloc32_SpyCS); 288 } 289 } 290 291 /****************************************************************************** 292 * IMalloc32_GetSize [VTABLE] 293 * 294 * NOTES 295 * FIXME returns: 296 * win95: size allocated (4 byte boundarys) 297 * win2k: size originally requested !!! (allocated on 8 byte boundarys) 298 */ 299 static SIZE_T WINAPI IMalloc_fnGetSize(IMalloc *iface, void *mem) 300 { 301 BOOL spyed_block = FALSE, spy_active = FALSE; 302 SIZE_T size; 303 304 TRACE("(%p)\n", mem); 305 306 if (!mem) 307 return (SIZE_T)-1; 308 309 if (Malloc32.pSpy) 310 { 311 EnterCriticalSection(&IMalloc32_SpyCS); 312 spyed_block = !!mallocspy_is_allocation_spyed(mem); 313 spy_active = TRUE; 314 mem = IMallocSpy_PreGetSize(Malloc32.pSpy, mem, spyed_block); 315 } 316 317 size = HeapSize(GetProcessHeap(), 0, mem); 318 319 if (spy_active) 320 { 321 size = IMallocSpy_PostGetSize(Malloc32.pSpy, size, spyed_block); 322 LeaveCriticalSection(&IMalloc32_SpyCS); 323 } 324 return size; 325 } 326 327 /****************************************************************************** 328 * IMalloc32_DidAlloc [VTABLE] 329 */ 330 static INT WINAPI IMalloc_fnDidAlloc(IMalloc *iface, void *mem) 331 { 332 BOOL spyed_block = FALSE, spy_active = FALSE; 333 int did_alloc; 334 335 TRACE("(%p)\n", mem); 336 337 if (!mem) 338 return -1; 339 340 if (Malloc32.pSpy) 341 { 342 EnterCriticalSection(&IMalloc32_SpyCS); 343 spyed_block = !!mallocspy_is_allocation_spyed(mem); 344 spy_active = TRUE; 345 mem = IMallocSpy_PreDidAlloc(Malloc32.pSpy, mem, spyed_block); 346 } 347 348 did_alloc = HeapValidate(GetProcessHeap(), 0, mem); 349 350 if (spy_active) 351 { 352 did_alloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, mem, spyed_block, did_alloc); 353 LeaveCriticalSection(&IMalloc32_SpyCS); 354 } 355 356 return did_alloc; 357 } 358 359 /****************************************************************************** 360 * IMalloc32_HeapMinimize [VTABLE] 361 */ 362 static void WINAPI IMalloc_fnHeapMinimize(IMalloc *iface) 363 { 364 TRACE("()\n"); 365 366 if(Malloc32.pSpy) { 367 EnterCriticalSection(&IMalloc32_SpyCS); 368 IMallocSpy_PreHeapMinimize(Malloc32.pSpy); 369 } 370 371 if(Malloc32.pSpy) { 372 IMallocSpy_PostHeapMinimize(Malloc32.pSpy); 373 LeaveCriticalSection(&IMalloc32_SpyCS); 374 } 375 } 376 377 static const IMallocVtbl VT_IMalloc32 = 378 { 379 IMalloc_fnQueryInterface, 380 IMalloc_fnAddRefRelease, 381 IMalloc_fnAddRefRelease, 382 IMalloc_fnAlloc, 383 IMalloc_fnRealloc, 384 IMalloc_fnFree, 385 IMalloc_fnGetSize, 386 IMalloc_fnDidAlloc, 387 IMalloc_fnHeapMinimize 388 }; 389 390 /****************************************************************************** 391 * CoGetMalloc [OLE32.@] 392 * 393 * Retrieves the current IMalloc interface for the process. 394 * 395 * PARAMS 396 * context [I] Should always be MEMCTX_TASK. 397 * imalloc [O] Address where memory allocator object will be stored. 398 * 399 * RETURNS 400 * Success: S_OK. 401 * Failure: HRESULT code. 402 */ 403 HRESULT WINAPI CoGetMalloc(DWORD context, IMalloc **imalloc) 404 { 405 if (context != MEMCTX_TASK) { 406 *imalloc = NULL; 407 return E_INVALIDARG; 408 } 409 410 *imalloc = &Malloc32.IMalloc_iface; 411 return S_OK; 412 } 413 414 /*********************************************************************** 415 * CoTaskMemAlloc [OLE32.@] 416 * 417 * Allocates memory using the current process memory allocator. 418 * 419 * PARAMS 420 * size [I] Size of the memory block to allocate. 421 * 422 * RETURNS 423 * Success: Pointer to newly allocated memory block. 424 * Failure: NULL. 425 */ 426 LPVOID WINAPI CoTaskMemAlloc(SIZE_T size) 427 { 428 return IMalloc_Alloc(&Malloc32.IMalloc_iface,size); 429 } 430 431 /*********************************************************************** 432 * CoTaskMemFree [OLE32.@] 433 * 434 * Frees memory allocated from the current process memory allocator. 435 * 436 * PARAMS 437 * ptr [I] Memory block to free. 438 * 439 * RETURNS 440 * Nothing. 441 */ 442 VOID WINAPI CoTaskMemFree(LPVOID ptr) 443 { 444 IMalloc_Free(&Malloc32.IMalloc_iface, ptr); 445 } 446 447 /*********************************************************************** 448 * CoTaskMemRealloc [OLE32.@] 449 * 450 * Allocates memory using the current process memory allocator. 451 * 452 * PARAMS 453 * pvOld [I] Pointer to old memory block. 454 * size [I] Size of the new memory block. 455 * 456 * RETURNS 457 * Success: Pointer to newly allocated memory block. 458 * Failure: NULL. 459 */ 460 LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, SIZE_T size) 461 { 462 return IMalloc_Realloc(&Malloc32.IMalloc_iface, pvOld, size); 463 } 464 465 /*********************************************************************** 466 * CoRegisterMallocSpy [OLE32.@] 467 * 468 * Registers an object that receives notifications on memory allocations and 469 * frees. 470 * 471 * PARAMS 472 * pMallocSpy [I] New spy object. 473 * 474 * RETURNS 475 * Success: S_OK. 476 * Failure: HRESULT code. 477 * 478 * NOTES 479 * if a mallocspy is already registered, we can't do it again since 480 * only the spy knows, how to free a memory block 481 */ 482 HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy) 483 { 484 IMallocSpy* pSpy; 485 HRESULT hres = E_INVALIDARG; 486 487 TRACE("%p\n", pMallocSpy); 488 489 if(!pMallocSpy) return E_INVALIDARG; 490 491 EnterCriticalSection(&IMalloc32_SpyCS); 492 493 if (Malloc32.pSpy) 494 hres = CO_E_OBJISREG; 495 else if (SUCCEEDED(IMallocSpy_QueryInterface(pMallocSpy, &IID_IMallocSpy, (void**)&pSpy))) { 496 Malloc32.pSpy = pSpy; 497 hres = S_OK; 498 } 499 500 LeaveCriticalSection(&IMalloc32_SpyCS); 501 502 return hres; 503 } 504 505 /*********************************************************************** 506 * CoRevokeMallocSpy [OLE32.@] 507 * 508 * Revokes a previously registered object that receives notifications on memory 509 * allocations and frees. 510 * 511 * PARAMS 512 * pMallocSpy [I] New spy object. 513 * 514 * RETURNS 515 * Success: S_OK. 516 * Failure: HRESULT code. 517 * 518 * NOTES 519 * we can't revoke a malloc spy as long as memory blocks allocated with 520 * the spy are active since only the spy knows how to free them 521 */ 522 HRESULT WINAPI CoRevokeMallocSpy(void) 523 { 524 HRESULT hres = S_OK; 525 TRACE("\n"); 526 527 EnterCriticalSection(&IMalloc32_SpyCS); 528 529 if (!Malloc32.pSpy) 530 hres = CO_E_OBJNOTREG; 531 else if (Malloc32.SpyedAllocationsLeft) { 532 TRACE("SpyReleasePending with %u allocations left\n", Malloc32.SpyedAllocationsLeft); 533 Malloc32.SpyReleasePending = TRUE; 534 hres = E_ACCESSDENIED; 535 } else { 536 IMallocSpy_Release(Malloc32.pSpy); 537 Malloc32.pSpy = NULL; 538 } 539 LeaveCriticalSection(&IMalloc32_SpyCS); 540 541 return hres; 542 } 543 544 /****************************************************************************** 545 * IsValidInterface [OLE32.@] 546 * 547 * Determines whether a pointer is a valid interface. 548 * 549 * PARAMS 550 * punk [I] Interface to be tested. 551 * 552 * RETURNS 553 * TRUE, if the passed pointer is a valid interface, or FALSE otherwise. 554 */ 555 BOOL WINAPI IsValidInterface(LPUNKNOWN punk) 556 { 557 return !( 558 IsBadReadPtr(punk,4) || 559 IsBadReadPtr(punk->lpVtbl,4) || 560 IsBadReadPtr(punk->lpVtbl->QueryInterface,9) || 561 IsBadCodePtr((FARPROC)punk->lpVtbl->QueryInterface) 562 ); 563 } 564