Skip to content

SIGSEGV in ~QMessageBox on Plasma - native message dialog helper is used after free #68

Description

@wehrwolfmann

cachyos-pi 1.6.2, Qt 6.11.2, plasma-integration 6.7.4, Plasma 6 / Wayland.

What I did: a package install was running (pkexec pacman -S krita), I pressed "About" and
closed the About box. The app died with SIGSEGV. The core dump is kept, key frames below.

What happens: on Plasma our message boxes are shown as native dialogs.
KDEPlatformMessageDialogHelper::show() creates its own QMessageBox, sets Qt::WA_DeleteOnClose
on it and keeps a raw pointer to it. QDialog::exec() deletes that dialog when it closes, so
the helper keeps a dangling pointer. Later, when our QMessageBox is destroyed,
~QMessageBox -> QMessageBoxPrivate::setVisible(false) -> QDialogPrivate::setNativeDialogVisible(false)
-> helper->hide() -> m_dialog->hide() reads the freed object and calls through a garbage vtable.

Key frames from the core:

  #0  0x0000000000000071            <- jump through freed vtable
  #1  QDialogPrivate::setNativeDialogVisible(bool)
  #2  QMessageBoxPrivate::setVisible(bool)
  #3  QMessageBox::~QMessageBox()
  #4  about::display_about_msgbox()   (src/about.cpp:16)
In the core: nativeDialogInUse = 1, m_platformHelper is a KDEPlatformMessageDialogHelper, and
its m_dialog member points into a freed 40-byte block. Its hide() is literally
`mov 0x20(%rdi),%rdi; jmp QWidget::hide` - no null check, no QPointer.

The defect itself is in plasma-integration and I am reporting it there too, but every
QMessageBox in the app is exposed to it, so the patch below simply keeps Qt's own dialogs.
Nothing changes visually - the "native" Plasma dialog is a QMessageBox as well.

What I measured: with the patch the helper dialog is never created - 10 About/License cycles
under a debugger, zero helper QMessageBox constructions and zero setNativeDialogVisible calls,
app alive. Without the patch the helper dialog is created and destroyed before our own box on
every single About click, i.e. the dangling pointer is there every time.

Patch

diff --git a/src/main.cpp b/src/main.cpp
index bd9fb40..a1b964a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -138,6 +138,12 @@ auto main(int argc, char** argv) -> std::int32_t {
     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 #endif

+    // Keep using Qt's own dialogs. The Plasma platform theme creates a helper QMessageBox
+    // for every native message dialog, marks it WA_DeleteOnClose and keeps a raw pointer
+    // to it. QDialog::exec() deletes that helper dialog when it closes, so the later
+    // hide() call from ~QMessageBox dereferences freed memory and can kill the app.
+    QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
+
     /// 2. Application identification
     QApplication::setOrganizationName("cachyos");
     QApplication::setOrganizationDomain("cachyos.org");

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions