From ae6f73e8566fa76470937aca737141183929a5ec Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 18 Dec 2019 18:45:36 -0800 Subject: QLibrary: introduce a mutex to protect non-atomic internals And make pHnd atomic. The majority of the variables is updated in QLibraryPrivate::load_sys and updatePluginState(), which get the mutex protection. QLibraryPrivate::unload_sys() doesn't need a mutex protection because we have the refcounting. [ChangeLog][QtCore][QLibrary & QPluginLoader] Fixed a number of race conditions caused by having two QLibrary objects pointing to the same library being operated in different threads. Fixes: QTBUG-39642 Change-Id: I46bf1f65e8db46afbde5fffd15e1a5b3f5e74ea4 Reviewed-by: Lars Knoll --- src/corelib/plugin/qlibrary.cpp | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'src/corelib/plugin/qlibrary.cpp') diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 7ce959cc8f..ddb053c26f 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -407,7 +407,7 @@ inline void QLibraryStore::cleanup() QLibraryPrivate *lib = it.value(); if (lib->libraryRefCount.loadRelaxed() == 1) { if (lib->libraryUnloadCount.loadRelaxed() > 0) { - Q_ASSERT(lib->pHnd); + Q_ASSERT(lib->pHnd.loadRelaxed()); lib->libraryUnloadCount.storeRelaxed(1); #ifdef __GLIBC__ // glibc has a bug in unloading from global destructors @@ -498,8 +498,7 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) } QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString &version, QLibrary::LoadHints loadHints) - : pHnd(0), fileName(canonicalFileName), fullVersion(version), - libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin) + : fileName(canonicalFileName), fullVersion(version), pluginState(MightBeAPlugin) { loadHintsInt.storeRelaxed(loadHints); if (canonicalFileName.isEmpty()) @@ -519,7 +518,7 @@ QLibraryPrivate::~QLibraryPrivate() void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) { // if the library is already loaded, we can't change the load hints - if (pHnd) + if (pHnd.loadRelaxed()) return; loadHintsInt.storeRelaxed(lh); @@ -527,7 +526,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) QFunctionPointer QLibraryPrivate::resolve(const char *symbol) { - if (!pHnd) + if (!pHnd.loadRelaxed()) return 0; return resolve_sys(symbol); } @@ -542,7 +541,7 @@ void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) QObject *QLibraryPrivate::pluginInstance() { // first, check if the instance is cached and hasn't been deleted - QObject *obj = inst.data(); + QObject *obj = (QMutexLocker(&mutex), inst.data()); if (obj) return obj; @@ -558,6 +557,7 @@ QObject *QLibraryPrivate::pluginInstance() obj = factory(); // cache again + QMutexLocker locker(&mutex); if (inst) obj = inst; else @@ -567,7 +567,7 @@ QObject *QLibraryPrivate::pluginInstance() bool QLibraryPrivate::load() { - if (pHnd) { + if (pHnd.loadRelaxed()) { libraryUnloadCount.ref(); return true; } @@ -576,7 +576,9 @@ bool QLibraryPrivate::load() Q_TRACE(QLibraryPrivate_load_entry, fileName); + mutex.lock(); bool ret = load_sys(); + mutex.unlock(); if (qt_debug_component()) { if (ret) { qDebug() << "loaded library" << fileName; @@ -599,9 +601,10 @@ bool QLibraryPrivate::load() bool QLibraryPrivate::unload(UnloadFlag flag) { - if (!pHnd) + if (!pHnd.loadRelaxed()) return false; if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to + QMutexLocker locker(&mutex); delete inst.data(); if (flag == NoUnloadSys || unload_sys()) { if (qt_debug_component()) @@ -610,12 +613,13 @@ bool QLibraryPrivate::unload(UnloadFlag flag) //when the library is unloaded, we release the reference on it so that 'this' //can get deleted libraryRefCount.deref(); - pHnd = 0; + pHnd.storeRelaxed(nullptr); instanceFactory.storeRelaxed(nullptr); + return true; } } - return (pHnd == 0); + return false; } void QLibraryPrivate::release() @@ -746,6 +750,7 @@ bool QLibraryPrivate::isPlugin() void QLibraryPrivate::updatePluginState() { + QMutexLocker locker(&mutex); errorString.clear(); if (pluginState != MightBeAPlugin) return; @@ -766,7 +771,7 @@ void QLibraryPrivate::updatePluginState() } #endif - if (!pHnd) { + if (!pHnd.loadRelaxed()) { // scan for the plugin metadata without loading success = findPatternUnloaded(fileName, this); } else { @@ -830,7 +835,7 @@ bool QLibrary::load() if (!d) return false; if (did_load) - return d->pHnd; + return d->pHnd.loadRelaxed(); did_load = true; return d->load(); } @@ -866,7 +871,7 @@ bool QLibrary::unload() */ bool QLibrary::isLoaded() const { - return d && d->pHnd; + return d && d->pHnd.loadRelaxed(); } @@ -977,8 +982,10 @@ void QLibrary::setFileName(const QString &fileName) QString QLibrary::fileName() const { - if (d) + if (d) { + QMutexLocker locker(&d->mutex); return d->qualifiedFileName.isEmpty() ? d->fileName : d->qualifiedFileName; + } return QString(); } @@ -1119,7 +1126,12 @@ QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &versi */ QString QLibrary::errorString() const { - return (!d || d->errorString.isEmpty()) ? tr("Unknown error") : d->errorString; + QString str; + if (d) { + QMutexLocker locker(&d->mutex); + str = d->errorString; + } + return str.isEmpty() ? tr("Unknown error") : str; } /*! -- cgit v1.2.3