summaryrefslogtreecommitdiffstats
path: root/src/corelib/time
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2022-01-04 18:03:22 +0100
committerMarc Mutz <marc.mutz@qt.io>2022-01-12 19:37:21 +0000
commit11becbe910f806570ca9aa6a131b5887303e2a00 (patch)
tree5b56393aa3d6290d7610ffdf8341c8c469844295 /src/corelib/time
parentbfc713530a6354ce786d3f9bd0f4567844e7240f (diff)
QTzTimeZonePrivate: fix UB (data race on m_icu)
The fallback m_icu QIcuTimeZonePrivate is lazily constructed, which means that two threads each with their own copy of a QTimeZone with a shared QTzTimeZonePrivate will race over who gets to set m_icu, e.g. when concurrently calling QTimeZone::displayName(). Fix by protecting m_icu with a mutex. For simplicity, use a static mutex, not a per-instance one (which would delete the QTzTimeZonePrivate copy constructor, which clone() relies on). This is sufficient for 5.15. For Qt 6, going forward, we could make this lock-less, too. [ChangeLog][QtCore][QTimeZone] Fixed a data race on Unix platforms when implicitly-shared copies of QTimeZone objects were used in certain ways (e.g. calling displayName()) from different threads and Qt was configured with ICU support. Pick-to: 6.3 6.2 5.15 Change-Id: I7e57aef3dd44a90289ad86d0578ece1e54920730 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/corelib/time')
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp12
1 files changed, 12 insertions, 0 deletions
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index b23ccfb3b2..8dfd88b281 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -42,6 +42,7 @@
#include "qtimezone.h"
#include "qtimezoneprivate_p.h"
#include "private/qlocale_tools_p.h"
+#include "private/qlocking_p.h"
#include <QtCore/QDataStream>
#include <QtCore/QDateTime>
@@ -62,6 +63,10 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(icu)
+static QBasicMutex s_icu_mutex;
+#endif
+
/*
Private
@@ -729,6 +734,9 @@ QTzTimeZonePrivate::~QTzTimeZonePrivate()
QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const
{
+#if QT_CONFIG(icu)
+ const auto lock = qt_scoped_lock(s_icu_mutex);
+#endif
return new QTzTimeZonePrivate(*this);
}
@@ -989,12 +997,14 @@ QString QTzTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
const QLocale &locale) const
{
#if QT_CONFIG(icu)
+ auto lock = qt_unique_lock(s_icu_mutex);
if (!m_icu)
m_icu = new QIcuTimeZonePrivate(m_id);
// TODO small risk may not match if tran times differ due to outdated files
// TODO Some valid TZ names are not valid ICU names, use translation table?
if (m_icu->isValid())
return m_icu->displayName(atMSecsSinceEpoch, nameType, locale);
+ lock.unlock();
#else
Q_UNUSED(nameType);
Q_UNUSED(locale);
@@ -1008,12 +1018,14 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
const QLocale &locale) const
{
#if QT_CONFIG(icu)
+ auto lock = qt_unique_lock(s_icu_mutex);
if (!m_icu)
m_icu = new QIcuTimeZonePrivate(m_id);
// TODO small risk may not match if tran times differ due to outdated files
// TODO Some valid TZ names are not valid ICU names, use translation table?
if (m_icu->isValid())
return m_icu->displayName(timeType, nameType, locale);
+ lock.unlock();
#else
Q_UNUSED(timeType);
Q_UNUSED(nameType);