1 //===-- tsan_platform_mac.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 a part of ThreadSanitizer (TSan), a race detector. 10 // 11 // Mac-specific code. 12 //===----------------------------------------------------------------------===// 13 14 #include "sanitizer_common/sanitizer_platform.h" 15 #if SANITIZER_MAC 16 17 #include "sanitizer_common/sanitizer_atomic.h" 18 #include "sanitizer_common/sanitizer_common.h" 19 #include "sanitizer_common/sanitizer_libc.h" 20 #include "sanitizer_common/sanitizer_posix.h" 21 #include "sanitizer_common/sanitizer_procmaps.h" 22 #include "sanitizer_common/sanitizer_ptrauth.h" 23 #include "sanitizer_common/sanitizer_stackdepot.h" 24 #include "tsan_platform.h" 25 #include "tsan_rtl.h" 26 #include "tsan_flags.h" 27 28 #include <limits.h> 29 #include <mach/mach.h> 30 #include <pthread.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <stdarg.h> 36 #include <sys/mman.h> 37 #include <sys/syscall.h> 38 #include <sys/time.h> 39 #include <sys/types.h> 40 #include <sys/resource.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <errno.h> 44 #include <sched.h> 45 46 namespace __tsan { 47 48 #if !SANITIZER_GO 49 static char main_thread_state[sizeof(ThreadState)] ALIGNED( 50 SANITIZER_CACHE_LINE_SIZE); 51 static ThreadState *dead_thread_state; 52 static pthread_key_t thread_state_key; 53 54 // We rely on the following documented, but Darwin-specific behavior to keep the 55 // reference to the ThreadState object alive in TLS: 56 // pthread_key_create man page: 57 // If, after all the destructors have been called for all non-NULL values with 58 // associated destructors, there are still some non-NULL values with 59 // associated destructors, then the process is repeated. If, after at least 60 // [PTHREAD_DESTRUCTOR_ITERATIONS] iterations of destructor calls for 61 // outstanding non-NULL values, there are still some non-NULL values with 62 // associated destructors, the implementation stops calling destructors. 63 static_assert(PTHREAD_DESTRUCTOR_ITERATIONS == 4, "Small number of iterations"); 64 static void ThreadStateDestructor(void *thr) { 65 int res = pthread_setspecific(thread_state_key, thr); 66 CHECK_EQ(res, 0); 67 } 68 69 static void InitializeThreadStateStorage() { 70 int res; 71 CHECK_EQ(thread_state_key, 0); 72 res = pthread_key_create(&thread_state_key, ThreadStateDestructor); 73 CHECK_EQ(res, 0); 74 res = pthread_setspecific(thread_state_key, main_thread_state); 75 CHECK_EQ(res, 0); 76 77 auto dts = (ThreadState *)MmapOrDie(sizeof(ThreadState), "ThreadState"); 78 dts->fast_state.SetIgnoreBit(); 79 dts->ignore_interceptors = 1; 80 dts->is_dead = true; 81 const_cast<Tid &>(dts->tid) = kInvalidTid; 82 res = internal_mprotect(dts, sizeof(ThreadState), PROT_READ); // immutable 83 CHECK_EQ(res, 0); 84 dead_thread_state = dts; 85 } 86 87 ThreadState *cur_thread() { 88 // Some interceptors get called before libpthread has been initialized and in 89 // these cases we must avoid calling any pthread APIs. 90 if (UNLIKELY(!thread_state_key)) { 91 return (ThreadState *)main_thread_state; 92 } 93 94 // We only reach this line after InitializeThreadStateStorage() ran, i.e, 95 // after TSan (and therefore libpthread) have been initialized. 96 ThreadState *thr = (ThreadState *)pthread_getspecific(thread_state_key); 97 if (UNLIKELY(!thr)) { 98 thr = (ThreadState *)MmapOrDie(sizeof(ThreadState), "ThreadState"); 99 int res = pthread_setspecific(thread_state_key, thr); 100 CHECK_EQ(res, 0); 101 } 102 return thr; 103 } 104 105 void set_cur_thread(ThreadState *thr) { 106 int res = pthread_setspecific(thread_state_key, thr); 107 CHECK_EQ(res, 0); 108 } 109 110 void cur_thread_finalize() { 111 ThreadState *thr = (ThreadState *)pthread_getspecific(thread_state_key); 112 CHECK(thr); 113 if (thr == (ThreadState *)main_thread_state) { 114 // Calling dispatch_main() or xpc_main() actually invokes pthread_exit to 115 // exit the main thread. Let's keep the main thread's ThreadState. 116 return; 117 } 118 // Intercepted functions can still get called after cur_thread_finalize() 119 // (called from DestroyThreadState()), so put a fake thread state for "dead" 120 // threads. An alternative solution would be to release the ThreadState 121 // object from THREAD_DESTROY (which is delivered later and on the parent 122 // thread) instead of THREAD_TERMINATE. 123 int res = pthread_setspecific(thread_state_key, dead_thread_state); 124 CHECK_EQ(res, 0); 125 UnmapOrDie(thr, sizeof(ThreadState)); 126 } 127 #endif 128 129 void FlushShadowMemory() { 130 } 131 132 static void RegionMemUsage(uptr start, uptr end, uptr *res, uptr *dirty) { 133 vm_address_t address = start; 134 vm_address_t end_address = end; 135 uptr resident_pages = 0; 136 uptr dirty_pages = 0; 137 while (address < end_address) { 138 vm_size_t vm_region_size; 139 mach_msg_type_number_t count = VM_REGION_EXTENDED_INFO_COUNT; 140 vm_region_extended_info_data_t vm_region_info; 141 mach_port_t object_name; 142 kern_return_t ret = vm_region_64( 143 mach_task_self(), &address, &vm_region_size, VM_REGION_EXTENDED_INFO, 144 (vm_region_info_t)&vm_region_info, &count, &object_name); 145 if (ret != KERN_SUCCESS) break; 146 147 resident_pages += vm_region_info.pages_resident; 148 dirty_pages += vm_region_info.pages_dirtied; 149 150 address += vm_region_size; 151 } 152 *res = resident_pages * GetPageSizeCached(); 153 *dirty = dirty_pages * GetPageSizeCached(); 154 } 155 156 void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns) { 157 uptr shadow_res, shadow_dirty; 158 uptr meta_res, meta_dirty; 159 uptr trace_res, trace_dirty; 160 RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res, &shadow_dirty); 161 RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res, &meta_dirty); 162 RegionMemUsage(TraceMemBeg(), TraceMemEnd(), &trace_res, &trace_dirty); 163 164 #if !SANITIZER_GO 165 uptr low_res, low_dirty; 166 uptr high_res, high_dirty; 167 uptr heap_res, heap_dirty; 168 RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res, &low_dirty); 169 RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res, &high_dirty); 170 RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res, &heap_dirty); 171 #else // !SANITIZER_GO 172 uptr app_res, app_dirty; 173 RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &app_res, &app_dirty); 174 #endif 175 176 StackDepotStats stacks = StackDepotGetStats(); 177 uptr nthread, nlive; 178 ctx->thread_registry.GetNumberOfThreads(&nthread, &nlive); 179 internal_snprintf( 180 buf, buf_size, 181 "shadow (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 182 "meta (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 183 "traces (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 184 # if !SANITIZER_GO 185 "low app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 186 "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 187 "heap (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 188 # else // !SANITIZER_GO 189 "app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 190 # endif 191 "stacks: %zd unique IDs, %zd kB allocated\n" 192 "threads: %zd total, %zd live\n" 193 "------------------------------\n", 194 ShadowBeg(), ShadowEnd(), shadow_res / 1024, shadow_dirty / 1024, 195 MetaShadowBeg(), MetaShadowEnd(), meta_res / 1024, meta_dirty / 1024, 196 TraceMemBeg(), TraceMemEnd(), trace_res / 1024, trace_dirty / 1024, 197 # if !SANITIZER_GO 198 LoAppMemBeg(), LoAppMemEnd(), low_res / 1024, low_dirty / 1024, 199 HiAppMemBeg(), HiAppMemEnd(), high_res / 1024, high_dirty / 1024, 200 HeapMemBeg(), HeapMemEnd(), heap_res / 1024, heap_dirty / 1024, 201 # else // !SANITIZER_GO 202 LoAppMemBeg(), LoAppMemEnd(), app_res / 1024, app_dirty / 1024, 203 # endif 204 stacks.n_uniq_ids, stacks.allocated / 1024, nthread, nlive); 205 } 206 207 # if !SANITIZER_GO 208 void InitializeShadowMemoryPlatform() { } 209 210 // On OS X, GCD worker threads are created without a call to pthread_create. We 211 // need to properly register these threads with ThreadCreate and ThreadStart. 212 // These threads don't have a parent thread, as they are created "spuriously". 213 // We're using a libpthread API that notifies us about a newly created thread. 214 // The `thread == pthread_self()` check indicates this is actually a worker 215 // thread. If it's just a regular thread, this hook is called on the parent 216 // thread. 217 typedef void (*pthread_introspection_hook_t)(unsigned int event, 218 pthread_t thread, void *addr, 219 size_t size); 220 extern "C" pthread_introspection_hook_t pthread_introspection_hook_install( 221 pthread_introspection_hook_t hook); 222 static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1; 223 static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3; 224 static pthread_introspection_hook_t prev_pthread_introspection_hook; 225 static void my_pthread_introspection_hook(unsigned int event, pthread_t thread, 226 void *addr, size_t size) { 227 if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) { 228 if (thread == pthread_self()) { 229 // The current thread is a newly created GCD worker thread. 230 ThreadState *thr = cur_thread(); 231 Processor *proc = ProcCreate(); 232 ProcWire(proc, thr); 233 ThreadState *parent_thread_state = nullptr; // No parent. 234 Tid tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true); 235 CHECK_NE(tid, kMainTid); 236 ThreadStart(thr, tid, GetTid(), ThreadType::Worker); 237 } 238 } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) { 239 CHECK_EQ(thread, pthread_self()); 240 ThreadState *thr = cur_thread(); 241 if (thr->tctx) { 242 DestroyThreadState(); 243 } 244 } 245 246 if (prev_pthread_introspection_hook != nullptr) 247 prev_pthread_introspection_hook(event, thread, addr, size); 248 } 249 #endif 250 251 void InitializePlatformEarly() { 252 # if !SANITIZER_GO && SANITIZER_IOS 253 uptr max_vm = GetMaxUserVirtualAddress() + 1; 254 if (max_vm != HiAppMemEnd()) { 255 Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n", 256 (void *)max_vm, (void *)HiAppMemEnd()); 257 Die(); 258 } 259 #endif 260 } 261 262 static uptr longjmp_xor_key = 0; 263 264 void InitializePlatform() { 265 DisableCoreDumperIfNecessary(); 266 #if !SANITIZER_GO 267 CheckAndProtect(); 268 269 InitializeThreadStateStorage(); 270 271 prev_pthread_introspection_hook = 272 pthread_introspection_hook_install(&my_pthread_introspection_hook); 273 #endif 274 275 if (GetMacosAlignedVersion() >= MacosVersion(10, 14)) { 276 // Libsystem currently uses a process-global key; this might change. 277 const unsigned kTLSLongjmpXorKeySlot = 0x7; 278 longjmp_xor_key = (uptr)pthread_getspecific(kTLSLongjmpXorKeySlot); 279 } 280 } 281 282 #ifdef __aarch64__ 283 # define LONG_JMP_SP_ENV_SLOT \ 284 ((GetMacosAlignedVersion() >= MacosVersion(10, 14)) ? 12 : 13) 285 #else 286 # define LONG_JMP_SP_ENV_SLOT 2 287 #endif 288 289 uptr ExtractLongJmpSp(uptr *env) { 290 uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT]; 291 uptr sp = mangled_sp ^ longjmp_xor_key; 292 sp = (uptr)ptrauth_auth_data((void *)sp, ptrauth_key_asdb, 293 ptrauth_string_discriminator("sp")); 294 return sp; 295 } 296 297 #if !SANITIZER_GO 298 extern "C" void __tsan_tls_initialization() {} 299 300 void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { 301 const uptr pc = StackTrace::GetNextInstructionPc( 302 reinterpret_cast<uptr>(__tsan_tls_initialization)); 303 // Unlike Linux, we only store a pointer to the ThreadState object in TLS; 304 // just mark the entire range as written to. 305 MemoryRangeImitateWrite(thr, pc, tls_addr, tls_size); 306 } 307 #endif 308 309 #if !SANITIZER_GO 310 // Note: this function runs with async signals enabled, 311 // so it must not touch any tsan state. 312 int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), 313 void (*cleanup)(void *arg), void *arg) { 314 // pthread_cleanup_push/pop are hardcore macros mess. 315 // We can't intercept nor call them w/o including pthread.h. 316 int res; 317 pthread_cleanup_push(cleanup, arg); 318 res = fn(arg); 319 pthread_cleanup_pop(0); 320 return res; 321 } 322 #endif 323 324 } // namespace __tsan 325 326 #endif // SANITIZER_MAC 327