xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_mutex.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1fe6060f1SDimitry Andric //===-- sanitizer_mutex.cpp -----------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric //
9fe6060f1SDimitry Andric // This file is shared between AddressSanitizer and ThreadSanitizer
10fe6060f1SDimitry Andric // run-time libraries.
11fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
12fe6060f1SDimitry Andric 
13fe6060f1SDimitry Andric #include "sanitizer_mutex.h"
14fe6060f1SDimitry Andric 
15fe6060f1SDimitry Andric #include "sanitizer_common.h"
16fe6060f1SDimitry Andric 
17fe6060f1SDimitry Andric namespace __sanitizer {
18fe6060f1SDimitry Andric 
19fe6060f1SDimitry Andric void StaticSpinMutex::LockSlow() {
20fe6060f1SDimitry Andric   for (int i = 0;; i++) {
21fe6060f1SDimitry Andric     if (i < 100)
22fe6060f1SDimitry Andric       proc_yield(1);
23fe6060f1SDimitry Andric     else
24fe6060f1SDimitry Andric       internal_sched_yield();
25fe6060f1SDimitry Andric     if (atomic_load(&state_, memory_order_relaxed) == 0 &&
26fe6060f1SDimitry Andric         atomic_exchange(&state_, 1, memory_order_acquire) == 0)
27fe6060f1SDimitry Andric       return;
28fe6060f1SDimitry Andric   }
29fe6060f1SDimitry Andric }
30fe6060f1SDimitry Andric 
31fe6060f1SDimitry Andric void Semaphore::Wait() {
32fe6060f1SDimitry Andric   u32 count = atomic_load(&state_, memory_order_relaxed);
33fe6060f1SDimitry Andric   for (;;) {
34fe6060f1SDimitry Andric     if (count == 0) {
35fe6060f1SDimitry Andric       FutexWait(&state_, 0);
36fe6060f1SDimitry Andric       count = atomic_load(&state_, memory_order_relaxed);
37fe6060f1SDimitry Andric       continue;
38fe6060f1SDimitry Andric     }
39fe6060f1SDimitry Andric     if (atomic_compare_exchange_weak(&state_, &count, count - 1,
40fe6060f1SDimitry Andric                                      memory_order_acquire))
41fe6060f1SDimitry Andric       break;
42fe6060f1SDimitry Andric   }
43fe6060f1SDimitry Andric }
44fe6060f1SDimitry Andric 
45fe6060f1SDimitry Andric void Semaphore::Post(u32 count) {
46fe6060f1SDimitry Andric   CHECK_NE(count, 0);
47fe6060f1SDimitry Andric   atomic_fetch_add(&state_, count, memory_order_release);
48fe6060f1SDimitry Andric   FutexWake(&state_, count);
49fe6060f1SDimitry Andric }
50fe6060f1SDimitry Andric 
51fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS
52fe6060f1SDimitry Andric // An empty mutex meta table, it effectively disables deadlock detection.
53fe6060f1SDimitry Andric // Each tool can override the table to define own mutex hierarchy and
54fe6060f1SDimitry Andric // enable deadlock detection.
55fe6060f1SDimitry Andric // The table defines a static mutex type hierarchy (what mutex types can be locked
56fe6060f1SDimitry Andric // under what mutex types). This table is checked to be acyclic and then
57fe6060f1SDimitry Andric // actual mutex lock/unlock operations are checked to adhere to this hierarchy.
58fe6060f1SDimitry Andric // The checking happens on mutex types rather than on individual mutex instances
59fe6060f1SDimitry Andric // because doing it on mutex instances will both significantly complicate
60fe6060f1SDimitry Andric // the implementation, worsen performance and memory overhead and is mostly
61fe6060f1SDimitry Andric // unnecessary (we almost never lock multiple mutexes of the same type recursively).
62fe6060f1SDimitry Andric static constexpr int kMutexTypeMax = 20;
63fe6060f1SDimitry Andric SANITIZER_WEAK_ATTRIBUTE MutexMeta mutex_meta[kMutexTypeMax] = {};
64fe6060f1SDimitry Andric SANITIZER_WEAK_ATTRIBUTE void PrintMutexPC(uptr pc) {}
65fe6060f1SDimitry Andric static StaticSpinMutex mutex_meta_mtx;
66fe6060f1SDimitry Andric static int mutex_type_count = -1;
67fe6060f1SDimitry Andric // Adjacency matrix of what mutexes can be locked under what mutexes.
68fe6060f1SDimitry Andric static bool mutex_can_lock[kMutexTypeMax][kMutexTypeMax];
69fe6060f1SDimitry Andric // Mutex types with MutexMulti mark.
70fe6060f1SDimitry Andric static bool mutex_multi[kMutexTypeMax];
71fe6060f1SDimitry Andric 
72fe6060f1SDimitry Andric void DebugMutexInit() {
73fe6060f1SDimitry Andric   // Build adjacency matrix.
74fe6060f1SDimitry Andric   bool leaf[kMutexTypeMax];
75fe6060f1SDimitry Andric   internal_memset(&leaf, 0, sizeof(leaf));
76349cc55cSDimitry Andric   int cnt[kMutexTypeMax];
77fe6060f1SDimitry Andric   internal_memset(&cnt, 0, sizeof(cnt));
78fe6060f1SDimitry Andric   for (int t = 0; t < kMutexTypeMax; t++) {
79fe6060f1SDimitry Andric     mutex_type_count = t;
80fe6060f1SDimitry Andric     if (!mutex_meta[t].name)
81fe6060f1SDimitry Andric       break;
82fe6060f1SDimitry Andric     CHECK_EQ(t, mutex_meta[t].type);
83fe6060f1SDimitry Andric     for (uptr j = 0; j < ARRAY_SIZE(mutex_meta[t].can_lock); j++) {
84fe6060f1SDimitry Andric       MutexType z = mutex_meta[t].can_lock[j];
85fe6060f1SDimitry Andric       if (z == MutexInvalid)
86fe6060f1SDimitry Andric         break;
87fe6060f1SDimitry Andric       if (z == MutexLeaf) {
88fe6060f1SDimitry Andric         CHECK(!leaf[t]);
89fe6060f1SDimitry Andric         leaf[t] = true;
90fe6060f1SDimitry Andric         continue;
91fe6060f1SDimitry Andric       }
92fe6060f1SDimitry Andric       if (z == MutexMulti) {
93fe6060f1SDimitry Andric         mutex_multi[t] = true;
94fe6060f1SDimitry Andric         continue;
95fe6060f1SDimitry Andric       }
96fe6060f1SDimitry Andric       CHECK_LT(z, kMutexTypeMax);
97fe6060f1SDimitry Andric       CHECK(!mutex_can_lock[t][z]);
98fe6060f1SDimitry Andric       mutex_can_lock[t][z] = true;
99fe6060f1SDimitry Andric       cnt[t]++;
100fe6060f1SDimitry Andric     }
101fe6060f1SDimitry Andric   }
102fe6060f1SDimitry Andric   // Indicates the array is not properly terminated.
103fe6060f1SDimitry Andric   CHECK_LT(mutex_type_count, kMutexTypeMax);
104fe6060f1SDimitry Andric   // Add leaf mutexes.
105fe6060f1SDimitry Andric   for (int t = 0; t < mutex_type_count; t++) {
106fe6060f1SDimitry Andric     if (!leaf[t])
107fe6060f1SDimitry Andric       continue;
108fe6060f1SDimitry Andric     CHECK_EQ(cnt[t], 0);
109fe6060f1SDimitry Andric     for (int z = 0; z < mutex_type_count; z++) {
110fe6060f1SDimitry Andric       if (z == MutexInvalid || t == z || leaf[z])
111fe6060f1SDimitry Andric         continue;
112fe6060f1SDimitry Andric       CHECK(!mutex_can_lock[z][t]);
113fe6060f1SDimitry Andric       mutex_can_lock[z][t] = true;
114fe6060f1SDimitry Andric     }
115fe6060f1SDimitry Andric   }
116fe6060f1SDimitry Andric   // Build the transitive closure and check that the graphs is acyclic.
117fe6060f1SDimitry Andric   u32 trans[kMutexTypeMax];
118fe6060f1SDimitry Andric   static_assert(sizeof(trans[0]) * 8 >= kMutexTypeMax,
119fe6060f1SDimitry Andric                 "kMutexTypeMax does not fit into u32, switch to u64");
120fe6060f1SDimitry Andric   internal_memset(&trans, 0, sizeof(trans));
121fe6060f1SDimitry Andric   for (int i = 0; i < mutex_type_count; i++) {
122fe6060f1SDimitry Andric     for (int j = 0; j < mutex_type_count; j++)
123fe6060f1SDimitry Andric       if (mutex_can_lock[i][j])
124fe6060f1SDimitry Andric         trans[i] |= 1 << j;
125fe6060f1SDimitry Andric   }
126fe6060f1SDimitry Andric   for (int k = 0; k < mutex_type_count; k++) {
127fe6060f1SDimitry Andric     for (int i = 0; i < mutex_type_count; i++) {
128fe6060f1SDimitry Andric       if (trans[i] & (1 << k))
129fe6060f1SDimitry Andric         trans[i] |= trans[k];
130fe6060f1SDimitry Andric     }
131fe6060f1SDimitry Andric   }
132fe6060f1SDimitry Andric   for (int i = 0; i < mutex_type_count; i++) {
133fe6060f1SDimitry Andric     if (trans[i] & (1 << i)) {
134fe6060f1SDimitry Andric       Printf("Mutex %s participates in a cycle\n", mutex_meta[i].name);
135fe6060f1SDimitry Andric       Die();
136fe6060f1SDimitry Andric     }
137fe6060f1SDimitry Andric   }
138fe6060f1SDimitry Andric }
139fe6060f1SDimitry Andric 
140fe6060f1SDimitry Andric struct InternalDeadlockDetector {
141fe6060f1SDimitry Andric   struct LockDesc {
142fe6060f1SDimitry Andric     u64 seq;
143fe6060f1SDimitry Andric     uptr pc;
144fe6060f1SDimitry Andric     int recursion;
145fe6060f1SDimitry Andric   };
146fe6060f1SDimitry Andric   int initialized;
147fe6060f1SDimitry Andric   u64 sequence;
148fe6060f1SDimitry Andric   LockDesc locked[kMutexTypeMax];
149fe6060f1SDimitry Andric 
150fe6060f1SDimitry Andric   void Lock(MutexType type, uptr pc) {
151fe6060f1SDimitry Andric     if (!Initialize(type))
152fe6060f1SDimitry Andric       return;
153fe6060f1SDimitry Andric     CHECK_LT(type, mutex_type_count);
154fe6060f1SDimitry Andric     // Find the last locked mutex type.
155fe6060f1SDimitry Andric     // This is the type we will use for hierarchy checks.
156fe6060f1SDimitry Andric     u64 max_seq = 0;
157fe6060f1SDimitry Andric     MutexType max_idx = MutexInvalid;
158fe6060f1SDimitry Andric     for (int i = 0; i != mutex_type_count; i++) {
159fe6060f1SDimitry Andric       if (locked[i].seq == 0)
160fe6060f1SDimitry Andric         continue;
161fe6060f1SDimitry Andric       CHECK_NE(locked[i].seq, max_seq);
162fe6060f1SDimitry Andric       if (max_seq < locked[i].seq) {
163fe6060f1SDimitry Andric         max_seq = locked[i].seq;
164fe6060f1SDimitry Andric         max_idx = (MutexType)i;
165fe6060f1SDimitry Andric       }
166fe6060f1SDimitry Andric     }
167fe6060f1SDimitry Andric     if (max_idx == type && mutex_multi[type]) {
168fe6060f1SDimitry Andric       // Recursive lock of the same type.
169fe6060f1SDimitry Andric       CHECK_EQ(locked[type].seq, max_seq);
170fe6060f1SDimitry Andric       CHECK(locked[type].pc);
171fe6060f1SDimitry Andric       locked[type].recursion++;
172fe6060f1SDimitry Andric       return;
173fe6060f1SDimitry Andric     }
174fe6060f1SDimitry Andric     if (max_idx != MutexInvalid && !mutex_can_lock[max_idx][type]) {
175fe6060f1SDimitry Andric       Printf("%s: internal deadlock: can't lock %s under %s mutex\n", SanitizerToolName,
176fe6060f1SDimitry Andric              mutex_meta[type].name, mutex_meta[max_idx].name);
177349cc55cSDimitry Andric       PrintMutexPC(locked[max_idx].pc);
178fe6060f1SDimitry Andric       CHECK(0);
179fe6060f1SDimitry Andric     }
180fe6060f1SDimitry Andric     locked[type].seq = ++sequence;
181fe6060f1SDimitry Andric     locked[type].pc = pc;
182fe6060f1SDimitry Andric     locked[type].recursion = 1;
183fe6060f1SDimitry Andric   }
184fe6060f1SDimitry Andric 
185fe6060f1SDimitry Andric   void Unlock(MutexType type) {
186fe6060f1SDimitry Andric     if (!Initialize(type))
187fe6060f1SDimitry Andric       return;
188fe6060f1SDimitry Andric     CHECK_LT(type, mutex_type_count);
189fe6060f1SDimitry Andric     CHECK(locked[type].seq);
190fe6060f1SDimitry Andric     CHECK_GT(locked[type].recursion, 0);
191fe6060f1SDimitry Andric     if (--locked[type].recursion)
192fe6060f1SDimitry Andric       return;
193fe6060f1SDimitry Andric     locked[type].seq = 0;
194fe6060f1SDimitry Andric     locked[type].pc = 0;
195fe6060f1SDimitry Andric   }
196fe6060f1SDimitry Andric 
197fe6060f1SDimitry Andric   void CheckNoLocks() {
198fe6060f1SDimitry Andric     for (int i = 0; i < mutex_type_count; i++) CHECK_EQ(locked[i].recursion, 0);
199fe6060f1SDimitry Andric   }
200fe6060f1SDimitry Andric 
201fe6060f1SDimitry Andric   bool Initialize(MutexType type) {
202fe6060f1SDimitry Andric     if (type == MutexUnchecked || type == MutexInvalid)
203fe6060f1SDimitry Andric       return false;
204fe6060f1SDimitry Andric     CHECK_GT(type, MutexInvalid);
205fe6060f1SDimitry Andric     if (initialized != 0)
206fe6060f1SDimitry Andric       return initialized > 0;
207fe6060f1SDimitry Andric     initialized = -1;
208fe6060f1SDimitry Andric     SpinMutexLock lock(&mutex_meta_mtx);
209fe6060f1SDimitry Andric     if (mutex_type_count < 0)
210fe6060f1SDimitry Andric       DebugMutexInit();
211fe6060f1SDimitry Andric     initialized = mutex_type_count ? 1 : -1;
212fe6060f1SDimitry Andric     return initialized > 0;
213fe6060f1SDimitry Andric   }
214fe6060f1SDimitry Andric };
215*0fca6ea1SDimitry Andric // This variable is used by the __tls_get_addr interceptor, so cannot use the
216*0fca6ea1SDimitry Andric // global-dynamic TLS model, as that would result in crashes.
217*0fca6ea1SDimitry Andric __attribute__((tls_model("initial-exec"))) static THREADLOCAL
218*0fca6ea1SDimitry Andric     InternalDeadlockDetector deadlock_detector;
219fe6060f1SDimitry Andric 
220fe6060f1SDimitry Andric void CheckedMutex::LockImpl(uptr pc) { deadlock_detector.Lock(type_, pc); }
221fe6060f1SDimitry Andric 
222fe6060f1SDimitry Andric void CheckedMutex::UnlockImpl() { deadlock_detector.Unlock(type_); }
223fe6060f1SDimitry Andric 
224fe6060f1SDimitry Andric void CheckedMutex::CheckNoLocksImpl() { deadlock_detector.CheckNoLocks(); }
225fe6060f1SDimitry Andric #endif
226fe6060f1SDimitry Andric 
227fe6060f1SDimitry Andric }  // namespace __sanitizer
228