[PATCH] dxgi: Avoid a double free in d3d11_swapchain_init().

Zhiyi Zhang zzhang at codeweavers.com
Mon Feb 17 20:27:09 CST 2020


If IDXGISwapChain1_GetContainingOutput() or dxgi_swapchain_set_fullscreen_state() fails,
wined3d_swapchain_decref() is called to free the private store and d3d11_swapchain, which
are freed again later in d3d11_swapchain_init() and upper functions. In this error path,
there are also use-after-frees for a IDXGIFactory and IDXGIDevice. This error could be
produced by creating a swapchain using a device window on the secondary monitor as of now.

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 dlls/dxgi/swapchain.c     |  4 ++--
 dlls/wined3d/device.c     |  9 +++++++++
 dlls/wined3d/swapchain.c  | 14 ++------------
 dlls/wined3d/wined3d.spec |  1 +
 include/wine/wined3d.h    |  1 +
 5 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c
index 004bd1457d..7ac5a881c2 100644
--- a/dlls/dxgi/swapchain.c
+++ b/dlls/dxgi/swapchain.c
@@ -898,7 +898,7 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi
                 &swapchain->target)))
         {
             WARN("Failed to get target output for fullscreen swapchain, hr %#x.\n", hr);
-            wined3d_swapchain_decref(swapchain->wined3d_swapchain);
+            wined3d_device_destroy_swapchain(device->wined3d_device, swapchain->wined3d_swapchain);
             goto cleanup;
         }
 
@@ -906,7 +906,7 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi
         {
             WARN("Failed to set fullscreen state, hr %#x.\n", hr);
             IDXGIOutput_Release(swapchain->target);
-            wined3d_swapchain_decref(swapchain->wined3d_swapchain);
+            wined3d_device_destroy_swapchain(device->wined3d_device, swapchain->wined3d_swapchain);
             goto cleanup;
         }
 
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 8405643741..f754a2bd49 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -888,6 +888,15 @@ void wined3d_device_destroy_default_samplers(struct wined3d_device *device, stru
     device->null_sampler = NULL;
 }
 
+void CDECL wined3d_device_destroy_swapchain(struct wined3d_device *device, struct wined3d_swapchain *swapchain)
+{
+    if (device->swapchain_count && device->swapchains[0] == swapchain)
+        wined3d_device_uninit_3d(device);
+
+    wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
+    device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain);
+}
+
 HRESULT CDECL wined3d_device_acquire_focus_window(struct wined3d_device *device, HWND window)
 {
     unsigned int screensaver_active;
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c
index 62bab65fcf..7648b99260 100644
--- a/dlls/wined3d/swapchain.c
+++ b/dlls/wined3d/swapchain.c
@@ -129,18 +129,9 @@ ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
 
     if (!refcount)
     {
-        struct wined3d_device *device;
-
         wined3d_mutex_lock();
-
-        device = swapchain->device;
-        if (device->swapchain_count && device->swapchains[0] == swapchain)
-            wined3d_device_uninit_3d(device);
-        wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
-
         swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
-        swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain);
-
+        wined3d_device_destroy_swapchain(swapchain->device, swapchain);
         wined3d_mutex_unlock();
     }
 
@@ -1081,8 +1072,7 @@ HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, struct win
         wined3d_mutex_lock();
         if (FAILED(hr = wined3d_device_set_implicit_swapchain(device, object)))
         {
-            wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
-            device->adapter->adapter_ops->adapter_destroy_swapchain(object);
+            wined3d_device_destroy_swapchain(device, object);
             wined3d_mutex_unlock();
             return hr;
         }
diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec
index c8ef442c72..1c198c121b 100644
--- a/dlls/wined3d/wined3d.spec
+++ b/dlls/wined3d/wined3d.spec
@@ -47,6 +47,7 @@
 @ cdecl wined3d_device_copy_uav_counter(ptr ptr long ptr)
 @ cdecl wined3d_device_create(ptr long long ptr long long ptr long ptr ptr)
 @ cdecl wined3d_device_decref(ptr)
+@ cdecl wined3d_device_destroy_swapchain(ptr ptr)
 @ cdecl wined3d_device_dispatch_compute(ptr long long long)
 @ cdecl wined3d_device_dispatch_compute_indirect(ptr ptr long)
 @ cdecl wined3d_device_draw_indexed_primitive(ptr long long)
diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h
index d313c7aec8..1bcae83d94 100644
--- a/include/wine/wined3d.h
+++ b/include/wine/wined3d.h
@@ -2313,6 +2313,7 @@ HRESULT __cdecl wined3d_device_create(struct wined3d *wined3d, unsigned int adap
         const enum wined3d_feature_level *feature_levels, unsigned int feature_level_count,
         struct wined3d_device_parent *device_parent, struct wined3d_device **device);
 ULONG __cdecl wined3d_device_decref(struct wined3d_device *device);
+void __cdecl wined3d_device_destroy_swapchain(struct wined3d_device *device, struct wined3d_swapchain *swapchain);
 void __cdecl wined3d_device_dispatch_compute(struct wined3d_device *device,
         unsigned int group_count_x, unsigned int group_count_y, unsigned int group_count_z);
 void __cdecl wined3d_device_dispatch_compute_indirect(struct wined3d_device *device,
-- 
2.20.1



More information about the wine-devel mailing list