Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
6.10
-
None
-
83af06b0e (dev), c6ae521e5 (6.9), b26b8f532 (6.8), 501e6ba3c (tqtc/lts-6.5)
Description
Given: A QMenuBar with an active menu.
When: Deleting the active QMenu instance.
Then: QMenuBar accesses Private::activeMenu, which is already demoted to a QWidget.
This is easily reproduced on Linux/X11 by tst_qmenubar allowActiveAndDisabled. In dev, it's flaky whether this code path is hit. To make it deterministic, change the qWaitForWindowExposed() to -Active(). A menuBar.close() at the end of the test function seems to deterministically avoid the code path, an activeMenu.close() instead does not.
Says UBSan:
/home/marc/Qt/qtbase-submit/src/corelib/kernel/qpointer.h:75:14: runtime error: downcast of address 0x7ffe47a12860 which does not point to an object of type 'QMenu' 0x7ffe47a12860: note: object is of type 'QWidget' 00 00 00 00 08 f1 3a 72 a4 7f 00 00 80 c8 00 00 90 61 00 00 b8 f2 3a 72 a4 7f 00 00 00 00 8e 7a ^~~~~~~~~~~~~~~~~~~~~~~ vptr for 'QWidget' #0 0x7fa46f217c25 in QPointer<QMenu>::data() const /home/marc/Qt/qtbase-submit/src/corelib/kernel/qpointer.h:75 #1 0x7fa46f217c25 in QPointer<QMenu>::operator QMenu*() const /home/marc/Qt/qtbase-submit/src/corelib/kernel/qpointer.h:83 #2 0x7fa46f217c25 in QMenuBarPrivate::setCurrentAction(QAction*, bool, bool) /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenubar.cpp:351 #3 0x7fa46f21a240 in QMenuBarPrivate::focusFirstAction() /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenubar.cpp:245 #4 0x7fa46f21b767 in QMenuBarPrivate::focusFirstAction() /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenubar.cpp:1260 #5 0x7fa46f21b767 in QMenuBar::focusInEvent(QFocusEvent*) /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenubar.cpp:1259 #6 0x7fa46d669a5a in QWidget::event(QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:9098 #7 0x7fa46f23e8fd in QMenuBar::event(QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenubar.cpp:1429 #8 0x7fa46ce873c1 in QApplicationPrivate::notify_helper(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qapplication.cpp:3309 #9 0x7fa46cf087ba in QApplication::notify(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qapplication.cpp:3259 #10 0x7fa444a13ada in QCoreApplication::notifyInternal2(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qcoreapplication.cpp:1111 #11 0x7fa444a164e3 in QCoreApplication::sendEvent(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qcoreapplication.cpp:1551 #12 0x7fa46ced8d9f in QApplicationPrivate::closePopup(QWidget*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qapplication.cpp:3387 #13 0x7fa46d5ecd42 in QWidgetPrivate::hide_helper() /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8196 #14 0x7fa46d65eaef in QWidgetPrivate::setVisible(bool) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8406 #15 0x7fa46d54c8b3 in QWidget::setVisible(bool) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8314 #16 0x7fa46d3d1a28 in QWidget::hide() /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8179 #17 0x7fa46d5f0f73 in QWidgetPrivate::handleClose(QWidgetPrivate::CloseMode) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8580 #18 0x7fa46d7bdf36 in QWidgetWindow::closeEvent(QCloseEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidgetwindow.cpp:871 #19 0x7fa456caec70 in QWindow::event(QEvent*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindow.cpp:2714 #20 0x7fa46d82dfc7 in QWidgetWindow::event(QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidgetwindow.cpp:251 #21 0x7fa46ce873c1 in QApplicationPrivate::notify_helper(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qapplication.cpp:3309 #22 0x7fa46cf087ba in QApplication::notify(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/widgets/kernel/qapplication.cpp:3259 #23 0x7fa444a13ada in QCoreApplication::notifyInternal2(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qcoreapplication.cpp:1111 #24 0x7fa444a16623 in QCoreApplication::sendSpontaneousEvent(QObject*, QEvent*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qcoreapplication.cpp:1565 #25 0x7fa45662a01b in QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::CloseEvent*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qguiapplication.cpp:2911 #26 0x7fa45666b40f in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qguiapplication.cpp:2259 #27 0x7fa456d6a882 in QWindowSystemEventHandler::sendEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindowsysteminterface.cpp:190 #28 0x7fa456d6a882 in bool QWindowSystemHelper<QWindowSystemInterface::SynchronousDelivery>::handleEvent<QWindowSystemInterfacePrivate::CloseEvent, QWindow*>(QWindow*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindowsysteminterface.cpp:102 #29 0x7fa456d6a882 in handleWindowSystemEvent<QWindowSystemInterfacePrivate::CloseEvent, QWindowSystemInterface::SynchronousDelivery, QWindow*> /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindowsysteminterface.cpp:138 #30 0x7fa456d6a882 in bool QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(QWindow*) /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindowsysteminterface.cpp:351 #31 0x7fa456a6ceee in QPlatformWindow::close() /home/marc/Qt/qtbase-submit/src/gui/kernel/qplatformwindow.cpp:348 #32 0x7fa456c33d58 in QWindow::close() /home/marc/Qt/qtbase-submit/src/gui/kernel/qwindow.cpp:2442 #33 0x7fa46d5f22c2 in QWidgetPrivate::close() /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:8632 #34 0x7fa46d5f73d6 in QWidget::~QWidget() /home/marc/Qt/qtbase-submit/src/widgets/kernel/qwidget.cpp:1508 #35 0x7fa46f074658 in QMenu::~QMenu() /home/marc/Qt/qtbase-submit/src/widgets/widgets/qmenu.cpp:1789 #36 0x5628d9615642 in tst_QMenuBar::allowActiveAndDisabled() /home/marc/Qt/qtbase-submit/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp:871 #37 0x5628d963b065 in tst_QMenuBar::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) /home/marc/Qt/qtbase-submit-build/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_autogen/include/tst_qmenubar.moc:296 #38 0x7fa444b1641b in QMetaMethodInvoker::invokeImpl(QMetaMethod, void*, Qt::ConnectionType, long long, void const* const*, char const* const*, QtPrivate::QMetaTypeInterface const* const*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qmetaobject.cpp:2801 #39 0x7fa444b1fc2d in QMetaMethod::invokeImpl(QMetaMethod, void*, Qt::ConnectionType, long long, void const* const*, char const* const*, QtPrivate::QMetaTypeInterface const* const*) /home/marc/Qt/qtbase-submit/src/corelib/kernel/qmetaobject.cpp:2640 #40 0x7fa47a1a9ff0 in std::enable_if<!std::disjunction<>::value, bool>::type QMetaMethod::invoke<void>(QObject*, Qt::ConnectionType, QTemplatedMetaMethodReturnArgument<void>) const /home/marc/Qt/qtbase-submit/src/corelib/kernel/qmetaobject.h:150 #41 0x7fa47a1a9ff0 in std::enable_if<!std::disjunction<>::value, bool>::type QMetaMethod::invoke<>(QObject*, Qt::ConnectionType) const /home/marc/Qt/qtbase-submit/src/corelib/kernel/qmetaobject.h:162 #42 0x7fa47a1a9ff0 in invokeTestMethodIfValid /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:434 #43 0x7fa47a1f94ea in QTest::TestMethods::invokeTestOnData(int) const /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1050 #44 0x7fa47a2013a7 in operator() /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1368 #45 0x7fa47a2013a7 in runWithWatchdog<QTest::TestMethods::invokeTest(int, QLatin1StringView, std::optional<QTest::WatchDog>&) const::<lambda()> > /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1251 #46 0x7fa47a2013a7 in QTest::TestMethods::invokeTest(int, QLatin1String, std::optional<QTest::WatchDog>&) const /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1367 #47 0x7fa47a205f5b in QTest::TestMethods::invokeTests(QObject*) const /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1710 #48 0x7fa47a20b7c4 in QTest::qRun() /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1951 #49 0x7fa47a20d977 in QTest::qExec(QObject*, int, char**) /home/marc/Qt/qtbase-submit/src/testlib/qtestcase.cpp:1823 #50 0x5628d9596387 in main /home/marc/Qt/qtbase-submit/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp:1716 #51 0x7fa43e252082 in __libc_start_main ../csu/libc-start.c:308 #52 0x5628d95965ed in _start (/home/marc/Qt/qtbase-submit-build/tests/auto/widgets/widgets/qmenubar/tst_qmenubar+0xf65ed)
This particular issue could be solved by making activeMenu a QWidget and only casting down to QMenu* when QMenu-ness is needed, which isn't the case in the function that hits UB.
But hopefully, there's a simpler fix?
Attachments
Gerrit Reviews
For Gerrit Dashboard: QTBUG-135151 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
634535,2 | tst_QMenuBar: avoid UB (invalid downcast) in allowActiveAndDisabled() | dev | qt/qtbase | Status: MERGED | +2 | 0 |
635382,2 | tst_QMenuBar: avoid UB (invalid downcast) in allowActiveAndDisabled() | 6.9 | qt/qtbase | Status: MERGED | +2 | 0 |
635609,2 | tst_QMenuBar: avoid UB (invalid downcast) in allowActiveAndDisabled() | 6.8 | qt/qtbase | Status: MERGED | +2 | 0 |
635684,2 | tst_QMenuBar: avoid UB (invalid downcast) in allowActiveAndDisabled() | tqtc/lts-6.5 | qt/tqtc-qtbase | Status: MERGED | +2 | 0 |