From 629a2da68d3d502d977861b6c6cc9f47916a52d4 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 9 Oct 2026 10:56:52 +0200 Subject: [PATCH] fix: remove app event filters on view destruction (crash on plugin reload) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The acme, palette, radial, and switch views each call qApp->installEventFilter(this) but never removed it. QObject only auto-removes event filters installed *on itself*, not filters it installed on *another* object (here qApp). So when a plugin .so is unloaded/reloaded — e.g. reinstalling the plugin — the view is destroyed while qApp still holds a dangling pointer to it in its filter list, and the next dispatched event calls into freed memory: QCoreApplicationPrivate::sendThroughApplicationEventFilters -> SIGSEGV Add qApp->removeEventFilter(this) to each view destructor (giving AcmeView a real dtor body instead of =default). 18/18 ctest green. --- src/acme/acmeplugin.cpp | 8 +++++++- src/palette/paletteplugin.cpp | 4 ++++ src/radial/radialplugin.cpp | 4 ++++ src/switch/switchplugin.cpp | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/acme/acmeplugin.cpp b/src/acme/acmeplugin.cpp index 026c360..e4d7544 100644 --- a/src/acme/acmeplugin.cpp +++ b/src/acme/acmeplugin.cpp @@ -193,7 +193,13 @@ AcmeView::AcmeView(AcmePlugin *plugin, KTextEditor::MainWindow *mainWindow) qApp->installEventFilter(this); } -AcmeView::~AcmeView() = default; +AcmeView::~AcmeView() +{ + // Remove the application event filter we installed on qApp; QObject only + // auto-removes filters installed on itself. Without this, a plugin + // unload/reload leaves qApp with a dangling filter pointer -> crash. + qApp->removeEventFilter(this); +} QAction *AcmeView::findActionByName(const QString &nameOrText) const { diff --git a/src/palette/paletteplugin.cpp b/src/palette/paletteplugin.cpp index 74e0229..dae25fb 100644 --- a/src/palette/paletteplugin.cpp +++ b/src/palette/paletteplugin.cpp @@ -124,6 +124,10 @@ PaletteView::PaletteView(PalettePlugin *plugin, KTextEditor::MainWindow *mainWin PaletteView::~PaletteView() { + // We installed this on qApp; QObject only auto-removes filters installed on + // itself, so remove ours explicitly. Otherwise, on plugin unload/reload + // qApp keeps a dangling filter pointer and the next event crashes. + qApp->removeEventFilter(this); delete m_palette; } diff --git a/src/radial/radialplugin.cpp b/src/radial/radialplugin.cpp index 11a53bd..b8f689d 100644 --- a/src/radial/radialplugin.cpp +++ b/src/radial/radialplugin.cpp @@ -83,6 +83,10 @@ RadialView::RadialView(RadialPlugin *plugin, KTextEditor::MainWindow *mainWindow RadialView::~RadialView() { + // Remove the application event filter we installed on qApp; QObject only + // auto-removes filters installed on itself. Without this, a plugin + // unload/reload leaves qApp with a dangling filter pointer -> crash. + qApp->removeEventFilter(this); delete m_radial; } diff --git a/src/switch/switchplugin.cpp b/src/switch/switchplugin.cpp index c928d9b..e83148d 100644 --- a/src/switch/switchplugin.cpp +++ b/src/switch/switchplugin.cpp @@ -143,6 +143,10 @@ SwitchView::SwitchView(SwitchPlugin *plugin, KTextEditor::MainWindow *mainWindow SwitchView::~SwitchView() { + // Remove the application event filter we installed on qApp; QObject only + // auto-removes filters installed on itself. Without this, a plugin + // unload/reload leaves qApp with a dangling filter pointer -> crash. + qApp->removeEventFilter(this); delete m_filePalette; delete m_symbolPalette; }