1 //===-- tsan_platform_mac.cc ----------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file is a part of ThreadSanitizer (TSan), a race detector. 11 // 12 // Mac-specific code. 13 //===----------------------------------------------------------------------===// 14 15 #include "sanitizer_common/sanitizer_platform.h" 16 #if SANITIZER_MAC 17 18 #include "sanitizer_common/sanitizer_atomic.h" 19 #include "sanitizer_common/sanitizer_common.h" 20 #include "sanitizer_common/sanitizer_libc.h" 21 #include "sanitizer_common/sanitizer_posix.h" 22 #include "sanitizer_common/sanitizer_procmaps.h" 23 #include "tsan_platform.h" 24 #include "tsan_rtl.h" 25 #include "tsan_flags.h" 26 27 #include <pthread.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <stdarg.h> 33 #include <sys/mman.h> 34 #include <sys/syscall.h> 35 #include <sys/time.h> 36 #include <sys/types.h> 37 #include <sys/resource.h> 38 #include <sys/stat.h> 39 #include <unistd.h> 40 #include <errno.h> 41 #include <sched.h> 42 43 namespace __tsan { 44 45 #ifndef SANITIZER_GO 46 static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) { 47 atomic_uintptr_t *a = (atomic_uintptr_t *)dst; 48 void *val = (void *)atomic_load_relaxed(a); 49 atomic_signal_fence(memory_order_acquire); // Turns the previous load into 50 // acquire wrt signals. 51 if (UNLIKELY(val == nullptr)) { 52 val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE, 53 MAP_PRIVATE | MAP_ANON, -1, 0); 54 CHECK(val); 55 void *cmp = nullptr; 56 if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val, 57 memory_order_acq_rel)) { 58 internal_munmap(val, size); 59 val = cmp; 60 } 61 } 62 return val; 63 } 64 65 // On OS X, accessing TLVs via __thread or manually by using pthread_key_* is 66 // problematic, because there are several places where interceptors are called 67 // when TLVs are not accessible (early process startup, thread cleanup, ...). 68 // The following provides a "poor man's TLV" implementation, where we use the 69 // shadow memory of the pointer returned by pthread_self() to store a pointer to 70 // the ThreadState object. The main thread's ThreadState pointer is stored 71 // separately in a static variable, because we need to access it even before the 72 // shadow memory is set up. 73 static uptr main_thread_identity = 0; 74 static ThreadState *main_thread_state = nullptr; 75 76 ThreadState *cur_thread() { 77 ThreadState **fake_tls; 78 uptr thread_identity = (uptr)pthread_self(); 79 if (thread_identity == main_thread_identity || main_thread_identity == 0) { 80 fake_tls = &main_thread_state; 81 } else { 82 fake_tls = (ThreadState **)MemToShadow(thread_identity); 83 } 84 ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate( 85 (uptr *)fake_tls, sizeof(ThreadState)); 86 return thr; 87 } 88 89 // TODO(kuba.brecka): This is not async-signal-safe. In particular, we call 90 // munmap first and then clear `fake_tls`; if we receive a signal in between, 91 // handler will try to access the unmapped ThreadState. 92 void cur_thread_finalize() { 93 uptr thread_identity = (uptr)pthread_self(); 94 CHECK_NE(thread_identity, main_thread_identity); 95 ThreadState **fake_tls = (ThreadState **)MemToShadow(thread_identity); 96 internal_munmap(*fake_tls, sizeof(ThreadState)); 97 *fake_tls = nullptr; 98 } 99 #endif 100 101 uptr GetShadowMemoryConsumption() { 102 return 0; 103 } 104 105 void FlushShadowMemory() { 106 } 107 108 void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { 109 } 110 111 #ifndef SANITIZER_GO 112 void InitializeShadowMemoryPlatform() { } 113 114 // On OS X, GCD worker threads are created without a call to pthread_create. We 115 // need to properly register these threads with ThreadCreate and ThreadStart. 116 // These threads don't have a parent thread, as they are created "spuriously". 117 // We're using a libpthread API that notifies us about a newly created thread. 118 // The `thread == pthread_self()` check indicates this is actually a worker 119 // thread. If it's just a regular thread, this hook is called on the parent 120 // thread. 121 typedef void (*pthread_introspection_hook_t)(unsigned int event, 122 pthread_t thread, void *addr, 123 size_t size); 124 extern "C" pthread_introspection_hook_t pthread_introspection_hook_install( 125 pthread_introspection_hook_t hook); 126 static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1; 127 static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3; 128 static pthread_introspection_hook_t prev_pthread_introspection_hook; 129 static void my_pthread_introspection_hook(unsigned int event, pthread_t thread, 130 void *addr, size_t size) { 131 if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) { 132 if (thread == pthread_self()) { 133 // The current thread is a newly created GCD worker thread. 134 ThreadState *parent_thread_state = nullptr; // No parent. 135 int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true); 136 CHECK_NE(tid, 0); 137 ThreadState *thr = cur_thread(); 138 ThreadStart(thr, tid, GetTid()); 139 } 140 } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) { 141 if (thread == pthread_self()) { 142 ThreadState *thr = cur_thread(); 143 if (thr->tctx) { 144 DestroyThreadState(); 145 } 146 } 147 } 148 149 if (prev_pthread_introspection_hook != nullptr) 150 prev_pthread_introspection_hook(event, thread, addr, size); 151 } 152 #endif 153 154 void InitializePlatformEarly() { 155 } 156 157 void InitializePlatform() { 158 DisableCoreDumperIfNecessary(); 159 #ifndef SANITIZER_GO 160 CheckAndProtect(); 161 162 CHECK_EQ(main_thread_identity, 0); 163 main_thread_identity = (uptr)pthread_self(); 164 165 prev_pthread_introspection_hook = 166 pthread_introspection_hook_install(&my_pthread_introspection_hook); 167 #endif 168 } 169 170 #ifndef SANITIZER_GO 171 // Note: this function runs with async signals enabled, 172 // so it must not touch any tsan state. 173 int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, 174 void *abstime), void *c, void *m, void *abstime, 175 void(*cleanup)(void *arg), void *arg) { 176 // pthread_cleanup_push/pop are hardcore macros mess. 177 // We can't intercept nor call them w/o including pthread.h. 178 int res; 179 pthread_cleanup_push(cleanup, arg); 180 res = fn(c, m, abstime); 181 pthread_cleanup_pop(0); 182 return res; 183 } 184 #endif 185 186 bool IsGlobalVar(uptr addr) { 187 return false; 188 } 189 190 } // namespace __tsan 191 192 #endif // SANITIZER_MAC 193