From 4cc4a6561c0e950bef4cff36c74099d0e4270ef5 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 11 May 2024 21:21:03 +1000 Subject: [PATCH] GS: Fix use-after-free on lost device --- pcsx2/GS/GS.cpp | 72 +++++++++++++++--------- pcsx2/GS/GS.h | 3 +- pcsx2/GS/Renderers/Common/GSRenderer.cpp | 7 +-- pcsx2/GS/Renderers/Common/GSRenderer.h | 2 - 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 0bc78a50f3..9fa2c6f882 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -210,14 +210,24 @@ static void CloseGSRenderer() } } -bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional old_config) +bool GSreopen(bool recreate_device, bool recreate_renderer, GSRendererType new_renderer, + std::optional old_config) { Console.WriteLn("Reopening GS with %s device", recreate_device ? "new" : "existing"); g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN); - if (GSConfig.UserHacks_ReadTCOnClose) + if (recreate_device && !recreate_renderer) + { + // Keeping the renderer around, this probably means we lost the device, so toss everything. + g_gs_renderer->PurgeTextureCache(true, true, true); + g_gs_device->ClearCurrent(); + g_gs_device->PurgePool(); + } + else if (GSConfig.UserHacks_ReadTCOnClose) + { g_gs_renderer->ReadbackTextureCache(); + } std::string capture_filename; GSVector2i capture_size; @@ -232,21 +242,25 @@ bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optionalGetRegsMem(); freezeData fd = {}; - if (g_gs_renderer->Freeze(&fd, true) != 0) + std::unique_ptr fd_data; + if (recreate_renderer) { - Console.Error("(GSreopen) Failed to get GS freeze size"); - return false; - } + if (g_gs_renderer->Freeze(&fd, true) != 0) + { + Console.Error("(GSreopen) Failed to get GS freeze size"); + return false; + } - std::unique_ptr fd_data = std::make_unique(fd.size); - fd.data = fd_data.get(); - if (g_gs_renderer->Freeze(&fd, false) != 0) - { - Console.Error("(GSreopen) Failed to freeze GS"); - return false; - } + fd_data = std::make_unique(fd.size); + fd.data = fd_data.get(); + if (g_gs_renderer->Freeze(&fd, false) != 0) + { + Console.Error("(GSreopen) Failed to freeze GS"); + return false; + } - CloseGSRenderer(); + CloseGSRenderer(); + } if (recreate_device) { @@ -274,16 +288,19 @@ bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optionalDefrost(&fd) != 0) - { - Console.Error("(GSreopen) Failed to defrost"); - return false; + if (g_gs_renderer->Defrost(&fd) != 0) + { + Console.Error("(GSreopen) Failed to defrost"); + return false; + } } if (!capture_filename.empty()) @@ -693,7 +710,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config) // Options which need a full teardown/recreate. if (!GSConfig.RestartOptionsAreEqual(old_config)) { - if (!GSreopen(true, GSConfig.Renderer, &old_config)) + if (!GSreopen(true, true, GSConfig.Renderer, &old_config)) pxFailRel("Failed to do full GS reopen"); return; } @@ -702,7 +719,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config) if (GSConfig.SWExtraThreads != old_config.SWExtraThreads || GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight) { - if (!GSreopen(false, GSConfig.Renderer, &old_config)) + if (!GSreopen(false, true, GSConfig.Renderer, &old_config)) pxFailRel("Failed to do quick GS reopen"); return; @@ -738,7 +755,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config) if (GSConfig.UserHacks_ReadTCOnClose) g_gs_renderer->ReadbackTextureCache(); g_gs_renderer->PurgeTextureCache(true, true, true); - g_gs_renderer->PurgePool(); + g_gs_device->ClearCurrent(); + g_gs_device->PurgePool(); } // clear out the sampler cache when AF options change, since the anisotropy gets baked into them @@ -776,7 +794,7 @@ void GSSetSoftwareRendering(bool software_renderer, GSInterlaceMode new_interlac // Config might be SW, and we're switching to HW -> use Auto. const GSRendererType renderer = (software_renderer ? GSRendererType::SW : (GSConfig.Renderer == GSRendererType::SW ? GSRendererType::Auto : GSConfig.Renderer)); - if (!GSreopen(false, renderer, std::nullopt)) + if (!GSreopen(false, true, renderer, std::nullopt)) pxFailRel("Failed to reopen GS for renderer switch."); } } @@ -1115,7 +1133,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic MTGS::RunOnGSThread([new_level]() { GSConfig.HWMipmap = new_level; g_gs_renderer->PurgeTextureCache(true, false, true); - g_gs_renderer->PurgePool(); + g_gs_device->PurgePool(); }); }}, {"CycleInterlaceMode", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Cycle Deinterlace Mode"), diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index 69b7dfb063..4b42cd32ef 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -57,7 +57,8 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name); s16 GSLookupMoveHandlerFunctionId(const std::string_view& name); bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem); -bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional old_config); +bool GSreopen(bool recreate_device, bool recreate_renderer, GSRendererType new_renderer, + std::optional old_config); void GSreset(bool hardware_reset); void GSclose(); void GSgifSoftReset(u32 mask); diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.cpp b/pcsx2/GS/Renderers/Common/GSRenderer.cpp index 8dc7535277..46f7bbe231 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.cpp +++ b/pcsx2/GS/Renderers/Common/GSRenderer.cpp @@ -75,11 +75,6 @@ void GSRenderer::Destroy() GSCapture::EndCapture(); } -void GSRenderer::PurgePool() -{ - g_gs_device->PurgePool(); -} - void GSRenderer::UpdateRenderFixes() { } @@ -518,7 +513,7 @@ bool GSRenderer::BeginPresentFrame(bool frame_skip) // Device lost, something went really bad. // Let's just toss out everything, and try to hobble on. - if (!GSreopen(true, GSGetCurrentRenderer(), std::nullopt)) + if (!GSreopen(true, false, GSGetCurrentRenderer(), std::nullopt)) { pxFailRel("Failed to recreate GS device after loss."); return false; diff --git a/pcsx2/GS/Renderers/Common/GSRenderer.h b/pcsx2/GS/Renderers/Common/GSRenderer.h index bcd65f3726..46f0dd6088 100644 --- a/pcsx2/GS/Renderers/Common/GSRenderer.h +++ b/pcsx2/GS/Renderers/Common/GSRenderer.h @@ -44,8 +44,6 @@ public: virtual void UpdateRenderFixes(); - void PurgePool(); - virtual void VSync(u32 field, bool registers_written, bool idle_frame); virtual bool CanUpscale() { return false; } virtual float GetUpscaleMultiplier() { return 1.0f; }