Avoid crash due to accessing deleted QWidgetWindow object

Change childWidget->windowHandle() to childWidget->internalWinId() in
q_createNativeChildrenAndSetParent() to determine whether should we call
childWidget->winId().
This is because in some circumstances Qt will crash due to accessing
deleted QWidgetWindow object if we use windowHandle(). Think about the
following scenario:

1) create a widget A without parent and add two child widgets B and C to A

2) create a native widget D as the child of B, note that when we set
Qt::WA_NativeWindow attribute to it, its QWidgetWindow will be created
which means its windowHandle() is not null.

3) create a top level widget E as the child of C and show it. This will
make Qt call createWinId() to A and then
q_createNativeChildrenAndSetParent() will be called to create A's native
children recursively and finally make D's QWidgetWindow object become a
child of A's QWidgetWindow object. Please note here that B will not become
a native widget just because at that moment windowHandle() of D is not
null and Qt will not call winId() to its parent B

4) Set A's parent to another widget which has been shown, setParent_sys()
will be called to A and then Qt will call destroy() to A. in destroy() Qt
will try to call destroy() to its children recursively with a condition that
the child has Qt::WA_NativeWindow been set. But D's parent B is not a native
widget right now so B and D is not destroyed. Qt will then deleted the
QWidgetWindow object of A, since E's QWidgetWindow object is a child of
A's QWidgetWindow object, it will also be deleted. Now E hold a deleted
pointer of QWidgetWindow object. This is the source of crash later.

Task-number: QTBUG-35600

Change-Id: I97a20a68e626ee62b15bb4eae580e26f8948923b
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
bb10
Jian Liang 2013-12-14 10:59:35 +08:00 committed by The Qt Project
parent 0e1ce36ae6
commit 8fcab70408
2 changed files with 31 additions and 1 deletions

View File

@ -70,7 +70,7 @@ void q_createNativeChildrenAndSetParent(const QWidget *parentWidget)
const QWidget *childWidget = qobject_cast<const QWidget *>(children.at(i));
if (childWidget) { // should not be necessary
if (childWidget->testAttribute(Qt::WA_NativeWindow)) {
if (!childWidget->windowHandle())
if (!childWidget->internalWinId())
childWidget->winId();
if (childWidget->windowHandle()) {
QWindow *parentWindow = childWidget->nativeParentWidget()->windowHandle();

View File

@ -94,6 +94,8 @@ private slots:
#ifndef QT_NO_DRAGANDDROP
void tst_dnd();
#endif
void tst_qtbug35600();
};
void tst_QWidget_window::initTestCase()
@ -568,5 +570,33 @@ void tst_QWidget_window::tst_dnd()
}
#endif
void tst_QWidget_window::tst_qtbug35600()
{
QWidget w;
w.show();
QWidget *wA = new QWidget;
QHBoxLayout *layoutA = new QHBoxLayout;
QWidget *wB = new QWidget;
layoutA->addWidget(wB);
QWidget *wC = new QWidget;
layoutA->addWidget(wC);
wA->setLayout(layoutA);
QWidget *wD = new QWidget;
wD->setAttribute(Qt::WA_NativeWindow);
wD->setParent(wB);
QWidget *wE = new QWidget(wC, Qt::Tool | Qt::FramelessWindowHint | Qt::WindowTransparentForInput);
wE->show();
wA->setParent(&w);
// QTBUG-35600: program may crash here or on exit
}
QTEST_MAIN(tst_QWidget_window)
#include "tst_qwidget_window.moc"