From 9e4e2ea2ad0c2b68c82820f7bce925f08d8cf931 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 4 Apr 2024 17:43:59 +0100 Subject: [PATCH] QToolButton: Don't crash if deleted while in event handler If QToolButton::mouseReleaseEvent triggers its deletion then it will crash when dereferencing its d pointer. qabstractbutton.cpp already uses QPointer guards in many places but qtoolbutton.cpp was missing this one. While deleteLater() is still our recommendation, we shouldn't crash. It's not always obvious what led to the button's destruction, as the chain of indirections can be long. Change-Id: I4a33447fa4e90953370277eb57a161398ded9a9c Reviewed-by: Volker Hilsheimer Reviewed-by: Giuseppe D'Angelo (cherry picked from commit 97d227acc74c6034615dc05d9d94ee5cad7c5f50) Reviewed-by: Qt Cherry-pick Bot --- src/widgets/widgets/qtoolbutton.cpp | 4 +++- .../widgets/qtoolbutton/tst_qtoolbutton.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp index f3e3c8261e9..e0775afd265 100644 --- a/src/widgets/widgets/qtoolbutton.cpp +++ b/src/widgets/widgets/qtoolbutton.cpp @@ -584,8 +584,10 @@ void QToolButton::mousePressEvent(QMouseEvent *e) void QToolButton::mouseReleaseEvent(QMouseEvent *e) { Q_D(QToolButton); + QPointer guard(this); QAbstractButton::mouseReleaseEvent(e); - d->buttonPressed = QToolButtonPrivate::NoButtonPressed; + if (guard) + d->buttonPressed = QToolButtonPrivate::NoButtonPressed; } /*! diff --git a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp index 30224323dcf..20472861d97 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp +++ b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp @@ -34,6 +34,7 @@ private slots: void qtbug_26956_popupTimerDone(); void qtbug_34759_sizeHintResetWhenSettingMenu(); void defaultActionSynced(); + void deleteInHandler(); protected slots: void sendMouseClick(); @@ -316,5 +317,22 @@ void tst_QToolButton::defaultActionSynced() QCOMPARE(bSpy.size(), ++bToggledCount); } +void tst_QToolButton::deleteInHandler() +{ + // Tests that if something deletes the button + // while its event handler is still on the callstack, we don't crash + + QPointer tb = new QToolButton(); + tb->show(); + QVERIFY(QTest::qWaitForWindowActive(tb)); + + connect(tb, &QToolButton::clicked, this, [tb] { + delete tb; + }); + + QTest::mouseClick(tb, Qt::LeftButton); + QVERIFY(!tb); +} + QTEST_MAIN(tst_QToolButton) #include "tst_qtoolbutton.moc"