xref: /reactos/dll/win32/ole32/ifs.c (revision 8bd6b9b6)
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