diff options
author | Kai Köhne <[email protected]> | 2025-05-27 15:21:19 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <[email protected]> | 2025-06-30 13:39:14 +0000 |
commit | ec3a4590fd35ff9e674ecd77173f1ab0323b19f7 (patch) | |
tree | 88d79d8276d60272d46071dceb8e9048a76bacdf | |
parent | 9a71859debc5fb02479f05e9b7a9772ebc5496a4 (diff) |
QTranslator: Protect QTranslator data from being accessed while altered
The existing mutex in QCoreApplicationPrivate already protects
access to translation data while QCA::installTranslator(),
::removeTranslator() are running. Extend this to also protect the
altering methods in the QTranslator class itself. This makes sure
that the installed QTranslator objects can be safely altered via
QTranslator::load().
As loading translations might actually take a while, we don't want
to unnecessarily lock the mutex for QTranslator objects that are
_not_ registered. Instead, the patch introduces a
QCAPrivate::mutexLockerForTranslator()
method that returns a std::unique_lock<> object. If the QTranslator
is not registered, the mutex is instantly unlocked in the method.
If it is registered, the mutex is only unlocked after the scope of
the calling code is exited (so the end of QTranslator::load()).
Unfortunately, QWriteLocker does not support std::move semantics.
Therefore switch to a simple std::unique_lock/QMutex. This means that
QCoreApplication::translate() cannot be executed concurrently
from multiple threads anymore, but that is arguably not the
most common case, anyhow.
While at it, remove the QTranslatorList typedef.
Task-number: QTBUG-137179
Change-Id: Id5c7abf0e7664058cb07cf76a0e9532533e37ad3
Reviewed-by: Thiago Macieira <[email protected]>
(cherry picked from commit 46b2a0c1b6de152fee37bfe0b39e3f2b11ba1b15)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
-rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 24 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication_p.h | 12 | ||||
-rw-r--r-- | src/corelib/kernel/qtranslator.cpp | 23 |
3 files changed, 42 insertions, 17 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 3b92c5f48f2..a19e39685f1 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2189,7 +2189,7 @@ bool QCoreApplication::installTranslator(QTranslator *translationFile) QCoreApplicationPrivate *d = self->d_func(); { - QWriteLocker locker(&d->translateMutex); + QMutexLocker locker(&d->translateMutex); d->translators.prepend(translationFile); } @@ -2221,7 +2221,7 @@ bool QCoreApplication::removeTranslator(QTranslator *translationFile) if (!QCoreApplicationPrivate::checkInstance("removeTranslator")) return false; QCoreApplicationPrivate *d = self->d_func(); - QWriteLocker locker(&d->translateMutex); + QMutexLocker locker(&d->translateMutex); if (d->translators.removeAll(translationFile)) { #ifndef QT_NO_QOBJECT locker.unlock(); @@ -2306,7 +2306,7 @@ QString QCoreApplication::translate(const char *context, const char *sourceText, if (self) { QCoreApplicationPrivate *d = self->d_func(); - QReadLocker locker(&d->translateMutex); + QMutexLocker locker(&d->translateMutex); if (!d->translators.isEmpty()) { QList<QTranslator*>::ConstIterator it; QTranslator *translationFile; @@ -2332,13 +2332,23 @@ QString qtTrId(const char *id, int n) return QCoreApplication::translate(nullptr, id, nullptr, n); } -bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator) +/*! + \internal + Returns a locked mutex handle if \a translator is registered in QCoreApplication, + and might be therefore queried for translations from other threads. + Returns an unlocked/dummy QMutexLocker otherwise. + */ +std::unique_lock<QMutex> QCoreApplicationPrivate::mutexLockerForTranslator(QTranslator *translator) { if (!QCoreApplication::self) - return false; + return std::unique_lock<QMutex>(); + QCoreApplicationPrivate *d = QCoreApplication::self->d_func(); - QReadLocker locker(&d->translateMutex); - return d->translators.contains(translator); + std::unique_lock<QMutex> locker(d->translateMutex); + if (!d->translators.contains(translator)) + locker.unlock(); + + return locker; } #else diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 0027b1ad57f..4cb6254e98d 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -30,9 +30,11 @@ #include "private/qcore_mac_p.h" #endif -QT_BEGIN_NAMESPACE +#ifndef QT_NO_TRANSLATION +#include <mutex> +#endif -typedef QList<QTranslator*> QTranslatorList; +QT_BEGIN_NAMESPACE class QAbstractEventDispatcher; @@ -147,9 +149,9 @@ public: static bool is_app_closing; #endif #ifndef QT_NO_TRANSLATION - QTranslatorList translators; - QReadWriteLock translateMutex; - static bool isTranslatorInstalled(QTranslator *translator); + QList<QTranslator*> translators; + QMutex translateMutex; + static std::unique_lock<QMutex> mutexLockerForTranslator(QTranslator *translator); #endif static bool setuidAllowed; diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 6b68cb7a513..47483dc6470 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -463,6 +463,12 @@ bool QTranslator::load(const QString & filename, const QString & directory, const QString & suffix) { Q_D(QTranslator); + + std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); + if (locker.owns_lock()) + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::LanguageChange)); + d->clear(); QString prefix; @@ -738,6 +744,12 @@ bool QTranslator::load(const QLocale & locale, const QString & suffix) { Q_D(QTranslator); + + std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); + if (locker.owns_lock()) + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::LanguageChange)); + d->clear(); return d->load_translation(locale, filename, prefix, directory, suffix); } @@ -757,6 +769,12 @@ bool QTranslator::load(const QLocale & locale, bool QTranslator::load(const uchar *data, int len, const QString &directory) { Q_D(QTranslator); + + std::unique_lock locker = QCoreApplicationPrivate::mutexLockerForTranslator(this); + if (locker.owns_lock()) + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::LanguageChange)); + d->clear(); if (!data || len < MagicLength || memcmp(data, magic, MagicLength)) @@ -1030,7 +1048,6 @@ searchDependencies: void QTranslatorPrivate::clear() { - Q_Q(QTranslator); if (unmapPointer && unmapLength) { #if defined(QT_USE_MMAP) if (used_mmap) { @@ -1058,10 +1075,6 @@ void QTranslatorPrivate::clear() language.clear(); filePath.clear(); - - if (QCoreApplicationPrivate::isTranslatorInstalled(q)) - QCoreApplication::postEvent(QCoreApplication::instance(), - new QEvent(QEvent::LanguageChange)); } /*! |