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 */
SetSpyedBlockTableLength(DWORD NewLength)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 */
AddMemoryLocation(LPVOID * pMem)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
mallocspy_is_allocation_spyed(const void * mem)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
RemoveMemoryLocation(LPCVOID pMem)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 */
IMalloc_fnQueryInterface(IMalloc * iface,REFIID refiid,void ** obj)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 */
IMalloc_fnAddRefRelease(IMalloc * iface)166 static ULONG WINAPI IMalloc_fnAddRefRelease(IMalloc *iface)
167 {
168 return 1;
169 }
170
171 /******************************************************************************
172 * IMalloc32_Alloc [VTABLE]
173 */
IMalloc_fnAlloc(IMalloc * iface,SIZE_T cb)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 */
IMalloc_fnRealloc(IMalloc * iface,void * pv,SIZE_T cb)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 */
IMalloc_fnFree(IMalloc * iface,void * pv)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 */
IMalloc_fnGetSize(IMalloc * iface,void * mem)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 */
IMalloc_fnDidAlloc(IMalloc * iface,void * mem)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 */
IMalloc_fnHeapMinimize(IMalloc * iface)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 */
CoGetMalloc(DWORD context,IMalloc ** imalloc)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 */
CoTaskMemAlloc(SIZE_T size)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 */
CoTaskMemFree(LPVOID ptr)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 */
CoTaskMemRealloc(LPVOID pvOld,SIZE_T size)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 */
CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy)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 */
CoRevokeMallocSpy(void)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 */
IsValidInterface(LPUNKNOWN punk)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