diff options
author | Olivier De Cannière <olivier.decanniere@qt.io> | 2023-09-28 10:36:46 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-09-29 11:04:29 +0000 |
commit | 5a643eb8c35afec5a4d6c33d4faf8b707fb7a705 (patch) | |
tree | d6620896ba9bc7fbfd65efb26ad1e51fa2d8050c | |
parent | bbd1fcd6ffa96b08dfce2472de4577a3f29e87ad (diff) |
QQmlObjectCreator: Don't crash when installing bindings
When creating qqml object, all pending bindings are installed and run
once to determine their dependencies. If they do not have any, they
cannot change and are therefore removed.
A missing check for the ref count of the binding after it was set lead
to a use-after-free. A check was added to determine if the binding has
enough remaining references to be used.
Pick-to: 6.2
Fixes: QTBUG-117642
Change-Id: Ida94d46d6f482a5294b19c41578e592b42906634
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit 1786f6c1839f4ce5ee36b585b29f9c0a10c7e376)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit fe856f76915d5ddc052c1c3ac61254dff8aee773)
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml | 19 | ||||
-rw-r--r-- | tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 10 |
3 files changed, 32 insertions, 1 deletions
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 11ec639087..e325f70ee7 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1464,11 +1464,13 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv); const bool success = bindable.setBinding(qmlBinding); + const auto bindingPrivateRefCount = QPropertyBindingPrivate::get(qmlBinding)->ref; + // Only pop_front after setting the binding as the bindings are refcounted. sharedState->allQPropertyBindings.pop_front(); // If the binding was actually not set, it's deleted now. - if (success) { + if (success && bindingPrivateRefCount > 1) { if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) { auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv); auto jsExpression = qmlBindingPriv->jsExpression(); diff --git a/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml new file mode 100644 index 0000000000..596ab10ee7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml @@ -0,0 +1,19 @@ +import QtQuick + +Item { + visible: false + + property int test: 1 + + Component { + id: comp + Item { + width: { width = test * 100 } + } + } + + Loader { + sourceComponent: comp + width: 100 + } +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 804cced33c..00d28cfe60 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -78,6 +78,7 @@ private slots: void nativeModuleImport(); void lockedRootObject(); void crossReferencingSingletonsDeletion(); + void bindingInstallUseAfterFree(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1696,6 +1697,15 @@ void tst_qqmlengine::crossReferencingSingletonsDeletion() QCOMPARE(o->property("s").toString(), "SingletonA"); } +void tst_qqmlengine::bindingInstallUseAfterFree() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindingInstallUseAfterFree.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr<QObject> o{ c.create() }; + QVERIFY(o); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" |