If multiple threads are creating and releasing the factory, these accesses are racy and can result in callers of DXCoreCreateAdapterFactory getting a null pointer or a pointer to freed memory.
From: Tim Clem tclem@codeweavers.com
If multiple threads are creating and releasing the factory, these accesses are racy and can result in callers of DXCoreCreateAdapterFactory getting a null pointer or a pointer to freed memory. --- dlls/dxcore/dxcore.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/dlls/dxcore/dxcore.c b/dlls/dxcore/dxcore.c index 71cdca9412a..3de9ce99220 100644 --- a/dlls/dxcore/dxcore.c +++ b/dlls/dxcore/dxcore.c @@ -29,6 +29,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(dxcore);
static struct dxcore_adapter_factory *dxcore_adapter_factory;
+static CRITICAL_SECTION factory_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &factory_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": factory_section") } +}; +static CRITICAL_SECTION factory_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + struct dxcore_adapter { IDXCoreAdapter IDXCoreAdapter_iface; @@ -428,18 +437,26 @@ static HRESULT STDMETHODCALLTYPE dxcore_adapter_factory_QueryInterface(IDXCoreAd static ULONG STDMETHODCALLTYPE dxcore_adapter_factory_AddRef(IDXCoreAdapterFactory *iface) { struct dxcore_adapter_factory *factory = impl_from_IDXCoreAdapterFactory(iface); - ULONG refcount = InterlockedIncrement(&factory->refcount); + ULONG refcount; + + EnterCriticalSection(&factory_section);
+ refcount = InterlockedIncrement(&factory->refcount); TRACE("%p increasing refcount to %lu.\n", iface, refcount);
+ LeaveCriticalSection(&factory_section); + return refcount; }
static ULONG STDMETHODCALLTYPE dxcore_adapter_factory_Release(IDXCoreAdapterFactory *iface) { struct dxcore_adapter_factory *factory = impl_from_IDXCoreAdapterFactory(iface); - ULONG refcount = InterlockedDecrement(&factory->refcount); + ULONG refcount; + + EnterCriticalSection(&factory_section);
+ refcount = InterlockedDecrement(&factory->refcount); TRACE("%p decreasing refcount to %lu.\n", iface, refcount);
if (!refcount) @@ -448,6 +465,8 @@ static ULONG STDMETHODCALLTYPE dxcore_adapter_factory_Release(IDXCoreAdapterFact dxcore_adapter_factory = NULL; }
+ LeaveCriticalSection(&factory_section); + return refcount; }
@@ -614,15 +633,20 @@ static const struct IDXCoreAdapterFactoryVtbl dxcore_adapter_factory_vtbl =
HRESULT STDMETHODCALLTYPE DXCoreCreateAdapterFactory(REFIID riid, void **out) { + HRESULT hr; + TRACE("riid %s, out %p\n", debugstr_guid(riid), out);
if (!out) return E_POINTER;
+ EnterCriticalSection(&factory_section); + if (!dxcore_adapter_factory) { if (!(dxcore_adapter_factory = calloc(1, sizeof(*dxcore_adapter_factory)))) { + LeaveCriticalSection(&factory_section); *out = NULL; return E_OUTOFMEMORY; } @@ -631,6 +655,10 @@ HRESULT STDMETHODCALLTYPE DXCoreCreateAdapterFactory(REFIID riid, void **out) dxcore_adapter_factory->refcount = 0; }
+ hr = IDXCoreAdapterFactory_QueryInterface(&dxcore_adapter_factory->IDXCoreAdapterFactory_iface, riid, out); + + LeaveCriticalSection(&factory_section); + TRACE("created IDXCoreAdapterFactory %p.\n", *out); - return IDXCoreAdapterFactory_QueryInterface(&dxcore_adapter_factory->IDXCoreAdapterFactory_iface, riid, out); + return hr; }
It's not caused by your change, but one awkward thing is that calling with wrong IID will create a factory with 0 refcount and function will return leaving it like that. I think it's better to set global pointer only on success.
On Wed Sep 10 18:01:43 2025 +0000, Nikolay Sivov wrote:
It's not caused by your change, but one awkward thing is that calling with wrong IID will create a factory with 0 refcount and function will return leaving it like that. I think it's better to set global pointer only on success. It will get bumped to 1 on first successful call, but still having a live object with refcount == 0 is not intuitive.
Looks like native just returns E_NOINTERFACE for the wrong IID. May as well just check that up front and bail early, right?
The synchronization doesn't really look right. We should probably just make the factory static, and then there should be no need for a mutex.
On Wed Sep 10 18:20:43 2025 +0000, Elizabeth Figura wrote:
The synchronization doesn't really look right. We should probably just make the factory static, and then there should be no need for a mutex.
Huh, ok. What should we do with the refcount? Just make AddRef and Release no-ops?
On Wed Sep 10 18:20:43 2025 +0000, Tim Clem wrote:
Huh, ok. What should we do with the refcount? Just make AddRef and Release no-ops?
The refcount should stay I think; it just won't free anything on release.