From 19c4db4201e8933fbbbf951809ceff30e99b4458 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 2 Apr 2025 10:03:58 +0200 Subject: [PATCH] QDockWidget: don't access a QMainWindow that's under destruction Amends b6b489db6968fbc3b4aab3755d0166ee09712ff0, which introduced UB (invalid downcast): By the time the QMainWindowTabBars are destroyed, and they want to unregister themselves from the QMainWindow, the ex-QMainWindow has already been demoted to a QWidget. Says UBSan: qmainwindow.cpp:63:47: runtime error: member call on address 0x6040000267d0 which does not point to an object of type 'QMainWindow' 0x6040000267d0: note: object is of type 'QWidget' 00 00 00 00 28 c1 e6 f3 c7 7f 00 00 80 24 00 00 60 61 00 00 d8 c2 e6 f3 c7 7f 00 00 00 00 be be ^~~~~~~~~~~~~~~~~~~~~~~ vptr for 'QWidget' #0 0x7fc7f06da8c5 in QMainWindowPrivate::mainWindowLayout(QMainWindow const*) qmainwindow.cpp:63 #1 0x7fc7f06da8c5 in qt_mainwindow_layout(QMainWindow const*) qmainwindow.cpp:69 #2 0x7fc7f07bb4ed in QMainWindowTabBar::~QMainWindowTabBar() qmainwindowlayout.cpp:2042 #3 0x7fc7f07bf4e5 in QMainWindowTabBar::~QMainWindowTabBar() qmainwindowlayout.cpp:2047 #4 0x7fc7c69f9c2a in QObjectPrivate::deleteChildren() qobject.cpp:2226 #5 0x7fc7ef0b7f3d in QWidget::~QWidget() qwidget.cpp:1557 #6 0x7fc7f082c7c7 in QDockWidgetGroupWindow::~QDockWidgetGroupWindow() qmainwindowlayout_p.h:343 #7 0x7fc7f082c7c7 in QDockWidgetGroupWindow::~QDockWidgetGroupWindow() qmainwindowlayout_p.h:343 #8 0x7fc7c69f9c2a in QObjectPrivate::deleteChildren() qobject.cpp:2226 #9 0x7fc7ef0b7f3d in QWidget::~QWidget() qwidget.cpp:1557 #10 0x7fc7f06ce495 in QMainWindow::~QMainWindow() qmainwindow.cpp:338 #12 0x556a6180a84c in std::default_delete::operator()(QMainWindow*) const unique_ptr.h:85 #13 0x556a6180a84c in std::unique_ptr >::~unique_ptr() unique_ptr.h:361 #14 0x556a6180a84c in tst_QDockWidget::setFloatingReparenting() tst_qdockwidget.cpp:492 Use qobject_cast to verify that the mainWindow is not pointing to a QMainWindow that is being destroyed (and has passed the ~QMainWindow destructor). If the main window is destroyed, then removing the tab bar from the list of unused tab bars is unnecessary. Pick-to: 6.8 6.5 Change-Id: I25e12d79198137b75cd2576ff1440b6c94277eba Reviewed-by: Marc Mutz Reviewed-by: Axel Spoerl (cherry picked from commit 1bbbacbecb0bd03a58fea889215850dfffebeb3b) Reviewed-by: Qt Cherry-pick Bot --- src/widgets/widgets/qmainwindowlayout.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index a83587a78a0..6658c24a547 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -2033,7 +2033,9 @@ void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e) QMainWindowTabBar::~QMainWindowTabBar() { - if (!mainWindow || mainWindow == parentWidget()) + // Use qobject_cast to verify that we are not already in the (QWidget) + // destructor of mainWindow + if (!qobject_cast(mainWindow) || mainWindow == parentWidget()) return; // tab bar is not parented to the main window