Fix invalid memory read when shutting down QML applications

As the last line in the QObject destructor, we call setParentHelper(0) to
remove ourselves from the parent. In the process of that we also initiate the
QML parentChanged callback. The first thing that parentChanged callback used to
do (but now does it too late, after 26350b5ceafa0ade1328037f6234a7d288eb8f48 in
qtdeclarative) is to check if the object was deleted and then return. We could
re-introduce the check there, but I think it's cleaner to not bother calling
the callback on a dead object in the first place.

Change-Id: Ia4d43b65a9b3744a451b4c312a2d6f9c0e3b67dc
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Simon Hausmann 2013-11-28 11:00:15 +01:00 committed by The Qt Project
parent 82a2d28d84
commit 7c029e83a3
2 changed files with 39 additions and 1 deletions

View File

@ -1876,7 +1876,7 @@ void QObjectPrivate::setParent_helper(QObject *o)
} }
} }
} }
if (!isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged) if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
QAbstractDeclarativeData::parentChanged(declarativeData, q, o); QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
} }

View File

@ -152,6 +152,7 @@ private slots:
void connectBase(); void connectBase();
void qmlConnect(); void qmlConnect();
void exceptions(); void exceptions();
void noDeclarativeParentChangedOnDestruction();
}; };
struct QObjectCreatedOnShutdown struct QObjectCreatedOnShutdown
@ -6233,6 +6234,43 @@ void tst_QObject::exceptions()
#endif #endif
} }
#ifdef QT_BUILD_INTERNAL
static bool parentChangeCalled = false;
static void testParentChanged(QAbstractDeclarativeData *, QObject *, QObject *)
{
parentChangeCalled = true;
}
#endif
void tst_QObject::noDeclarativeParentChangedOnDestruction()
{
#ifdef QT_BUILD_INTERNAL
typedef void (*ParentChangedCallback)(QAbstractDeclarativeData *, QObject *, QObject *);
QScopedValueRollback<ParentChangedCallback> rollback(QAbstractDeclarativeData::parentChanged);
QAbstractDeclarativeData::parentChanged = testParentChanged;
QObject *parent = new QObject;
QObject *child = new QObject;
QAbstractDeclarativeData dummy;
QObjectPrivate::get(child)->declarativeData = &dummy;
parentChangeCalled = false;
child->setParent(parent);
QVERIFY(parentChangeCalled);
parentChangeCalled = false;
delete child;
QVERIFY(!parentChangeCalled);
delete parent;
#else
QSKIP("Needs QT_BUILD_INTERNAL");
#endif
}
// Test for QtPrivate::HasQ_OBJECT_Macro // Test for QtPrivate::HasQ_OBJECT_Macro
Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value); Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value);
Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value); Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value);