From f579be96f7ad0362220f710a03a390d1c1139c52 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 6 May 2020 16:31:14 +0200 Subject: QMenuPrivate::hideMenu - avoid deleting 'q' too early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function among other things enters a nested event loop twice. With enough luck processing events may end with posting deferred delete event for 'q' and then ... executing this event leaving the whole call tree with a dangling pointer. This is not very convenient, and we filter out such events to re-post them a bit later. Pick-to: 5.15 Fixes: QTBUG-82349 Change-Id: Ic620273b529b89f2bd57e25df1f91c2754940670 Reviewed-by: Volker Hilsheimer Reviewed-by: Mårten Nordheim Reviewed-by: Tor Arne Vestbø --- src/widgets/widgets/qmenu.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index fe5c52ee93..6198b8bfa1 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -571,6 +571,35 @@ void QMenuPrivate::hideMenu(QMenu *menu) { if (!menu) return; + + // See two execs below. They may trigger an akward situation + // when 'menu' (also known as 'q' or 'this' in the many functions + // around) to become a dangling pointer if the loop manages + // to execute 'deferred delete' ... posted while executing + // this same loop. Not good! + struct Reposter : QObject + { + Reposter(QMenu *menu) : q(menu) + { + Q_ASSERT(q); + q->installEventFilter(this); + } + ~Reposter() + { + if (deleteLater) + q->deleteLater(); + } + bool eventFilter(QObject *obj, QEvent *event) override + { + if (obj == q && event->type() == QEvent::DeferredDelete) + return deleteLater = true; + + return QObject::eventFilter(obj, event); + } + QMenu *q = nullptr; + bool deleteLater = false; + }; + #if QT_CONFIG(effects) QSignalBlocker blocker(menu); aboutToHide = true; @@ -582,6 +611,7 @@ void QMenuPrivate::hideMenu(QMenu *menu) QAction *activeAction = currentAction; menu->setActiveAction(nullptr); + const Reposter deleteDeleteLate(menu); QTimer::singleShot(60, &eventLoop, SLOT(quit())); eventLoop.exec(); -- cgit v1.2.3