From 613a9964a1eb95450d2ecd9fbe25f78c2ad425c9 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 14 Jan 2023 12:43:04 +1000 Subject: [PATCH] FullscreenUI: Fix descriptor use-after-free when deleting state --- pcsx2/Frontend/FullscreenUI.cpp | 226 +++++++++++++++++--------------- 1 file changed, 117 insertions(+), 109 deletions(-) diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 7fe8c23294..83c2ebba2c 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -402,8 +402,7 @@ namespace FullscreenUI s32 slot; }; - static void InitializePlaceholderSaveStateListEntry( - SaveStateListEntry* li, const std::string& title, const std::string& serial, u32 crc, s32 slot); + static void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot); static bool InitializeSaveStateListEntry( SaveStateListEntry* li, const std::string& title, const std::string& serial, u32 crc, s32 slot); static void ClearSaveStateEntryList(); @@ -4496,8 +4495,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type) #endif } -void FullscreenUI::InitializePlaceholderSaveStateListEntry( - SaveStateListEntry* li, const std::string& title, const std::string& serial, u32 crc, s32 slot) +void FullscreenUI::InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot) { li->title = (slot == 0) ? std::string("Quick Save Slot") : fmt::format("Save Slot {0}##game_slot_{0}", slot); li->summary = "No save present in this slot."; @@ -4514,7 +4512,7 @@ bool FullscreenUI::InitializeSaveStateListEntry( FILESYSTEM_STAT_DATA sd; if (filename.empty() || !FileSystem::StatFile(filename.c_str(), &sd)) { - InitializePlaceholderSaveStateListEntry(li, title, serial, crc, slot); + InitializePlaceholderSaveStateListEntry(li, slot); return false; } @@ -4686,16 +4684,126 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) u32 grid_x = 0; ImGui::SetCursorPos(ImVec2(start_x, 0.0f)); - for (u32 i = 0; i < s_save_state_selector_slots.size(); i++) + for (u32 i = 0; i < s_save_state_selector_slots.size();) { if (i == 0) ResetFocusHere(); - const SaveStateListEntry& entry = s_save_state_selector_slots[i]; + if (static_cast(i) == s_save_state_selector_submenu_index) + { + SaveStateListEntry& entry = s_save_state_selector_slots[i]; + + // can't use a choice dialog here, because we're already in a modal... + ImGuiFullscreen::PushResetLayout(); + ImGui::PushFont(g_large_font); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f)); + + const float width = LayoutScale(600.0f); + const float title_height = + g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f; + const float height = + title_height + LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + (LAYOUT_MENU_BUTTON_Y_PADDING * 2.0f)) * 3.0f; + ImGui::SetNextWindowSize(ImVec2(width, height)); + ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + ImGui::OpenPopup(entry.title.c_str()); + + // don't let the back button flow through to the main window + bool submenu_open = !WantsToCloseMenu(); + close_handled ^= submenu_open; + + bool closed = false; + if (ImGui::BeginPopupModal( + entry.title.c_str(), &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + { + ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + + BeginMenuButtons(); + + if (ActiveButton(is_loading ? ICON_FA_FOLDER_OPEN " Load State" : ICON_FA_FOLDER_OPEN " Save State", false, true, + LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) + { + if (is_loading) + DoLoadState(std::move(entry.path)); + else + Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); }); + + CloseSaveStateSelector(); + closed = true; + } + + if (ActiveButton(ICON_FA_FOLDER_MINUS " Delete Save", false, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) + { + if (!FileSystem::FileExists(entry.path.c_str())) + { + ShowToast({}, fmt::format("{} does not exist.", ImGuiFullscreen::RemoveHash(entry.title))); + is_open = true; + } + else if (FileSystem::DeleteFilePath(entry.path.c_str())) + { + ShowToast({}, fmt::format("{} deleted.", ImGuiFullscreen::RemoveHash(entry.title))); + if (s_save_state_selector_loading) + s_save_state_selector_slots.erase(s_save_state_selector_slots.begin() + i); + else + InitializePlaceholderSaveStateListEntry(&entry, entry.slot); + + // Close if this was the last state. + if (s_save_state_selector_slots.empty()) + { + CloseSaveStateSelector(); + closed = true; + } + else + { + is_open = false; + } + } + else + { + ShowToast({}, fmt::format("Failed to delete {}.", ImGuiFullscreen::RemoveHash(entry.title))); + is_open = false; + } + } + + if (ActiveButton(ICON_FA_WINDOW_CLOSE " Close Menu", false, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) + { + is_open = false; + } + + EndMenuButtons(); + + ImGui::PopStyleColor(); + ImGui::EndPopup(); + } + if (!is_open) + { + s_save_state_selector_submenu_index = -1; + if (!closed) + QueueResetFocus(); + } + + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(3); + ImGui::PopFont(); + ImGuiFullscreen::PopResetLayout(); + + if (closed || i >= s_save_state_selector_slots.size()) + break; + } + ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) + { + i++; continue; + } + const SaveStateListEntry& entry = s_save_state_selector_slots[i]; const ImGuiID id = window->GetID(static_cast(i)); const ImVec2 pos(window->DC.CursorPos); ImRect bb(pos, pos + item_size); @@ -4765,108 +4873,6 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) { s_save_state_selector_submenu_index = static_cast(i); } - - if (static_cast(i) == s_save_state_selector_submenu_index) - { - // can't use a choice dialog here, because we're already in a modal... - ImGuiFullscreen::PushResetLayout(); - ImGui::PushFont(g_large_font); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); - ImGui::PushStyleVar( - ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); - ImGui::PushStyleColor(ImGuiCol_PopupBg, MulAlpha(UIBackgroundColor, 0.95f)); - - const float width = LayoutScale(600.0f); - const float title_height = - g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f; - const float height = - title_height + LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + (LAYOUT_MENU_BUTTON_Y_PADDING * 2.0f)) * 3.0f; - ImGui::SetNextWindowSize(ImVec2(width, height)); - ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup(entry.title.c_str()); - - // don't let the back button flow through to the main window - bool submenu_open = !WantsToCloseMenu(); - close_handled ^= submenu_open; - - bool closed = false; - if (ImGui::BeginPopupModal(entry.title.c_str(), &is_open, - ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) - { - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); - - BeginMenuButtons(); - - if (ActiveButton(is_loading ? ICON_FA_FOLDER_OPEN " Load State" : ICON_FA_FOLDER_OPEN " Save State", false, true, - LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) - { - if (is_loading) - DoLoadState(std::move(entry.path)); - else - Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); }); - - CloseSaveStateSelector(); - closed = true; - } - - if (ActiveButton(ICON_FA_FOLDER_MINUS " Delete Save", false, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) - { - if (!FileSystem::FileExists(entry.path.c_str())) - { - ShowToast({}, fmt::format("{} does not exist.", ImGuiFullscreen::RemoveHash(entry.title))); - is_open = true; - } - else if (FileSystem::DeleteFilePath(entry.path.c_str())) - { - ShowToast({}, fmt::format("{} deleted.", ImGuiFullscreen::RemoveHash(entry.title))); - s_save_state_selector_slots.erase(s_save_state_selector_slots.begin() + i); - - if (s_save_state_selector_slots.empty()) - { - CloseSaveStateSelector(); - closed = true; - } - else - { - is_open = false; - } - } - else - { - ShowToast({}, fmt::format("Failed to delete {}.", ImGuiFullscreen::RemoveHash(entry.title))); - is_open = false; - } - } - - if (ActiveButton(ICON_FA_WINDOW_CLOSE " Close Menu", false, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) - { - is_open = false; - } - - EndMenuButtons(); - - ImGui::PopStyleColor(); - ImGui::EndPopup(); - } - if (!is_open) - { - s_save_state_selector_submenu_index = -1; - if (!closed) - QueueResetFocus(); - } - - ImGui::PopStyleColor(4); - ImGui::PopStyleVar(3); - ImGui::PopFont(); - ImGuiFullscreen::PopResetLayout(); - - if (closed) - break; - } } grid_x++; @@ -4880,6 +4886,8 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) { ImGui::SameLine(start_x + static_cast(grid_x) * (item_width + item_spacing)); } + + i++; } EndMenuButtons();