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