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