From 29c61f83c98c269c1c862a668c0a91a1027ee2d2 Mon Sep 17 00:00:00 2001 From: Tom Scheler Date: Sun, 19 May 2019 18:16:34 +0200 Subject: Fix read access violation when using KeyNavigation attached property Setting the KeyNavigation.up property of an item to another item will implicitly set the reverse (KeyNavigation.down) property on that other item pointing back to the item. Once the item is destroyed, you will have an invalid pointer stored in the other item pointing to the destroyed item. Using QPointer<> instead of raw pointers fixes that issue, because they will become null on QObject's destruction. Added QQuickItem test that verifies the issue is solved. Fixes: QTBUG-75399 Change-Id: Ibb3e976c4eb9fcd81604bcc2eb757257d3653930 Reviewed-by: Shawn Rutledge Reviewed-by: Ulf Hermann --- src/quick/items/qquickitem_p.h | 13 ++++++------ .../data/keynavigationtest_implicitDestroy.qml | 13 ++++++++++++ tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 24 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 tests/auto/quick/qquickitem2/data/keynavigationtest_implicitDestroy.qml diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 771228914b..f618bcf1c3 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -75,6 +75,7 @@ #include #include #include +#include #if QT_CONFIG(quick_shadereffect) #include @@ -682,12 +683,12 @@ public: : leftSet(false), rightSet(false), upSet(false), downSet(false), tabSet(false), backtabSet(false) {} - QQuickItem *left = nullptr; - QQuickItem *right = nullptr; - QQuickItem *up = nullptr; - QQuickItem *down = nullptr; - QQuickItem *tab = nullptr; - QQuickItem *backtab = nullptr; + QPointer left; + QPointer right; + QPointer up; + QPointer down; + QPointer tab; + QPointer backtab; bool leftSet : 1; bool rightSet : 1; bool upSet : 1; diff --git a/tests/auto/quick/qquickitem2/data/keynavigationtest_implicitDestroy.qml b/tests/auto/quick/qquickitem2/data/keynavigationtest_implicitDestroy.qml new file mode 100644 index 0000000000..54d20273da --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/keynavigationtest_implicitDestroy.qml @@ -0,0 +1,13 @@ +import QtQuick 2.12 + +Item { + id: root + + function createImplicitKeyNavigation() { + var item = Qt.createQmlObject("import QtQuick 2.0; Item { }", root); + item.KeyNavigation.up = root + item.destroy(); + + forceActiveFocus(); + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 7107f4d995..399535cfa6 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -86,6 +86,7 @@ private slots: void keyNavigation_RightToLeft(); void keyNavigation_skipNotVisible(); void keyNavigation_implicitSetting(); + void keyNavigation_implicitDestroy(); void keyNavigation_focusReason(); void keyNavigation_loop(); void layoutMirroring(); @@ -2164,6 +2165,29 @@ void tst_QQuickItem::keyNavigation_implicitSetting() delete window; } +// QTBUG-75399 +void tst_QQuickItem::keyNavigation_implicitDestroy() +{ + QQuickView view; + view.setSource(testFileUrl("keynavigationtest_implicitDestroy.qml")); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQuickItem *root = view.rootObject(); + QVERIFY(QMetaObject::invokeMethod(root, "createImplicitKeyNavigation")); + + // process events is necessary to trigger upcoming memory access violation + QTest::qWait(0); + + QVERIFY(root->hasActiveFocus()); + + QKeyEvent keyPress = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(&view, &keyPress); // <-- access violation happens here + // this should fail the test, even if the access violation does not occur + QVERIFY(!keyPress.isAccepted()); +} + void tst_QQuickItem::keyNavigation_focusReason() { QQuickView *window = new QQuickView(nullptr); -- cgit v1.2.3