Fix a deadlock in Krita: https://bugreports.qt.io/browse/QTBUG-83207 Patch copied from Qt bug tracker: https://codereview.qt-project.org/c/qt/qtbase/+/296034 From 276fa8383a7535765be7182883ef4aade17ce013 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 02 Apr 2020 12:08:41 -0300 Subject: [PATCH] QLibrary: fix deadlock caused by fix to QTBUG-39642 Commit ae6f73e8566fa76470937aca737141183929a5ec inserted a mutex around the entire load_sys(). We had reasoed that deadlocks would only occur if the object creation in instance() recursed into its own instance(), which was already a bug. But we had forgotten that dlopen()/ LoadLibrary() executes initialization code from the module being loaded, which could cause a recursion back into the same QPluginLoader or QLibrary object. This recursion is benign because the module *is* loaded and dlopen()/LoadLibrary() returns the same handle. [ChangeLog][QtCore][QLibrary and QPluginLoader] Fixed a deadlock that would happen if the plugin or library being loaded has load-time initialization code (C++ global variables) that recursed back into the same QLibrary or QPluginLoader object. PS: QLibraryPrivate::loadPlugin() updates pluginState outside a mutex lock, so pluginState should be made an atomic variable. Once that is done, we'll only need locking the mutex to update errorString (no locking before loading). Fixes: QTBUG-83207 Task-number: QTBUG-39642 Change-Id: Ibdc95e9af7bd456a94ecfffd160209304e5ab2eb Reviewed-by: Volker Hilsheimer Reviewed-by: David Faure --- diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index ddb053c..be9d92b 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -576,9 +576,7 @@ Q_TRACE(QLibraryPrivate_load_entry, fileName); - mutex.lock(); bool ret = load_sys(); - mutex.unlock(); if (qt_debug_component()) { if (ret) { qDebug() << "loaded library" << fileName; diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 017aa97..a5c72f8 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -123,6 +123,7 @@ bool QLibraryPrivate::load_sys() { + QMutexLocker locker(&mutex); QString attempt; QFileSystemEntry fsEntry(fileName); @@ -213,6 +214,7 @@ } #endif + locker.unlock(); bool retry = true; Handle hnd = nullptr; for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) { @@ -273,6 +275,8 @@ } } #endif + + locker.relock(); if (!hnd) { errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror()); } diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp index 000bf76..ef58724 100644 --- a/src/corelib/plugin/qlibrary_win.cpp +++ b/src/corelib/plugin/qlibrary_win.cpp @@ -78,6 +78,7 @@ // fileName // // NB If it's a plugin we do not ever try the ".dll" extension + QMutexLocker locker(&mutex); QStringList attempts; if (pluginState != IsAPlugin) @@ -95,6 +96,7 @@ attempts.prepend(QDir::rootPath() + fileName); #endif + locker.unlock(); Handle hnd = nullptr; for (const QString &attempt : qAsConst(attempts)) { #ifndef Q_OS_WINRT @@ -115,6 +117,7 @@ #ifndef Q_OS_WINRT SetErrorMode(oldmode); #endif + locker.relock(); if (!hnd) { errorString = QLibrary::tr("Cannot load library %1: %2").arg( QDir::toNativeSeparators(fileName), qt_error_string());