From dc1aef2afa73b10d693130b24e88e4e6cf6fb2e4 Mon Sep 17 00:00:00 2001 From: Walter Almada B Date: Sat, 4 Jul 2026 18:43:59 -0700 Subject: [PATCH] fix(macos): prevent use-after-free crash on quit in GLCanvas3D teardown Plater is a wxWindow subclass holding a pImpl (unique_ptr p). On app exit the member p is destroyed before the wxWindow base destructor runs DestroyChildren(), so the child View3D/GLCanvas3D windows are torn down *after* p has already been freed. GLCanvas3D::~GLCanvas3D() -> reset_volumes() then reaches back into the dead Plater via two callbacks, each dereferencing the freed p: - Selection::clear() -> plater()->canvas3D() -> p->get_current_canvas3D() - _set_warning_notification() -> plater()->get_notification_manager() -> p->notification_manager Which one fires depends on scene state (selection vs. volumes present), so quitting with a model loaded reliably crashes with EXC_BAD_ACCESS. Guard both callbacks with the existing wxGetApp().is_closing() flag (set in MainFrame's close handler and GUI_App::shutdown(), both before DoDelayedCleanup destroys the frame). Both calls are UI-only side effects that are no-ops during shutdown, so normal-use behavior is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/slic3r/GUI/GLCanvas3D.cpp | 6 ++++++ src/slic3r/GUI/Selection.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5bfdea07dc..563df3245a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -10060,6 +10060,12 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) { + // On app quit the Plater pImpl (p) is freed before its child GLCanvas3D windows are + // destroyed; reset_volumes() -> _set_warning_notification() -> get_notification_manager() + // would then dereference the freed p (use-after-free crash on exit). No warning + // notifications are needed while the app is closing. + if (wxGetApp().is_closing()) + return; using NotificationLevel = NotificationManager::NotificationLevel; enum ErrorType{ PLATER_WARNING, diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 8452b8222c..7521334956 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -760,6 +760,12 @@ void Selection::clear() #endif // #et_FIXME fake KillFocus from sidebar + // On app quit the Plater's child GLCanvas3D windows are destroyed by the wxWindow + // base destructor *after* Plater's pImpl (p) has already been freed. reset_volumes() + // -> Selection::clear() then reaches back into plater()->canvas3D(), a use-after-free + // that crashes on exit. This is a UI-only redraw hint, so skip it while closing. + if (wxGetApp().is_closing()) + return; wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false); }