Version in base suite: 6.8.2+dfsg-9+deb13u1 Base version: qt6-base_6.8.2+dfsg-9+deb13u1 Target version: qt6-base_6.8.2+dfsg-9+deb13u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/q/qt6-base/qt6-base_6.8.2+dfsg-9+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/q/qt6-base/qt6-base_6.8.2+dfsg-9+deb13u2.dsc changelog | 10 patches/series | 9 patches/upstream_qreadwritelock_data_race.patch | 60 +++++ patches/upstream_qreadwritelock_data_race_2.patch | 251 ++++++++++++++++++++++ patches/upstream_revert_simplify_locking.diff | 200 +++++++++++++++++ 5 files changed, 530 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpzjp787by/qt6-base_6.8.2+dfsg-9+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpzjp787by/qt6-base_6.8.2+dfsg-9+deb13u2.dsc: no acceptable signature found diff -Nru qt6-base-6.8.2+dfsg/debian/changelog qt6-base-6.8.2+dfsg/debian/changelog --- qt6-base-6.8.2+dfsg/debian/changelog 2025-09-02 19:04:45.000000000 +0000 +++ qt6-base-6.8.2+dfsg/debian/changelog 2026-05-04 08:21:36.000000000 +0000 @@ -1,3 +1,13 @@ +qt6-base (6.8.2+dfsg-9+deb13u2) trixie; urgency=medium + + * Non-maintainer upload. + * Backport upstream patches to fix data races in QReadWriteLock + (Closes: #1122640). + * Backport a upstream patch to fix data races in QProcessEnvironment + (Closes: #1123679). + + -- Miao Wang Mon, 04 May 2026 16:21:36 +0800 + qt6-base (6.8.2+dfsg-9+deb13u1) trixie; urgency=medium * Backport patch to fix high CPU load of kwin_x11 when locking the diff -Nru qt6-base-6.8.2+dfsg/debian/patches/series qt6-base-6.8.2+dfsg/debian/patches/series --- qt6-base-6.8.2+dfsg/debian/patches/series 2025-09-02 19:04:45.000000000 +0000 +++ qt6-base-6.8.2+dfsg/debian/patches/series 2026-05-04 08:21:36.000000000 +0000 @@ -25,6 +25,15 @@ # Needs to be fixed upstream. Add-SH-detection.patch +# fixed in 6.10.0 +upstream_qreadwritelock_data_race.patch + +# fixed in 6.10.2 +upstream_qreadwritelock_data_race_2.patch + +# fixed in 6.8.7/6.10.3 +upstream_revert_simplify_locking.diff + # Debian specific remove_privacy_breaches.diff build_path_embedded_qtbuildinternalsextra_cmake.patch diff -Nru qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race.patch qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race.patch --- qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race.patch 1970-01-01 00:00:00.000000000 +0000 +++ qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race.patch 2026-05-04 08:21:36.000000000 +0000 @@ -0,0 +1,60 @@ +From 80d01c4ccb697b9d390cc0da49c2f111b19b4c5b Mon Sep 17 00:00:00 2001 +From: David Faure +Date: Sat, 5 Apr 2025 13:40:05 +0200 +Subject: [PATCH] QReadWriteLock: fix data race on the d_ptr members + +Testcase: ./tst_qreadwritelock countingTest + +WARNING: ThreadSanitizer: data race (pid=356186) + Read of size 1 at 0x7294000000f8 by thread T12: + #0 contendedTryLockForRead qtbase/src/corelib/thread/qreadwritelock.cpp:230 (libQt6Core_tsan.so.6+0x6c3743) + #1 QReadWriteLock::tryLockForRead(QDeadlineTimer) qtbase/src/corelib/thread/qreadwritelock.cpp:190 (libQt6Core_tsan.so.6+0x6c347b) + #2 QReadWriteLock::lockForRead() qtbase/src/corelib/thread/qreadwritelock.h:68 (tst_qreadwritelock+0xb0f0) + #3 ReadLockCountThread::run() qtbase/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp:597 (tst_qreadwritelock+0xc506) + + Previous write of size 8 at 0x7294000000f8 by thread T2: + #0 operator new[](unsigned long, std::align_val_t) (libtsan.so.2+0xa78eb) + #1 allocate qtbase/src/corelib/tools/qfreelist_p.h:135 (libQt6Core_tsan.so.6+0x6c6079) + #2 next qtbase/src/corelib/tools/qfreelist_p.h:212 (libQt6Core_tsan.so.6+0x6c5915) + #3 QReadWriteLockPrivate::allocate() qtbase/src/corelib/thread/qreadwritelock.cpp:564 (libQt6Core_tsan.so.6+0x6c5354) + #4 contendedTryLockForRead qtbase/src/corelib/thread/qreadwritelock.cpp:218 (libQt6Core_tsan.so.6+0x6c364f) + +The loadRelaxed() at the beginning of tryLockForRead/tryLockForWrite +isn't enough to bring us the non-atomic write of the recursive bool. +Same issue with the std::mutex itself. + +Pick-to: 6.9 6.8 6.5 +Change-Id: I6f5e371cf94292b643cb36041d1406b19d22cdbe +Reviewed-by: Thiago Macieira +--- + src/corelib/thread/qreadwritelock.cpp | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp +index e79bed223117..0fefc7dab693 100644 +--- a/src/corelib/thread/qreadwritelock.cpp ++++ b/src/corelib/thread/qreadwritelock.cpp +@@ -225,7 +225,10 @@ Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointerrecursive) + return d->recursiveLockForRead(timeout); +@@ -333,7 +336,10 @@ Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointerrecursive) + return d->recursiveLockForWrite(timeout); diff -Nru qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race_2.patch qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race_2.patch --- qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ qt6-base-6.8.2+dfsg/debian/patches/upstream_qreadwritelock_data_race_2.patch 2026-05-04 08:21:36.000000000 +0000 @@ -0,0 +1,251 @@ +From 4fd88011fa7975ce64d5648698a234f85bac359c Mon Sep 17 00:00:00 2001 +From: Miao Wang +Date: Fri, 5 Dec 2025 18:41:24 +0000 +Subject: [PATCH] QReadWriteLock: fix data race on weakly-ordered memory + architectures + +Testcase: ./tst_qreadwritelock heavyLoadLocks + +When the test run under release mode on arm64, all the spawned threads +may block without this fix. When the test run with optimization +enabled and assertions enabled and the assertions for !mutex.try_lock() +are removed from the entry of QReadWriteLockPrivate:: +lockFor{Read,Write}, random assertion failures may happen without this +fix. + +The reason for the race is because when a lock is uncontended locked and +being converted into a contended lock, no synchronization happens +between the initialization of new allocated QReadWriteLockPrivate object +and the use of the existing QReadWriteLockPrivate object in +lockFor{Read,Write}. QReadWriteLockPrivate objects are allocated from a +statically allocated freelist and it is of high probability that the +newly allocated object has just been released. The possible execution +order that leads to a data race is described as follows: + +Suppose there are three threads T1, T2, and T3, and T1 holds the write +lock initially. T1 first releases the lock, and then gains the read +lock, while T2 tries to gain the write lock, and T3 tries to gain the +read lock. The interleaved execution order is as follows, where <- means +a normal memory write, <1> means a memory address of a +QReadWriteLockPrivate object, : means a return value, #n means a +synchronization point. For abberviation, wc denotes writerCount and rc +denotes readerCount. The .h/.c and the number in the parentheses denotes +the line number in qreadwritelock.h/.cpp. + +T2 T1 T3 + unlock() lockForRead() + d = d_ptr.loadRelaxed(): <1> (.h 52) d = d_ptr.loadRelaxed(): <1> (.h 52) + <1>->mutex.lock() (.c 393) d = d_ptr.loadAcquire(): <1> (.c 229) + <1> <-{wc = 0}(rc should be 0) (.c 397) <1>->mutex.lock() ... (.c 236) + d_ptr.storeRelease(null) #1 (.c 409) + <1>->release() (.c 410) + <1>->mutex.unlock() #2 (.c 412) + lockForRead() <1>->mutex.lock() returns #2 + d = d_ptr.loadRelaxed(): null (.h 93) +lockForWrite() d_ptr.testAndSetAcquire(1) #3 (.h 81) +d = d_ptr.loadRelaxed(): 1 (.h 116) +val = allocate -> <1> (.c 321) +// ^ suppose <1> is reused here +<1> <-{rc = 1}(wc should be 0) (.c 325) +d_ptr.testAndSetOrdered(<1>) #5 (.c 326) +d = d_ptr.loadAcquire(): <1> #6 (.c 335) d_ptr.loadRelaxed(): <1> (.c 237) +<1>->mutex.lock() ... (.c 342) // Here T3 sees the d_ptr load result + // as <1>, which is the same as + // before, thinking it unchanged and + // thus continues to execute + // d->lockForRead(). + // T3 here has no synchronization T2, + // but had synchronization with T1 at + // #2. So T3 may see the stale data + // previous written by T1 to <1>, i.e. + // wc = 0, rc = 0 + <1> <-{rc = 1} (.c 432) + <1>->mutex.unlock() #4 (.c 248) +<1>->mutex.lock() returns #4 +d_ptr.loadRelaxed(): <1> (.c 343) +// The same happens to T2 here, it continues +// to execute d->lockForWrite(). +// T2 here is synchronized with T3 at #4, +// so T2 must see the data written by T3 +// to <1>, i.e. wc = 0, rc = 1 +<1>->writerCond.wait() (.c 455) + +After the above interleaved execution, T2 is blocked while T3 and T1 are +holding the read lock, but in the QReadWriteLockPrivate object, the +readerCount is 1, which is incorrect. This might further lead to +deadlock if readerCount becomes -1 after the two readers release the +lock or letting a writer to proceed when only one of the readers +releases the lock. + +The fix changes the relaxed load of d_ptr in lockFor{Read,Write} after +the acquire of the mutex to an acquire load, to establish +synchronization with the release store of d_ptr when converting from an +uncontended lock to a contended lock. + +Fixes: QTBUG-142321 +Change-Id: I5a570471b52359dd65f309e644d9aacfd58ce943 +Reviewed-by: Thiago Macieira +--- + src/corelib/thread/qreadwritelock.cpp | 8 +- + .../qreadwritelock/tst_qreadwritelock.cpp | 106 ++++++++++++++++++ + 2 files changed, 110 insertions(+), 4 deletions(-) + +diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp +index 96e35dcb9652..2a1af2315ca7 100644 +--- a/src/corelib/thread/qreadwritelock.cpp ++++ b/src/corelib/thread/qreadwritelock.cpp +@@ -234,14 +234,14 @@ QBasicReadWriteLock::contendedTryLockForRead(QDeadlineTimer timeout, void *dd) + return d->recursiveLockForRead(timeout); + + auto lock = qt_unique_lock(d->mutex); +- if (d != d_ptr.loadRelaxed()) { ++ if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { + // d_ptr has changed: this QReadWriteLock was unlocked before we had + // time to lock d->mutex. + // We are holding a lock to a mutex within a QReadWriteLockPrivate + // that is already released (or even is already re-used). That's ok + // because the QFreeList never frees them. + // Just unlock d->mutex (at the end of the scope) and retry. +- d = d_ptr.loadAcquire(); ++ d = dd; + continue; + } + return d->lockForRead(lock, timeout); +@@ -340,11 +340,11 @@ QBasicReadWriteLock::contendedTryLockForWrite(QDeadlineTimer timeout, void *dd) + return d->recursiveLockForWrite(timeout); + + auto lock = qt_unique_lock(d->mutex); +- if (d != d_ptr.loadRelaxed()) { ++ if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { + // The mutex was unlocked before we had time to lock the mutex. + // We are holding to a mutex within a QReadWriteLockPrivate that is already released + // (or even is already re-used) but that's ok because the QFreeList never frees them. +- d = d_ptr.loadAcquire(); ++ d = dd; + continue; + } + return d->lockForWrite(lock, timeout); +diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp +index 86dfa5faffc3..4c089091f8dc 100644 +--- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp ++++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp +@@ -57,6 +57,7 @@ private slots: + void multipleReadersLoop(); + void multipleWritersLoop(); + void multipleReadersWritersLoop(); ++ void heavyLoadLocks(); + void countingTest(); + void limitedReaders(); + void deleteOnUnlock(); +@@ -603,6 +604,111 @@ class ReadLockCountThread : public QThread + } + }; + ++class HeavyLoadLockThread : public QThread ++{ ++public: ++ QReadWriteLock &testRwlock; ++ const qsizetype iterations; ++ const int numThreads; ++ inline HeavyLoadLockThread(QReadWriteLock &l, qsizetype iters, int numThreads, QVector &counters): ++ testRwlock(l), ++ iterations(iters), ++ numThreads(numThreads), ++ counters(counters) ++ { } ++ ++private: ++ QVector &counters; ++ QAtomicInt *getCounter(qsizetype index) ++ { ++ QReadLocker locker(&testRwlock); ++ /* ++ The index is increased monotonically, so the index ++ being requested should be always within or at the end of the ++ counters vector. ++ */ ++ Q_ASSERT(index <= counters.size()); ++ if (counters.size() <= index || counters[index] == nullptr) { ++ locker.unlock(); ++ QWriteLocker wlocker(&testRwlock); ++ if (counters.size() <= index) ++ counters.resize(index + 1, nullptr); ++ if (counters[index] == nullptr) ++ counters[index] = new QAtomicInt(0); ++ return counters[index]; ++ } ++ return counters[index]; ++ } ++ void releaseCounter(qsizetype index) ++ { ++ QWriteLocker locker(&testRwlock); ++ delete counters[index]; ++ counters[index] = nullptr; ++ } ++ ++public: ++ void run() override ++ { ++ for (qsizetype i = 0; i < iterations; ++i) { ++ QAtomicInt *counter = getCounter(i); ++ /* ++ Here each counter is accessed by each thread ++ and increaed only once. As a result, when the ++ counter reaches numThreads, i.e. the fetched ++ value before the increment is numThreads-1, ++ we know all threads have accessed this counter ++ and we can delete it safely. ++ */ ++ int prev = counter->fetchAndAddRelaxed(1); ++ if (prev == numThreads - 1) { ++#ifdef QT_BUILDING_UNDER_TSAN ++ /* ++ Under TSAN, deleting and freeing an object ++ will trigger a write operation on the memory ++ of the object. Since we used fetchAndAddRelaxed ++ to update the counter, TSAN will report a data ++ race when deleting the counter here. To avoid ++ the false positive, we simply reset the counter ++ to 0 here, with ordered semantics to establish ++ the sequence to ensure the the free-ing option ++ happens after all fetchAndAddRelaxed operations ++ in other threads. ++ ++ When not building under TSAN, deleting the counter ++ will not result in any data read or written to the ++ memory region of the counter, so no data race will ++ happen. ++ */ ++ counter->fetchAndStoreOrdered(0); ++#endif ++ releaseCounter(i); ++ } ++ } ++ } ++}; ++ ++/* ++ Multiple threads racing acquiring and releasing ++ locks on the same indices. ++*/ ++ ++void tst_QReadWriteLock::heavyLoadLocks() ++{ ++ constexpr qsizetype iterations = 65536 * 4; ++ constexpr int numThreads = 8; ++ QVector counters; ++ QReadWriteLock testLock; ++ std::array, numThreads> threads; ++ for (auto &thread : threads) ++ thread = std::make_unique(testLock, iterations, numThreads, counters); ++ for (auto &thread : threads) ++ thread->start(); ++ for (auto &thread : threads) ++ thread->wait(); ++ QVERIFY(counters.size() == iterations); ++ for (qsizetype i = 0; i < iterations; ++i) ++ QVERIFY(counters[i] == nullptr); ++} + + /* + A writer acquires a read-lock, a reader locks diff -Nru qt6-base-6.8.2+dfsg/debian/patches/upstream_revert_simplify_locking.diff qt6-base-6.8.2+dfsg/debian/patches/upstream_revert_simplify_locking.diff --- qt6-base-6.8.2+dfsg/debian/patches/upstream_revert_simplify_locking.diff 1970-01-01 00:00:00.000000000 +0000 +++ qt6-base-6.8.2+dfsg/debian/patches/upstream_revert_simplify_locking.diff 2026-05-04 08:21:36.000000000 +0000 @@ -0,0 +1,200 @@ +From 080d61c020678b75ed9d5acb062ec82ba8fc402f Mon Sep 17 00:00:00 2001 +From: Miao Wang +Date: Thu, 22 Jan 2026 01:30:17 +0800 +Subject: [PATCH] Revert "QProcessEnvironment: simplify locking" + +This reverts commit c5d6b263c204cb09db2be36826e19acb03dc24fb. + +The commit being reverted assumes the mutex is only protecting 'nameMap' +and nothing else is mutable, which is false. The mutex is not only +protecting 'nameMap' but also protecting the containing value objects, +since even though the value object is accessed read-only, its +implementation mutates its internal states for 2-way conversion between +ByteArray and QString. + +Commit 85e61297f7b02297641826332dbdbc845a88c34b ("restore +QProcessEnvironment shared data thread safety on unix") said that +implicit sharing together with 'mutable' is a time bomb and the bomb is +triggered by the reverted commit. + +Fixes: QTBUG-142938 +Pick-to: 6.11 6.10 6.8 +Change-Id: I9e3234f0eb2c691eccf753a11f63fae9944bd503 +Reviewed-by: Thiago Macieira +Reviewed-by: Oswald Buddenhagen +--- + src/corelib/io/qprocess.cpp | 12 ++++++-- + src/corelib/io/qprocess_p.h | 50 ++++++++++++++++++-------------- + src/corelib/io/qprocess_unix.cpp | 2 ++ + 3 files changed, 40 insertions(+), 24 deletions(-) + +diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp +index 3e4f5cc27cd8..9d904c0bc186 100644 +--- a/src/corelib/io/qprocess.cpp ++++ b/src/corelib/io/qprocess.cpp +@@ -105,7 +105,6 @@ void QProcessEnvironmentPrivate::insert(const QProcessEnvironmentPrivate &other) + vars.insert(it.key(), it.value()); + + #ifdef Q_OS_UNIX +- const OrderedNameMapMutexLocker locker(this, &other); + auto nit = other.nameMap.constBegin(); + const auto nend = other.nameMap.constEnd(); + for ( ; nit != nend; ++nit) +@@ -206,8 +205,10 @@ bool comparesEqual(const QProcessEnvironment &lhs, const QProcessEnvironment &rh + { + if (lhs.d == rhs.d) + return true; +- +- return lhs.d && rhs.d && lhs.d->vars == rhs.d->vars; ++ if (!(lhs.d && rhs.d)) ++ return false; ++ QProcessEnvironmentPrivate::OrderedMutexLocker locker(lhs.d, rhs.d); ++ return lhs.d->vars == rhs.d->vars; + } + + /*! +@@ -265,6 +266,7 @@ bool QProcessEnvironment::contains(const QString &name) const + { + if (!d) + return false; ++ QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->vars.contains(d->prepareName(name)); + } + +@@ -315,6 +317,7 @@ QString QProcessEnvironment::value(const QString &name, const QString &defaultVa + if (!d) + return defaultValue; + ++ QProcessEnvironmentPrivate::MutexLocker locker(d); + const auto it = d->vars.constFind(d->prepareName(name)); + if (it == d->vars.constEnd()) + return defaultValue; +@@ -339,6 +342,7 @@ QStringList QProcessEnvironment::toStringList() const + { + if (!d) + return QStringList(); ++ QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->toList(); + } + +@@ -355,6 +359,7 @@ QStringList QProcessEnvironment::keys() const + { + if (!d) + return QStringList(); ++ QProcessEnvironmentPrivate::MutexLocker locker(d); + return d->keys(); + } + +@@ -371,6 +376,7 @@ void QProcessEnvironment::insert(const QProcessEnvironment &e) + return; + + // our re-impl of detach() detaches from null ++ QProcessEnvironmentPrivate::MutexLocker locker(e.d); + d->insert(*e.d); + } + +diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h +index e9634c13dca9..d48cab9c24ba 100644 +--- a/src/corelib/io/qprocess_p.h ++++ b/src/corelib/io/qprocess_p.h +@@ -111,22 +111,16 @@ class QProcessEnvironmentPrivate: public QSharedData + inline QString nameToString(const Key &name) const { return name; } + inline Value prepareValue(const QString &value) const { return value; } + inline QString valueToString(const Value &value) const { return value; } +-#else +- struct NameMapMutexLocker : public QMutexLocker +- { +- NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {} ++ struct MutexLocker { ++ MutexLocker(const QProcessEnvironmentPrivate *) {} + }; +- struct OrderedNameMapMutexLocker : public QOrderedMutexLocker +- { +- OrderedNameMapMutexLocker(const QProcessEnvironmentPrivate *d1, +- const QProcessEnvironmentPrivate *d2) +- : QOrderedMutexLocker(&d1->nameMapMutex, &d2->nameMapMutex) +- {} ++ struct OrderedMutexLocker { ++ OrderedMutexLocker(const QProcessEnvironmentPrivate *, ++ const QProcessEnvironmentPrivate *) {} + }; +- ++#else + inline Key prepareName(const QString &name) const + { +- const NameMapMutexLocker locker(this); + Key &ent = nameMap[name]; + if (ent.isEmpty()) + ent = name.toLocal8Bit(); +@@ -135,27 +129,40 @@ class QProcessEnvironmentPrivate: public QSharedData + inline QString nameToString(const Key &name) const + { + const QString sname = QString::fromLocal8Bit(name); +- { +- const NameMapMutexLocker locker(this); +- nameMap[sname] = name; +- } ++ nameMap[sname] = name; + return sname; + } + inline Value prepareValue(const QString &value) const { return Value(value); } + inline QString valueToString(const Value &value) const { return value.string(); } + ++ struct MutexLocker : public QMutexLocker ++ { ++ MutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->mutex) {} ++ }; ++ struct OrderedMutexLocker : public QOrderedMutexLocker ++ { ++ OrderedMutexLocker(const QProcessEnvironmentPrivate *d1, ++ const QProcessEnvironmentPrivate *d2) : ++ QOrderedMutexLocker(&d1->mutex, &d2->mutex) ++ {} ++ }; ++ + QProcessEnvironmentPrivate() : QSharedData() {} + QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate &other) : +- QSharedData(), vars(other.vars) ++ QSharedData() + { ++ // This being locked ensures that the functions that only assign ++ // d pointers don't need explicit locking. + // We don't need to lock our own mutex, as this object is new and + // consequently not shared. For the same reason, non-const methods + // do not need a lock, as they detach objects (however, we need to + // ensure that they really detach before using prepareName()). +- NameMapMutexLocker locker(&other); ++ MutexLocker locker(&other); ++ vars = other.vars; + nameMap = other.nameMap; +- // We need to detach our nameMap, so that our mutex can protect it. +- // As we are being detached, it likely would be detached a moment later anyway. ++ // We need to detach our members, so that our mutex can protect them. ++ // As we are being detached, they likely would be detached a moment later anyway. ++ vars.detach(); + nameMap.detach(); + } + #endif +@@ -166,7 +173,8 @@ class QProcessEnvironmentPrivate: public QSharedData + #ifdef Q_OS_UNIX + typedef QHash NameHash; + mutable NameHash nameMap; +- mutable QMutex nameMapMutex; ++ ++ mutable QMutex mutex; + #endif + + static QProcessEnvironment fromList(const QStringList &list); +diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp +index 41175b60ee8e..2338b3e91110 100644 +--- a/src/corelib/io/qprocess_unix.cpp ++++ b/src/corelib/io/qprocess_unix.cpp +@@ -425,6 +425,8 @@ QChildProcess::CharPointerList::CharPointerList(const QProcessEnvironmentPrivate + if (!environment) + return; + ++ QProcessEnvironmentPrivate::MutexLocker locker(environment); ++ + const QProcessEnvironmentPrivate::Map &env = environment->vars; + qsizetype count = env.size(); + pointers.reset(new char *[count + 1]);