From 97dee9349b87874fdb40ab6ed304528042addfe7 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Thu, 18 Jun 2026 08:35:03 +0200 Subject: [PATCH] Fix use-after-free in CallAfter lambda; replace raw thread pointer with unique_ptr --- src/slic3r/GUI/WebGuideDialog.cpp | 20 +++++++++++--------- src/slic3r/GUI/WebGuideDialog.hpp | 8 +++++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index bfe0b2abbe..2948e64128 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -1,4 +1,4 @@ -#include "WebGuideDialog.hpp" +#include "WebGuideDialog.hpp" #include "ConfigWizard.hpp" #include @@ -191,11 +191,10 @@ GuideFrame::GuideFrame(GUI_App *pGUI, long style) GuideFrame::~GuideFrame() { m_destroy = true; - if (m_load_task && m_load_task->joinable()) { + *m_cancel_token = true; // signal any queued CallAfter lambdas before join + if (m_load_task && m_load_task->joinable()) m_load_task->join(); - delete m_load_task; - m_load_task = nullptr; - } + m_load_task.reset(); if (m_browser) { delete m_browser; m_browser = nullptr; @@ -362,11 +361,11 @@ void GuideFrame::OnNavigationComplete(wxWebViewEvent &evt) } } else { // Steps 3+4 are slow — delegate to background thread. - m_load_task = new boost::thread(boost::bind(&GuideFrame::LoadProfileData, this)); + m_load_task = std::make_unique(boost::bind(&GuideFrame::LoadProfileData, this)); } } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", init error: " << e.what(); - m_load_task = new boost::thread(boost::bind(&GuideFrame::LoadProfileData, this)); + m_load_task = std::make_unique(boost::bind(&GuideFrame::LoadProfileData, this)); } } @@ -1586,8 +1585,11 @@ int GuideFrame::LoadProfileData() if (!m_destroy) save_guide_cache(); - wxGetApp().CallAfter([this] { - if (!m_destroy) + // Capture the cancel token by value (shared_ptr) so the lambda doesn't + // touch `this` if GuideFrame is destroyed before the event fires. + auto tok = m_cancel_token; + wxGetApp().CallAfter([this, tok] { + if (!*tok) on_profile_loaded(); }); } catch (const std::exception& e) { diff --git a/src/slic3r/GUI/WebGuideDialog.hpp b/src/slic3r/GUI/WebGuideDialog.hpp index bc23b8b2b2..f063596ad9 100644 --- a/src/slic3r/GUI/WebGuideDialog.hpp +++ b/src/slic3r/GUI/WebGuideDialog.hpp @@ -31,10 +31,13 @@ #include "slic3r/Utils/PresetUpdater.hpp" #include +#include #include #include +#include + namespace Slic3r { namespace GUI { class GuideFrame : public DPIDialog @@ -120,7 +123,10 @@ private: //First Load bool bFirstComplete{false}; std::atomic m_destroy{false}; - boost::thread* m_load_task{ nullptr }; + // Shared cancel token captured by CallAfter lambdas so they don't touch + // `this` after the destructor has run and the object is freed. + std::shared_ptr> m_cancel_token{std::make_shared>(false)}; + std::unique_ptr m_load_task; // User Config bool PrivacyUse;