From: Yuxuan Shui yshui@codeweavers.com
In wined3d_swapchain_cleanup, before decrementing the reference counts of each of the back/front buffers, we first set their swapchain to NULL, presumably to stop the current under-destruction swapchain from being used through them. But there is an oversight.
When we call wined3d_texture_decref on the front_buffer, the back_buffers still have their swapchains pointing to the swapchain being destroyed. In texture_resource_unload, we call context_acquire(device, NULL, 0), note the NULL texture parameter here. When the texture parameter is NULL, wined3d_context_gl_acquire (in turn called by context_acquire) will default to using the first back buffer from the implicit swapchain of "device", which, as previously stated, has not had their swapchain set to NULL yet. From here, we reach wined3d_context_gl_activate with a texture whose swapchain is currently being destroyed. This swapchain is then assigned to "context_gl" here before being freed, leaving a dangling pointer.
When this context_gl is acquired again later, we will try to access context_gl->c.swapchain->win_handle, thus complete the use-after-free cycle.
This commit makes sure the swapchain pointer of ALL front and back buffers are set to NULL before decrementing their reference counts.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58325 --- dlls/wined3d/swapchain.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 507d6e64bd8..2a852e40771 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -34,14 +34,28 @@ void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain)
TRACE("Destroying swapchain %p.\n", swapchain);
+ for (i = 0; i < swapchain->device->context_count; i++) + if (swapchain->device->contexts[i]->swapchain == swapchain) + swapchain->device->contexts[i]->swapchain = NULL; + wined3d_swapchain_state_cleanup(&swapchain->state); wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma);
/* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0] * is the last buffer to be destroyed, FindContext() depends on that. */ if (swapchain->front_buffer) - { wined3d_texture_set_swapchain(swapchain->front_buffer, NULL); + + if (swapchain->back_buffers) + { + i = swapchain->state.desc.backbuffer_count; + + while (i--) + wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); + } + + if (swapchain->front_buffer) + { if (wined3d_texture_decref(swapchain->front_buffer)) WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer); swapchain->front_buffer = NULL; @@ -53,7 +67,6 @@ void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain)
while (i--) { - wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL); if (wined3d_texture_decref(swapchain->back_buffers[i])) WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]); }