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 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");
ThreadStateDestructor(void * thr)64 static void ThreadStateDestructor(void *thr) {
65 int res = pthread_setspecific(thread_state_key, thr);
66 CHECK_EQ(res, 0);
67 }
68
InitializeThreadStateStorage()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
cur_thread()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
set_cur_thread(ThreadState * thr)105 void set_cur_thread(ThreadState *thr) {
106 int res = pthread_setspecific(thread_state_key, thr);
107 CHECK_EQ(res, 0);
108 }
109
cur_thread_finalize()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
FlushShadowMemory()129 void FlushShadowMemory() {
130 }
131
RegionMemUsage(uptr start,uptr end,uptr * res,uptr * dirty)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
WriteMemoryProfile(char * buf,uptr buf_size,u64 uptime_ns)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
InitializeShadowMemoryPlatform()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;
my_pthread_introspection_hook(unsigned int event,pthread_t thread,void * addr,size_t size)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
InitializePlatformEarly()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
InitializePlatform()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
ExtractLongJmpSp(uptr * env)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
__tsan_tls_initialization()298 extern "C" void __tsan_tls_initialization() {}
299
ImitateTlsWrite(ThreadState * thr,uptr tls_addr,uptr tls_size)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.
call_pthread_cancel_with_cleanup(int (* fn)(void * arg),void (* cleanup)(void * arg),void * arg)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_APPLE
327