From ce37467acf34edf1380f896a0942b616dc4cbead Mon Sep 17 00:00:00 2001
From: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
Date: Fri, 29 Apr 2016 16:32:42 -0700
Subject: [PATCH] Acknowledge QWidgetWindow::widget() may be null

We guard QWidgetWindow's widget with a QPointer to avoid
sending it events during destruction (which may result in
undefined behavior, since this originates from ~QObject and
we expect the object to behave as a QWidget). Therefore, we
need to harden all access to that widget since it can now
be null, specially during destruction.

As an example, QGestureManager may crash when we delete a
top-level widget. The crash stack trace is:

1  QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>::data() const
2  QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>::pointer qGetPtrHelper<QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>>(QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>> const&)
3  QWidget::d_func()
4  QGestureManager::filterEvent(QWidget *, QEvent *)  <-- the widget ptr is null
5  QGestureManager::filterEvent(QObject *, QEvent *)
6  QApplication::notify(QObject *, QEvent *)
7  QCoreApplication::notifyInternal2(QObject *, QEvent *)
8  QCoreApplication::sendEvent(QObject *, QEvent *)
9  QWindow::destroy()
10 QWidgetPrivate::deleteTLSysExtra()
11 QWidgetPrivate::deleteExtra()
12 QWidgetPrivate::~QWidgetPrivate()
13 QWidgetPrivate::~QWidgetPrivate()
14 QWidgetPrivate::~QWidgetPrivate()
15 QScopedPointerDeleter<QObjectData>::cleanup(QObjectData *)
16 QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>::~QScopedPointer()
17 QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>::~QScopedPointer()
18 QObject::~QObject()
19 QWidget::~QWidget()

Task-number: QTBUG-53103
Change-Id: I1bb32648270c4f7791f668b8f0b639ddb4235703
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
---
 src/widgets/kernel/qapplication.cpp    |  8 ++++----
 src/widgets/kernel/qgesturemanager.cpp |  2 +-
 src/widgets/kernel/qwidgetwindow.cpp   | 20 +++++++++++---------
 3 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index a459a57482c..fcc195f6010 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -2249,10 +2249,10 @@ void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous)
     QApplication::setActiveWindow(tlw);
     // QTBUG-37126, Active X controls may set the focus on native child widgets.
     if (wnd && tlw && wnd != tlw->windowHandle()) {
-        if (QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(wnd)) {
-            if (widgetWindow->widget()->inherits("QAxHostWidget"))
-                widgetWindow->widget()->setFocus(Qt::ActiveWindowFocusReason);
-        }
+        if (QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(wnd))
+            if (QWidget *widget = widgetWindow->widget())
+                if (widget->inherits("QAxHostWidget"))
+                    widget->setFocus(Qt::ActiveWindowFocusReason);
     }
 }
 
diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp
index 967ef6b40cc..89ed6922b58 100644
--- a/src/widgets/kernel/qgesturemanager.cpp
+++ b/src/widgets/kernel/qgesturemanager.cpp
@@ -545,7 +545,7 @@ bool QGestureManager::filterEvent(QObject *receiver, QEvent *event)
     // filter method.
     QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(receiver);
 
-    if (widgetWindow)
+    if (widgetWindow && widgetWindow->widget())
         return filterEvent(widgetWindow->widget(), event);
 
     QGesture *state = qobject_cast<QGesture *>(receiver);
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index ec2618e5013..5290d79d9e8 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -87,7 +87,7 @@ QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
 {
     Q_Q(const QWidgetWindow);
     const QWidget *widget = q->widget();
-    if (!widget->isWindow() || !widget->hasHeightForWidth())
+    if (!widget || !widget->isWindow() || !widget->hasHeightForWidth())
         return QRect();
     const QSize oldSize = rect.size().toSize();
     const QSize newSize = QLayout::closestAcceptableSize(widget, oldSize);
@@ -142,16 +142,18 @@ QAccessibleInterface *QWidgetWindow::accessibleRoot() const
 
 QObject *QWidgetWindow::focusObject() const
 {
-    QWidget *widget = m_widget->focusWidget();
+    QWidget *windowWidget = m_widget;
+    if (!windowWidget)
+        return Q_NULLPTR;
+
+    QWidget *widget = windowWidget->focusWidget();
 
     if (!widget)
-        widget = m_widget;
+        widget = windowWidget;
 
-    if (widget) {
-        QObject *focusObj = QWidgetPrivate::get(widget)->focusObject();
-        if (focusObj)
-            return focusObj;
-    }
+    QObject *focusObj = QWidgetPrivate::get(widget)->focusObject();
+    if (focusObj)
+        return focusObj;
 
     return widget;
 }
@@ -204,7 +206,7 @@ bool QWidgetWindow::event(QEvent *event)
 #ifndef QT_NO_ACCESSIBILITY
         QAccessible::State state;
         state.active = true;
-        QAccessibleStateChangeEvent ev(widget(), state);
+        QAccessibleStateChangeEvent ev(m_widget, state);
         QAccessible::updateAccessibility(&ev);
 #endif
         return false; }
-- 
GitLab