commit bf1a754f6d3eb1f4599261bc4c0def08d47df1f9
parent e60b860a4ea7dabe06a376cb4095896f5daa7301
Author: Mike Hommey <mh@glandium.org>
Date: Mon, 5 Jan 2026 21:39:16 +0000
Bug 1984952 - Allow corner case of recursive locking from libgallium. r=sergesanspaille
Differential Revision: https://phabricator.services.mozilla.com/D276976
Diffstat:
6 files changed, 182 insertions(+), 0 deletions(-)
diff --git a/build/build-clang/clang-20.json b/build/build-clang/clang-20.json
@@ -18,6 +18,7 @@
"android-hardware-buffer-header-workaround.patch",
"arm64e-hack.patch",
"no-no-rosegment.patch",
+ "sanitizer_deadlock_suppression.patch",
"compiler-rt-rss-limit-heap-profile.patch"
]
}
diff --git a/build/build-clang/clang-21.json b/build/build-clang/clang-21.json
@@ -16,6 +16,7 @@
"arm64e-hack.patch",
"symbols.patch",
"no-no-rosegment.patch",
+ "sanitizer_deadlock_suppression.patch",
"compiler-rt-rss-limit-heap-profile.patch"
]
}
diff --git a/build/build-clang/clang-trunk.json b/build/build-clang/clang-trunk.json
@@ -11,6 +11,7 @@
"android-hardware-buffer-header-workaround_clang_21.patch",
"arm64e-hack.patch",
"no-no-rosegment.patch",
+ "sanitizer_deadlock_suppression_clang_22.patch",
"compiler-rt-rss-limit-heap-profile.patch"
]
}
diff --git a/build/build-clang/sanitizer_deadlock_suppression.patch b/build/build-clang/sanitizer_deadlock_suppression.patch
@@ -0,0 +1,86 @@
+Add support for suppressing corner cases of recursive locking that TSAN
+normally doesn't allow.
+
+diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
+index ccb7065b07ae..58cfc5e1a1fb 100644
+--- a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
++++ b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
+@@ -15,6 +15,7 @@
+ #include "sanitizer_allocator_internal.h"
+ #include "sanitizer_placement_new.h"
+ #include "sanitizer_mutex.h"
++#include "sanitizer_stackdepot.h"
+
+ #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+
+@@ -162,8 +163,19 @@ void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
+
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+- if (wlock) // Only a recursive rlock may be held.
+- CHECK(!dd.isHeld(<->dd, m->id));
++ // Only a recursive rlock may be held.
++ if (wlock && dd.isHeld(<->dd, m->id)) {
++ // Get stack traces from where the lock is already held.
++ u32 held_stk = dd.findLockContext(<->dd, m->id);
++ if (!cb->IsDeadlockSuppressed(held_stk)) {
++ stk = stk ? stk : cb->Unwind();
++ if (!cb->IsDeadlockSuppressed(stk)) {
++ // We could avoid calling this twice, by storing the result above, but
++ // we do want the error message to be unchanged.
++ CHECK(!dd.isHeld(<->dd, m->id));
++ }
++ }
++ }
+ if (!trylock)
+ dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
+ dd.onLockAfter(<->dd, m->id, stk);
+diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
+index 7f461c98bade..252e62e05622 100644
+--- a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
++++ b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
+@@ -66,6 +66,7 @@ struct DDCallback {
+
+ virtual u32 Unwind() { return 0; }
+ virtual int UniqueTid() { return 0; }
++ virtual bool IsDeadlockSuppressed(u32 stk) { return false; }
+
+ protected:
+ ~DDCallback() {}
+diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+index 2a8aa1915c9a..1df8acfd7fd0 100644
+--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
++++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+@@ -15,8 +15,10 @@
+
+ #include "tsan_rtl.h"
+ #include "tsan_flags.h"
++#include "tsan_mman.h"
+ #include "tsan_sync.h"
+ #include "tsan_report.h"
++#include "tsan_suppressions.h"
+ #include "tsan_symbolize.h"
+ #include "tsan_platform.h"
+
+@@ -39,6 +41,21 @@ struct Callback final : public DDCallback {
+
+ StackID Unwind() override { return CurrentStackId(thr, pc); }
+ int UniqueTid() override { return thr->tid; }
++
++ bool IsDeadlockSuppressed(u32 stk) override {
++ bool result = false;
++ if (stk) {
++ Suppression *supp = nullptr;
++ ReportStack *rs = SymbolizeStackId(stk);
++ rs->suppressable = true;
++ result = IsSuppressed(ReportTypeDeadlock, rs, &supp);
++ if (rs->frames) {
++ rs->frames->ClearAll();
++ }
++ DestroyAndFree(rs);
++ }
++ return result;
++ }
+ };
+
+ void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
diff --git a/build/build-clang/sanitizer_deadlock_suppression_clang_22.patch b/build/build-clang/sanitizer_deadlock_suppression_clang_22.patch
@@ -0,0 +1,87 @@
+Add support for suppressing corner cases of recursive locking that TSAN
+normally doesn't allow.
+
+diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
+index ccb7065b07ae..58cfc5e1a1fb 100644
+--- a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
++++ b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp
+@@ -15,6 +15,7 @@
+ #include "sanitizer_allocator_internal.h"
+ #include "sanitizer_placement_new.h"
+ #include "sanitizer_mutex.h"
++#include "sanitizer_stackdepot.h"
+
+ #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+
+@@ -162,8 +163,19 @@ void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
+
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+- if (wlock) // Only a recursive rlock may be held.
+- CHECK(!dd.isHeld(<->dd, m->id));
++ // Only a recursive rlock may be held.
++ if (wlock && dd.isHeld(<->dd, m->id)) {
++ // Get stack traces from where the lock is already held.
++ u32 held_stk = dd.findLockContext(<->dd, m->id);
++ if (!cb->IsDeadlockSuppressed(held_stk)) {
++ stk = stk ? stk : cb->Unwind();
++ if (!cb->IsDeadlockSuppressed(stk)) {
++ // We could avoid calling this twice, by storing the result above, but
++ // we do want the error message to be unchanged.
++ CHECK(!dd.isHeld(<->dd, m->id));
++ }
++ }
++ }
+ if (!trylock)
+ dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
+ dd.onLockAfter(<->dd, m->id, stk);
+diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
+index 7f461c98bade..252e62e05622 100644
+--- a/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
++++ b/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h
+@@ -66,6 +66,7 @@ struct DDCallback {
+
+ virtual u32 Unwind() { return 0; }
+ virtual int UniqueTid() { return 0; }
++ virtual bool IsDeadlockSuppressed(u32 stk) { return false; }
+
+ protected:
+ ~DDCallback() {}
+diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+index 30f5e964939d..2b254e323330 100644
+--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
++++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+@@ -15,9 +15,11 @@
+ #include <sanitizer_common/sanitizer_stackdepot.h>
+
+ #include "tsan_flags.h"
++#include "tsan_mman.h"
+ #include "tsan_platform.h"
+ #include "tsan_report.h"
+ #include "tsan_rtl.h"
++#include "tsan_suppressions.h"
+ #include "tsan_symbolize.h"
+ #include "tsan_sync.h"
+
+@@ -40,6 +42,21 @@ struct Callback final : public DDCallback {
+
+ StackID Unwind() override { return CurrentStackId(thr, pc); }
+ int UniqueTid() override { return thr->tid; }
++
++ bool IsDeadlockSuppressed(u32 stk) override {
++ bool result = false;
++ if (stk) {
++ Suppression *supp = nullptr;
++ ReportStack *rs = SymbolizeStackId(stk);
++ rs->suppressable = true;
++ result = IsSuppressed(ReportTypeDeadlock, rs, &supp);
++ if (rs->frames) {
++ rs->frames->ClearAll();
++ }
++ DestroyAndFree(rs);
++ }
++ return result;
++ }
+ };
+
+ void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
diff --git a/build/sanitizers/TsanOptions.cpp b/build/sanitizers/TsanOptions.cpp
@@ -200,6 +200,12 @@ extern "C" MOZ_EXPORT const char* __tsan_default_suppressions() {
"deadlock:EncryptedClientHelloServer\n"
// Bug 1682861 - permanent
"deadlock:nsDOMWindowUtils::CompareCanvases\n"
+ // Bug 1984952 - not technically necessarily a deadlock, but a weird case of
+ // recursive locking that tsan normally doesn't allow, that is not clear yet
+ // how it happens and whether it's actually problematic, but it's originating
+ // from a system library so we can't do much about fixing it (except if it's
+ // actually a tsan bug).
+ "deadlock:libgallium-*.so\n"