tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mbuild/build-clang/clang-20.json | 1+
Mbuild/build-clang/clang-21.json | 1+
Mbuild/build-clang/clang-trunk.json | 1+
Abuild/build-clang/sanitizer_deadlock_suppression.patch | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild/build-clang/sanitizer_deadlock_suppression_clang_22.patch | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbuild/sanitizers/TsanOptions.cpp | 6++++++
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(&lt->dd, m->id)); ++ // Only a recursive rlock may be held. ++ if (wlock && dd.isHeld(&lt->dd, m->id)) { ++ // Get stack traces from where the lock is already held. ++ u32 held_stk = dd.findLockContext(&lt->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(&lt->dd, m->id)); ++ } ++ } ++ } + if (!trylock) + dd.addEdges(&lt->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid()); + dd.onLockAfter(&lt->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(&lt->dd, m->id)); ++ // Only a recursive rlock may be held. ++ if (wlock && dd.isHeld(&lt->dd, m->id)) { ++ // Get stack traces from where the lock is already held. ++ u32 held_stk = dd.findLockContext(&lt->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(&lt->dd, m->id)); ++ } ++ } ++ } + if (!trylock) + dd.addEdges(&lt->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid()); + dd.onLockAfter(&lt->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"