aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier De Cannière <olivier.decanniere@qt.io>2023-09-28 10:36:46 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-09-29 11:04:29 +0000
commit5a643eb8c35afec5a4d6c33d4faf8b707fb7a705 (patch)
treed6620896ba9bc7fbfd65efb26ad1e51fa2d8050c
parentbbd1fcd6ffa96b08dfce2472de4577a3f29e87ad (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.cpp4
-rw-r--r--tests/auto/qml/qqmlengine/data/bindingInstallUseAfterFree.qml19
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp10
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"