Fix the QtQml's use of plugins for value type providers
authorThiago Macieira <thiago.macieira@intel.com>
Thu, 28 Feb 2013 20:41:03 +0000 (12:41 -0800)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 1 Mar 2013 16:29:13 +0000 (17:29 +0100)
When the QtQuick library is loaded, it runs a global constructor that
installs the QtQuick value providers. But those providers are never
uninstalled when the library is unloaded, resulting in a dangling
pointer stored in QtQml's singly-linked list of value providers.

Since cafb02911a29b98ac2652fde64e95870e70fd547, QLibrary will unload
plugins after inspecting their plugin metadata on Mac and Windows. If
QtQml is trying to load a plugin that links to QtQuick (in particular,
*the* qtquick2plugin plugin), it would cause the value provider to go
stale.

To make matters worse, it's quite likely that the plugin would get
loaded soon after, at the same address in memory, which causes the
valueTypeProvider list to become cyclic.

Change-Id: I6f4db5475ceeaba766d9e9c78f86266c9a65806a
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>

src/qml/qml/qqmlglobal.cpp
src/qml/qml/qqmlglobal_p.h

index 5d4b2a5..379a168 100644 (file)
@@ -54,6 +54,7 @@ QQmlValueTypeProvider::QQmlValueTypeProvider()
 
 QQmlValueTypeProvider::~QQmlValueTypeProvider()
 {
+    QQml_removeValueTypeProvider(this);
 }
 
 QQmlValueType *QQmlValueTypeProvider::createValueType(int type)
@@ -266,13 +267,13 @@ bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return fa
 bool QQmlValueTypeProvider::read(int, const void *, size_t, int, void *) { return false; }
 bool QQmlValueTypeProvider::write(int, const void *, void *, size_t) { return false; }
 
+Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider)
 static QQmlValueTypeProvider *valueTypeProvider = 0;
 
 static QQmlValueTypeProvider **getValueTypeProvider(void)
 {
     if (valueTypeProvider == 0) {
-        static QQmlValueTypeProvider nullValueTypeProvider;
-        valueTypeProvider = &nullValueTypeProvider;
+        valueTypeProvider = nullValueTypeProvider;
     }
 
     return &valueTypeProvider;
@@ -285,6 +286,34 @@ Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newPr
     *providerPtr = newProvider;
 }
 
+Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider)
+{
+    if (oldProvider == nullValueTypeProvider) {
+        // don't remove the null provider
+        // we get here when the QtQml library is being unloaded
+        return;
+    }
+
+    // the only entry with next = 0 is the null provider
+    Q_ASSERT(oldProvider->next);
+
+    QQmlValueTypeProvider *prev = valueTypeProvider;
+    if (prev == oldProvider) {
+        valueTypeProvider = oldProvider->next;
+        return;
+    }
+
+    // singly-linked list removal
+    for ( ; prev; prev = prev->next) {
+        if (prev->next != oldProvider)
+            continue;               // this is not the provider you're looking for
+        prev->next = oldProvider->next;
+        return;
+    }
+
+    qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider);
+}
+
 Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void)
 {
     static QQmlValueTypeProvider **providerPtr = getValueTypeProvider();
index 0ce026a..f6b2c81 100644 (file)
@@ -271,6 +271,7 @@ private:
     virtual bool write(int, const void *, void *, size_t);
 
     friend Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *);
+    friend Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *);
 
     QQmlValueTypeProvider *next;
 };