1 //===-- asan_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 AddressSanitizer, an address sanity checker. 10 // 11 // Mac-specific details. 12 //===----------------------------------------------------------------------===// 13 14 #include "sanitizer_common/sanitizer_platform.h" 15 #if SANITIZER_APPLE 16 17 #include "asan_interceptors.h" 18 #include "asan_internal.h" 19 #include "asan_mapping.h" 20 #include "asan_stack.h" 21 #include "asan_thread.h" 22 #include "sanitizer_common/sanitizer_atomic.h" 23 #include "sanitizer_common/sanitizer_libc.h" 24 #include "sanitizer_common/sanitizer_mac.h" 25 26 #include <dlfcn.h> 27 #include <fcntl.h> 28 #include <libkern/OSAtomic.h> 29 #include <mach-o/dyld.h> 30 #include <mach-o/getsect.h> 31 #include <mach-o/loader.h> 32 #include <pthread.h> 33 #include <stdlib.h> // for free() 34 #include <sys/mman.h> 35 #include <sys/resource.h> 36 #include <sys/sysctl.h> 37 #include <sys/ucontext.h> 38 #include <unistd.h> 39 40 // from <crt_externs.h>, but we don't have that file on iOS 41 extern "C" { 42 extern char ***_NSGetArgv(void); 43 extern char ***_NSGetEnviron(void); 44 } 45 46 namespace __asan { 47 48 void InitializePlatformInterceptors() {} 49 void InitializePlatformExceptionHandlers() {} 50 bool IsSystemHeapAddress (uptr addr) { return false; } 51 52 uptr FindDynamicShadowStart() { 53 return MapDynamicShadow(MemToShadowSize(kHighMemEnd), ASAN_SHADOW_SCALE, 54 /*min_shadow_base_alignment*/ 0, kHighMemEnd, 55 GetMmapGranularity()); 56 } 57 58 // No-op. Mac does not support static linkage anyway. 59 void AsanCheckDynamicRTPrereqs() {} 60 61 // No-op. Mac does not support static linkage anyway. 62 void AsanCheckIncompatibleRT() {} 63 64 void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { 65 // Find the Mach-O header for the image containing the needle 66 Dl_info info; 67 int err = dladdr(needle, &info); 68 if (err == 0) return; 69 70 #if __LP64__ 71 const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase; 72 #else 73 const struct mach_header *mh = (struct mach_header *)info.dli_fbase; 74 #endif 75 76 // Look up the __asan_globals section in that image and register its globals 77 unsigned long size = 0; 78 __asan_global *globals = (__asan_global *)getsectiondata( 79 mh, 80 "__DATA", "__asan_globals", 81 &size); 82 83 if (!globals) return; 84 if (size % sizeof(__asan_global) != 0) return; 85 op(globals, size / sizeof(__asan_global)); 86 } 87 88 void FlushUnneededASanShadowMemory(uptr p, uptr size) { 89 // Since asan's mapping is compacting, the shadow chunk may be 90 // not page-aligned, so we only flush the page-aligned portion. 91 ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size)); 92 } 93 94 // Support for the following functions from libdispatch on Mac OS: 95 // dispatch_async_f() 96 // dispatch_async() 97 // dispatch_sync_f() 98 // dispatch_sync() 99 // dispatch_after_f() 100 // dispatch_after() 101 // dispatch_group_async_f() 102 // dispatch_group_async() 103 // TODO(glider): libdispatch API contains other functions that we don't support 104 // yet. 105 // 106 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 107 // they can cause jobs to run on a thread different from the current one. 108 // TODO(glider): if so, we need a test for this (otherwise we should remove 109 // them). 110 // 111 // The following functions use dispatch_barrier_async_f() (which isn't a library 112 // function but is exported) and are thus supported: 113 // dispatch_source_set_cancel_handler_f() 114 // dispatch_source_set_cancel_handler() 115 // dispatch_source_set_event_handler_f() 116 // dispatch_source_set_event_handler() 117 // 118 // The reference manual for Grand Central Dispatch is available at 119 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 120 // The implementation details are at 121 // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 122 123 typedef void* dispatch_group_t; 124 typedef void* dispatch_queue_t; 125 typedef void* dispatch_source_t; 126 typedef u64 dispatch_time_t; 127 typedef void (*dispatch_function_t)(void *block); 128 typedef void* (*worker_t)(void *block); 129 typedef unsigned long dispatch_mach_reason; 130 typedef void *dispatch_mach_msg_t; 131 typedef int mach_error_t; 132 typedef void *dispatch_mach_t; 133 134 typedef void (*dispatch_mach_handler_function_t)(void *context, 135 dispatch_mach_reason reason, 136 dispatch_mach_msg_t message, 137 mach_error_t error); 138 # if !defined(MISSING_BLOCKS_SUPPORT) 139 typedef void (^dispatch_mach_handler_t)(dispatch_mach_reason reason, 140 dispatch_mach_msg_t message, 141 mach_error_t error); 142 # endif 143 144 // A wrapper for the ObjC blocks used to support libdispatch. 145 typedef struct { 146 void *block; 147 dispatch_function_t func; 148 u32 parent_tid; 149 } asan_block_context_t; 150 151 ALWAYS_INLINE 152 void asan_register_worker_thread(int parent_tid, StackTrace *stack) { 153 AsanThread *t = GetCurrentThread(); 154 if (!t) { 155 t = AsanThread::Create(parent_tid, stack, /* detached */ true); 156 t->Init(); 157 asanThreadRegistry().StartThread(t->tid(), GetTid(), ThreadType::Worker, 158 nullptr); 159 SetCurrentThread(t); 160 } 161 } 162 163 // For use by only those functions that allocated the context via 164 // alloc_asan_context(). 165 extern "C" 166 void asan_dispatch_call_block_and_release(void *block) { 167 GET_STACK_TRACE_THREAD; 168 asan_block_context_t *context = (asan_block_context_t*)block; 169 VReport(2, 170 "asan_dispatch_call_block_and_release(): " 171 "context: %p, pthread_self: %p\n", 172 block, (void*)pthread_self()); 173 asan_register_worker_thread(context->parent_tid, &stack); 174 // Call the original dispatcher for the block. 175 context->func(context->block); 176 asan_free(context, &stack, FROM_MALLOC); 177 } 178 179 } // namespace __asan 180 181 using namespace __asan; 182 183 // Wrap |ctxt| and |func| into an asan_block_context_t. 184 // The caller retains control of the allocated context. 185 extern "C" 186 asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, 187 BufferedStackTrace *stack) { 188 asan_block_context_t *asan_ctxt = 189 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); 190 asan_ctxt->block = ctxt; 191 asan_ctxt->func = func; 192 asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); 193 return asan_ctxt; 194 } 195 196 // Define interceptor for dispatch_*_f function with the three most common 197 // parameters: dispatch_queue_t, context, dispatch_function_t. 198 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ 199 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ 200 dispatch_function_t func) { \ 201 GET_STACK_TRACE_THREAD; \ 202 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ 203 if (Verbosity() >= 2) { \ 204 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ 205 (void*)asan_ctxt, (void*)pthread_self()); \ 206 PRINT_CURRENT_STACK(); \ 207 } \ 208 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ 209 asan_dispatch_call_block_and_release); \ 210 } 211 212 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) 213 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) 214 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) 215 216 INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, 217 dispatch_queue_t dq, void *ctxt, 218 dispatch_function_t func) { 219 GET_STACK_TRACE_THREAD; 220 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 221 if (Verbosity() >= 2) { 222 Report("dispatch_after_f: %p\n", (void*)asan_ctxt); 223 PRINT_CURRENT_STACK(); 224 } 225 return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, 226 asan_dispatch_call_block_and_release); 227 } 228 229 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, 230 dispatch_queue_t dq, void *ctxt, 231 dispatch_function_t func) { 232 GET_STACK_TRACE_THREAD; 233 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 234 if (Verbosity() >= 2) { 235 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", 236 (void*)asan_ctxt, (void*)pthread_self()); 237 PRINT_CURRENT_STACK(); 238 } 239 REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, 240 asan_dispatch_call_block_and_release); 241 } 242 243 #if !defined(MISSING_BLOCKS_SUPPORT) 244 extern "C" { 245 void dispatch_async(dispatch_queue_t dq, void(^work)(void)); 246 void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, 247 void(^work)(void)); 248 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, 249 void(^work)(void)); 250 void dispatch_source_set_cancel_handler(dispatch_source_t ds, 251 void(^work)(void)); 252 void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); 253 dispatch_mach_t dispatch_mach_create(const char *label, dispatch_queue_t queue, 254 dispatch_mach_handler_t handler); 255 } 256 257 #define GET_ASAN_BLOCK(work) \ 258 void (^asan_block)(void); \ 259 int parent_tid = GetCurrentTidOrInvalid(); \ 260 asan_block = ^(void) { \ 261 GET_STACK_TRACE_THREAD; \ 262 asan_register_worker_thread(parent_tid, &stack); \ 263 work(); \ 264 } 265 266 INTERCEPTOR(void, dispatch_async, 267 dispatch_queue_t dq, void(^work)(void)) { 268 ENABLE_FRAME_POINTER; 269 GET_ASAN_BLOCK(work); 270 REAL(dispatch_async)(dq, asan_block); 271 } 272 273 INTERCEPTOR(void, dispatch_group_async, 274 dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { 275 ENABLE_FRAME_POINTER; 276 GET_ASAN_BLOCK(work); 277 REAL(dispatch_group_async)(dg, dq, asan_block); 278 } 279 280 INTERCEPTOR(void, dispatch_after, 281 dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { 282 ENABLE_FRAME_POINTER; 283 GET_ASAN_BLOCK(work); 284 REAL(dispatch_after)(when, queue, asan_block); 285 } 286 287 INTERCEPTOR(void, dispatch_source_set_cancel_handler, 288 dispatch_source_t ds, void(^work)(void)) { 289 if (!work) { 290 REAL(dispatch_source_set_cancel_handler)(ds, work); 291 return; 292 } 293 ENABLE_FRAME_POINTER; 294 GET_ASAN_BLOCK(work); 295 REAL(dispatch_source_set_cancel_handler)(ds, asan_block); 296 } 297 298 INTERCEPTOR(void, dispatch_source_set_event_handler, 299 dispatch_source_t ds, void(^work)(void)) { 300 ENABLE_FRAME_POINTER; 301 GET_ASAN_BLOCK(work); 302 REAL(dispatch_source_set_event_handler)(ds, asan_block); 303 } 304 305 INTERCEPTOR(void *, dispatch_mach_create, const char *label, 306 dispatch_queue_t dq, dispatch_mach_handler_t handler) { 307 int parent_tid = GetCurrentTidOrInvalid(); 308 return REAL(dispatch_mach_create)( 309 label, dq, 310 ^(dispatch_mach_reason reason, dispatch_mach_msg_t message, 311 mach_error_t error) { 312 GET_STACK_TRACE_THREAD; 313 asan_register_worker_thread(parent_tid, &stack); 314 handler(reason, message, error); 315 }); 316 } 317 318 INTERCEPTOR(void *, dispatch_mach_create_f, const char *label, 319 dispatch_queue_t dq, void *ctxt, 320 dispatch_mach_handler_function_t handler) { 321 int parent_tid = GetCurrentTidOrInvalid(); 322 return REAL(dispatch_mach_create)( 323 label, dq, 324 ^(dispatch_mach_reason reason, dispatch_mach_msg_t message, 325 mach_error_t error) { 326 GET_STACK_TRACE_THREAD; 327 asan_register_worker_thread(parent_tid, &stack); 328 handler(ctxt, reason, message, error); 329 }); 330 } 331 332 #endif 333 334 #endif // SANITIZER_APPLE 335