╭ tsan_platform_mac.cpp ────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
.. builtins/ interception/ sanitizer_common/ ubsan/ LICENSE.TXT tsan_debugging.cpp tsan_defs.h tsan_dense_alloc.h tsan_dispatch_defs.h tsan_external.cpp tsan_fd.cpp tsan_fd.h tsan_flags.cpp tsan_flags.h tsan_flags.inc tsan_ignoreset.cpp tsan_ignoreset.h tsan_ilist.h tsan_interceptors.h tsan_interceptors_mac.cpp tsan_interceptors_mach_vm.cpp tsan_interceptors_memintrinsics.c tsan_interceptors_posix.cpp tsan_interface.cpp tsan_interface.h tsan_interface.inc tsan_interface_ann.cpp tsan_interface_ann.h tsan_interface_atomic.cpp tsan_interface_java.cpp tsan_interface_java.h tsan_malloc_mac.cpp tsan_md5.cpp tsan_mman.cpp tsan_mman.h tsan_mutexset.cpp tsan_mutexset.h tsan_new_delete.cpp tsan_platform.h tsan_platform_linux.cpp ╭─────────────────────────────────╮ │tsan_platform_mac.cpp │ ╰─────────────────────────────────╯ tsan_platform_posix.cpp tsan_platform_windows.cpp tsan_ppc_regs.h tsan_preinit.cpp tsan_report.cpp tsan_report.h tsan_rtl.cpp tsan_rtl.h tsan_rtl_aarch64.S tsan_rtl_access.cpp tsan_rtl_amd64.S tsan_rtl_loongarch64.S tsan_rtl_mips64.S tsan_rtl_mutex.cpp tsan_rtl_ppc64.S tsan_rtl_proc.cpp tsan_rtl_report.cpp tsan_rtl_riscv64.S tsan_rtl_s390x.S tsan_rtl_thread.cpp tsan_shadow.h tsan_stack_trace.cpp tsan_stack_trace.h tsan_suppressions.cpp tsan_suppressions.h tsan_symbolize.cpp tsan_symbolize.h tsan_sync.cpp tsan_sync.h tsan_trace.h tsan_vector_clock.cpp tsan_vector_clock.h
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_APPLE 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 alignas(SANITIZER_CACHE_LINE_SIZE) static char main_thread_state[sizeof( 50 ThreadState)]; 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 static void RegionMemUsage(uptr start, uptr end, uptr *res, uptr *dirty) { 130 vm_address_t address = start; 131 vm_address_t end_address = end; 132 uptr resident_pages = 0; 133 uptr dirty_pages = 0; 134 while (address < end_address) { 135 vm_size_t vm_region_size; 136 mach_msg_type_number_t count = VM_REGION_EXTENDED_INFO_COUNT; 137 vm_region_extended_info_data_t vm_region_info; 138 mach_port_t object_name; 139 kern_return_t ret = vm_region_64( 140 mach_task_self(), &address, &vm_region_size, VM_REGION_EXTENDED_INFO, 141 (vm_region_info_t)&vm_region_info, &count, &object_name); 142 if (ret != KERN_SUCCESS) break; 143 144 resident_pages += vm_region_info.pages_resident; 145 dirty_pages += vm_region_info.pages_dirtied; 146 147 address += vm_region_size; 148 } 149 *res = resident_pages * GetPageSizeCached(); 150 *dirty = dirty_pages * GetPageSizeCached(); 151 } 152 153 void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns) { 154 uptr shadow_res, shadow_dirty; 155 uptr meta_res, meta_dirty; 156 RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res, &shadow_dirty); 157 RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res, &meta_dirty); 158 159 # if !SANITIZER_GO 160 uptr low_res, low_dirty; 161 uptr high_res, high_dirty; 162 uptr heap_res, heap_dirty; 163 RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res, &low_dirty); 164 RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res, &high_dirty); 165 RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res, &heap_dirty); 166 #else // !SANITIZER_GO 167 uptr app_res, app_dirty; 168 RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &app_res, &app_dirty); 169 #endif 170 171 StackDepotStats stacks = StackDepotGetStats(); 172 uptr nthread, nlive; 173 ctx->thread_registry.GetNumberOfThreads(&nthread, &nlive); 174 internal_snprintf( 175 buf, buf_size, 176 "shadow (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 177 "meta (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 178 # if !SANITIZER_GO 179 "low app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 180 "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 181 "heap (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 182 # else // !SANITIZER_GO 183 "app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n" 184 # endif 185 "stacks: %zd unique IDs, %zd kB allocated\n" 186 "threads: %zd total, %zd live\n" 187 "------------------------------\n", 188 ShadowBeg(), ShadowEnd(), shadow_res / 1024, shadow_dirty / 1024, 189 MetaShadowBeg(), MetaShadowEnd(), meta_res / 1024, meta_dirty / 1024, 190 # if !SANITIZER_GO 191 LoAppMemBeg(), LoAppMemEnd(), low_res / 1024, low_dirty / 1024, 192 HiAppMemBeg(), HiAppMemEnd(), high_res / 1024, high_dirty / 1024, 193 HeapMemBeg(), HeapMemEnd(), heap_res / 1024, heap_dirty / 1024, 194 # else // !SANITIZER_GO 195 LoAppMemBeg(), LoAppMemEnd(), app_res / 1024, app_dirty / 1024, 196 # endif 197 stacks.n_uniq_ids, stacks.allocated / 1024, nthread, nlive); 198 } 199 200 # if !SANITIZER_GO 201 void InitializeShadowMemoryPlatform() { } 202 203 // Register GCD worker threads, which are created without an observable call to 204 // pthread_create(). 205 static void ThreadCreateCallback(uptr thread, bool gcd_worker) { 206 if (gcd_worker) { 207 ThreadState *thr = cur_thread(); 208 Processor *proc = ProcCreate(); 209 ProcWire(proc, thr); 210 ThreadState *parent_thread_state = nullptr; // No parent. 211 Tid tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true); 212 CHECK_NE(tid, kMainTid); 213 ThreadStart(thr, tid, GetTid(), ThreadType::Worker); 214 } 215 } 216 217 // Destroy thread state for *all* threads. 218 static void ThreadTerminateCallback(uptr thread) { 219 ThreadState *thr = cur_thread(); 220 if (thr->tctx) { 221 DestroyThreadState(); 222 } 223 } 224 #endif 225 226 void InitializePlatformEarly() { 227 # if !SANITIZER_GO && SANITIZER_IOS 228 uptr max_vm = GetMaxUserVirtualAddress() + 1; 229 if (max_vm < HiAppMemEnd()) { 230 Report( 231 "ThreadSanitizer: Unsupported virtual memory layout:\n\tVM address " 232 "limit = %p\n\tExpected %p.\n", 233 (void*)max_vm, (void*)HiAppMemEnd()); 234 Die(); 235 } 236 // In some configurations, the max_vm is expanded, but much of this space is 237 // already mapped. TSAN will not work in this configuration. 238 if (!MemoryRangeIsAvailable(HiAppMemEnd() - 1, HiAppMemEnd() - 1)) { 239 Report( 240 "ThreadSanitizer: Unsupported virtual memory layout: Address %p is " 241 "already mapped.\n", 242 (void*)(HiAppMemEnd() - 1)); 243 Die(); 244 } 245 #endif 246 } 247 248 static uptr longjmp_xor_key = 0; 249 250 void InitializePlatform() { 251 DisableCoreDumperIfNecessary(); 252 #if !SANITIZER_GO 253 if (!CheckAndProtect(true, true, true)) { 254 Printf("FATAL: ThreadSanitizer: found incompatible memory layout.\n"); 255 Die(); 256 } 257 258 InitializeThreadStateStorage(); 259 260 ThreadEventCallbacks callbacks = { 261 .create = ThreadCreateCallback, 262 .start = nullptr, 263 .terminate = ThreadTerminateCallback, 264 .destroy = nullptr, 265 }; 266 InstallPthreadIntrospectionHook(callbacks); 267 #endif 268 269 if (GetMacosAlignedVersion() >= MacosVersion(10, 14)) { 270 // Libsystem currently uses a process-global key; this might change. 271 const unsigned kTLSLongjmpXorKeySlot = 0x7; 272 longjmp_xor_key = (uptr)pthread_getspecific(kTLSLongjmpXorKeySlot); 273 } 274 } 275 276 #ifdef __aarch64__ 277 # define LONG_JMP_SP_ENV_SLOT \ 278 ((GetMacosAlignedVersion() >= MacosVersion(10, 14)) ? 12 : 13) 279 #else 280 # define LONG_JMP_SP_ENV_SLOT 2 281 #endif 282 283 uptr ExtractLongJmpSp(uptr *env) { 284 uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT]; 285 uptr sp = mangled_sp ^ longjmp_xor_key; 286 sp = (uptr)ptrauth_auth_data((void *)sp, ptrauth_key_asdb, 287 ptrauth_string_discriminator("sp")); 288 return sp; 289 } 290 291 #if !SANITIZER_GO 292 extern "C" void __tsan_tls_initialization() {} 293 294 void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { 295 const uptr pc = StackTrace::GetNextInstructionPc( 296 reinterpret_cast<uptr>(__tsan_tls_initialization)); 297 // Unlike Linux, we only store a pointer to the ThreadState object in TLS; 298 // just mark the entire range as written to. 299 MemoryRangeImitateWrite(thr, pc, tls_addr, tls_size); 300 } 301 #endif 302 303 #if !SANITIZER_GO 304 // Note: this function runs with async signals enabled, 305 // so it must not touch any tsan state. 306 int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), 307 void (*cleanup)(void *arg), void *arg) { 308 // pthread_cleanup_push/pop are hardcore macros mess. 309 // We can't intercept nor call them w/o including pthread.h. 310 int res; 311 pthread_cleanup_push(cleanup, arg); 312 res = fn(arg); 313 pthread_cleanup_pop(0); 314 return res; 315 } 316 #endif 317 318 } // namespace __tsan 319 320 #endif // SANITIZER_APPLE 321