1 //===-- asan_mac.cc -------------------------------------------------------===// 2 // 3 // This file is distributed under the University of Illinois Open Source 4 // License. See LICENSE.TXT for details. 5 // 6 //===----------------------------------------------------------------------===// 7 // 8 // This file is a part of AddressSanitizer, an address sanity checker. 9 // 10 // Mac-specific details. 11 //===----------------------------------------------------------------------===// 12 13 #include "sanitizer_common/sanitizer_platform.h" 14 #if SANITIZER_MAC 15 16 #include "asan_interceptors.h" 17 #include "asan_internal.h" 18 #include "asan_mapping.h" 19 #include "asan_stack.h" 20 #include "asan_thread.h" 21 #include "sanitizer_common/sanitizer_atomic.h" 22 #include "sanitizer_common/sanitizer_libc.h" 23 #include "sanitizer_common/sanitizer_mac.h" 24 25 #include <crt_externs.h> // for _NSGetArgv 26 #include <dlfcn.h> // for dladdr() 27 #include <mach-o/dyld.h> 28 #include <mach-o/loader.h> 29 #include <sys/mman.h> 30 #include <sys/resource.h> 31 #include <sys/sysctl.h> 32 #include <sys/ucontext.h> 33 #include <fcntl.h> 34 #include <pthread.h> 35 #include <stdlib.h> // for free() 36 #include <unistd.h> 37 #include <libkern/OSAtomic.h> 38 39 namespace __asan { 40 41 void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { 42 ucontext_t *ucontext = (ucontext_t*)context; 43 # if SANITIZER_WORDSIZE == 64 44 *pc = ucontext->uc_mcontext->__ss.__rip; 45 *bp = ucontext->uc_mcontext->__ss.__rbp; 46 *sp = ucontext->uc_mcontext->__ss.__rsp; 47 # else 48 *pc = ucontext->uc_mcontext->__ss.__eip; 49 *bp = ucontext->uc_mcontext->__ss.__ebp; 50 *sp = ucontext->uc_mcontext->__ss.__esp; 51 # endif // SANITIZER_WORDSIZE 52 } 53 54 55 bool PlatformHasDifferentMemcpyAndMemmove() { 56 // On OS X 10.7 memcpy() and memmove() are both resolved 57 // into memmove$VARIANT$sse42. 58 // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. 59 // TODO(glider): need to check dynamically that memcpy() and memmove() are 60 // actually the same function. 61 return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; 62 } 63 64 extern "C" 65 void __asan_init(); 66 67 static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; 68 LowLevelAllocator allocator_for_env; 69 70 // Change the value of the env var |name|, leaking the original value. 71 // If |name_value| is NULL, the variable is deleted from the environment, 72 // otherwise the corresponding "NAME=value" string is replaced with 73 // |name_value|. 74 void LeakyResetEnv(const char *name, const char *name_value) { 75 char ***env_ptr = _NSGetEnviron(); 76 CHECK(env_ptr); 77 char **environ = *env_ptr; 78 CHECK(environ); 79 uptr name_len = internal_strlen(name); 80 while (*environ != 0) { 81 uptr len = internal_strlen(*environ); 82 if (len > name_len) { 83 const char *p = *environ; 84 if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { 85 // Match. 86 if (name_value) { 87 // Replace the old value with the new one. 88 *environ = const_cast<char*>(name_value); 89 } else { 90 // Shift the subsequent pointers back. 91 char **del = environ; 92 do { 93 del[0] = del[1]; 94 } while (*del++); 95 } 96 } 97 } 98 environ++; 99 } 100 } 101 102 bool DyldNeedsEnvVariable() { 103 // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if 104 // DYLD_INSERT_LIBRARIES is not set. 105 106 #if SANITIZER_IOSSIM 107 // GetMacosVersion will not work for the simulator, whose kernel version 108 // is tied to the host. Use a weak linking hack for the simulator. 109 // This API was introduced in the same version of the OS as the dyld 110 // optimization. 111 112 // Check for presence of a symbol that is available on OS X 10.11+, iOS 9.0+. 113 return (dlsym(RTLD_NEXT, "mach_memory_info") == nullptr); 114 #else 115 return (GetMacosVersion() <= MACOS_VERSION_YOSEMITE); 116 #endif 117 } 118 119 void MaybeReexec() { 120 if (!flags()->allow_reexec) return; 121 // Make sure the dynamic ASan runtime library is preloaded so that the 122 // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec 123 // ourselves. 124 Dl_info info; 125 CHECK(dladdr((void*)((uptr)__asan_init), &info)); 126 char *dyld_insert_libraries = 127 const_cast<char*>(GetEnv(kDyldInsertLibraries)); 128 uptr old_env_len = dyld_insert_libraries ? 129 internal_strlen(dyld_insert_libraries) : 0; 130 uptr fname_len = internal_strlen(info.dli_fname); 131 bool lib_is_in_env = 132 dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, info.dli_fname); 133 if (DyldNeedsEnvVariable() && !lib_is_in_env) { 134 // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime 135 // library. 136 char program_name[1024]; 137 uint32_t buf_size = sizeof(program_name); 138 _NSGetExecutablePath(program_name, &buf_size); 139 char *new_env = const_cast<char*>(info.dli_fname); 140 if (dyld_insert_libraries) { 141 // Append the runtime dylib name to the existing value of 142 // DYLD_INSERT_LIBRARIES. 143 new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); 144 internal_strncpy(new_env, dyld_insert_libraries, old_env_len); 145 new_env[old_env_len] = ':'; 146 // Copy fname_len and add a trailing zero. 147 internal_strncpy(new_env + old_env_len + 1, info.dli_fname, 148 fname_len + 1); 149 // Ok to use setenv() since the wrappers don't depend on the value of 150 // asan_inited. 151 setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); 152 } else { 153 // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. 154 setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); 155 } 156 VReport(1, "exec()-ing the program with\n"); 157 VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); 158 VReport(1, "to enable ASan wrappers.\n"); 159 VReport(1, "Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); 160 execv(program_name, *_NSGetArgv()); 161 } else { 162 163 if (!lib_is_in_env) 164 return; 165 166 // DYLD_INSERT_LIBRARIES is set and contains the runtime library. 167 if (old_env_len == fname_len) { 168 // It's just the runtime library name - fine to unset the variable. 169 LeakyResetEnv(kDyldInsertLibraries, NULL); 170 } else { 171 uptr env_name_len = internal_strlen(kDyldInsertLibraries); 172 // Allocate memory to hold the previous env var name, its value, the '=' 173 // sign and the '\0' char. 174 char *new_env = (char*)allocator_for_env.Allocate( 175 old_env_len + 2 + env_name_len); 176 CHECK(new_env); 177 internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); 178 internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); 179 new_env[env_name_len] = '='; 180 char *new_env_pos = new_env + env_name_len + 1; 181 182 // Iterate over colon-separated pieces of |dyld_insert_libraries|. 183 char *piece_start = dyld_insert_libraries; 184 char *piece_end = NULL; 185 char *old_env_end = dyld_insert_libraries + old_env_len; 186 do { 187 if (piece_start[0] == ':') piece_start++; 188 piece_end = REAL(strchr)(piece_start, ':'); 189 if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; 190 if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; 191 uptr piece_len = piece_end - piece_start; 192 193 // If the current piece isn't the runtime library name, 194 // append it to new_env. 195 if ((piece_len != fname_len) || 196 (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) { 197 if (new_env_pos != new_env + env_name_len + 1) { 198 new_env_pos[0] = ':'; 199 new_env_pos++; 200 } 201 internal_strncpy(new_env_pos, piece_start, piece_len); 202 } 203 // Move on to the next piece. 204 new_env_pos += piece_len; 205 piece_start = piece_end; 206 } while (piece_start < old_env_end); 207 208 // Can't use setenv() here, because it requires the allocator to be 209 // initialized. 210 // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in 211 // a separate function called after InitializeAllocator(). 212 LeakyResetEnv(kDyldInsertLibraries, new_env); 213 } 214 } 215 } 216 217 // No-op. Mac does not support static linkage anyway. 218 void *AsanDoesNotSupportStaticLinkage() { 219 return 0; 220 } 221 222 // No-op. Mac does not support static linkage anyway. 223 void AsanCheckDynamicRTPrereqs() {} 224 225 // No-op. Mac does not support static linkage anyway. 226 void AsanCheckIncompatibleRT() {} 227 228 bool AsanInterceptsSignal(int signum) { 229 return (signum == SIGSEGV || signum == SIGBUS) && 230 common_flags()->handle_segv; 231 } 232 233 void AsanPlatformThreadInit() { 234 } 235 236 void ReadContextStack(void *context, uptr *stack, uptr *ssize) { 237 UNIMPLEMENTED(); 238 } 239 240 // Support for the following functions from libdispatch on Mac OS: 241 // dispatch_async_f() 242 // dispatch_async() 243 // dispatch_sync_f() 244 // dispatch_sync() 245 // dispatch_after_f() 246 // dispatch_after() 247 // dispatch_group_async_f() 248 // dispatch_group_async() 249 // TODO(glider): libdispatch API contains other functions that we don't support 250 // yet. 251 // 252 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 253 // they can cause jobs to run on a thread different from the current one. 254 // TODO(glider): if so, we need a test for this (otherwise we should remove 255 // them). 256 // 257 // The following functions use dispatch_barrier_async_f() (which isn't a library 258 // function but is exported) and are thus supported: 259 // dispatch_source_set_cancel_handler_f() 260 // dispatch_source_set_cancel_handler() 261 // dispatch_source_set_event_handler_f() 262 // dispatch_source_set_event_handler() 263 // 264 // The reference manual for Grand Central Dispatch is available at 265 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 266 // The implementation details are at 267 // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 268 269 typedef void* dispatch_group_t; 270 typedef void* dispatch_queue_t; 271 typedef void* dispatch_source_t; 272 typedef u64 dispatch_time_t; 273 typedef void (*dispatch_function_t)(void *block); 274 typedef void* (*worker_t)(void *block); 275 276 // A wrapper for the ObjC blocks used to support libdispatch. 277 typedef struct { 278 void *block; 279 dispatch_function_t func; 280 u32 parent_tid; 281 } asan_block_context_t; 282 283 ALWAYS_INLINE 284 void asan_register_worker_thread(int parent_tid, StackTrace *stack) { 285 AsanThread *t = GetCurrentThread(); 286 if (!t) { 287 t = AsanThread::Create(0, 0); 288 CreateThreadContextArgs args = { t, stack }; 289 asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args); 290 t->Init(); 291 asanThreadRegistry().StartThread(t->tid(), 0, 0); 292 SetCurrentThread(t); 293 } 294 } 295 296 // For use by only those functions that allocated the context via 297 // alloc_asan_context(). 298 extern "C" 299 void asan_dispatch_call_block_and_release(void *block) { 300 GET_STACK_TRACE_THREAD; 301 asan_block_context_t *context = (asan_block_context_t*)block; 302 VReport(2, 303 "asan_dispatch_call_block_and_release(): " 304 "context: %p, pthread_self: %p\n", 305 block, pthread_self()); 306 asan_register_worker_thread(context->parent_tid, &stack); 307 // Call the original dispatcher for the block. 308 context->func(context->block); 309 asan_free(context, &stack, FROM_MALLOC); 310 } 311 312 } // namespace __asan 313 314 using namespace __asan; // NOLINT 315 316 // Wrap |ctxt| and |func| into an asan_block_context_t. 317 // The caller retains control of the allocated context. 318 extern "C" 319 asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, 320 BufferedStackTrace *stack) { 321 asan_block_context_t *asan_ctxt = 322 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); 323 asan_ctxt->block = ctxt; 324 asan_ctxt->func = func; 325 asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); 326 return asan_ctxt; 327 } 328 329 // Define interceptor for dispatch_*_f function with the three most common 330 // parameters: dispatch_queue_t, context, dispatch_function_t. 331 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ 332 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ 333 dispatch_function_t func) { \ 334 GET_STACK_TRACE_THREAD; \ 335 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ 336 if (common_flags()->verbosity >= 2) { \ 337 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ 338 asan_ctxt, pthread_self()); \ 339 PRINT_CURRENT_STACK(); \ 340 } \ 341 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ 342 asan_dispatch_call_block_and_release); \ 343 } 344 345 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) 346 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) 347 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) 348 349 INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, 350 dispatch_queue_t dq, void *ctxt, 351 dispatch_function_t func) { 352 GET_STACK_TRACE_THREAD; 353 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 354 if (common_flags()->verbosity >= 2) { 355 Report("dispatch_after_f: %p\n", asan_ctxt); 356 PRINT_CURRENT_STACK(); 357 } 358 return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, 359 asan_dispatch_call_block_and_release); 360 } 361 362 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, 363 dispatch_queue_t dq, void *ctxt, 364 dispatch_function_t func) { 365 GET_STACK_TRACE_THREAD; 366 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 367 if (common_flags()->verbosity >= 2) { 368 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", 369 asan_ctxt, pthread_self()); 370 PRINT_CURRENT_STACK(); 371 } 372 REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, 373 asan_dispatch_call_block_and_release); 374 } 375 376 #if !defined(MISSING_BLOCKS_SUPPORT) 377 extern "C" { 378 void dispatch_async(dispatch_queue_t dq, void(^work)(void)); 379 void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, 380 void(^work)(void)); 381 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, 382 void(^work)(void)); 383 void dispatch_source_set_cancel_handler(dispatch_source_t ds, 384 void(^work)(void)); 385 void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); 386 } 387 388 #define GET_ASAN_BLOCK(work) \ 389 void (^asan_block)(void); \ 390 int parent_tid = GetCurrentTidOrInvalid(); \ 391 asan_block = ^(void) { \ 392 GET_STACK_TRACE_THREAD; \ 393 asan_register_worker_thread(parent_tid, &stack); \ 394 work(); \ 395 } 396 397 // Forces the compiler to generate a frame pointer in the function. 398 #define ENABLE_FRAME_POINTER \ 399 do { \ 400 volatile uptr enable_fp; \ 401 enable_fp = GET_CURRENT_FRAME(); \ 402 } while (0) 403 404 INTERCEPTOR(void, dispatch_async, 405 dispatch_queue_t dq, void(^work)(void)) { 406 ENABLE_FRAME_POINTER; 407 GET_ASAN_BLOCK(work); 408 REAL(dispatch_async)(dq, asan_block); 409 } 410 411 INTERCEPTOR(void, dispatch_group_async, 412 dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { 413 ENABLE_FRAME_POINTER; 414 GET_ASAN_BLOCK(work); 415 REAL(dispatch_group_async)(dg, dq, asan_block); 416 } 417 418 INTERCEPTOR(void, dispatch_after, 419 dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { 420 ENABLE_FRAME_POINTER; 421 GET_ASAN_BLOCK(work); 422 REAL(dispatch_after)(when, queue, asan_block); 423 } 424 425 INTERCEPTOR(void, dispatch_source_set_cancel_handler, 426 dispatch_source_t ds, void(^work)(void)) { 427 ENABLE_FRAME_POINTER; 428 GET_ASAN_BLOCK(work); 429 REAL(dispatch_source_set_cancel_handler)(ds, asan_block); 430 } 431 432 INTERCEPTOR(void, dispatch_source_set_event_handler, 433 dispatch_source_t ds, void(^work)(void)) { 434 ENABLE_FRAME_POINTER; 435 GET_ASAN_BLOCK(work); 436 REAL(dispatch_source_set_event_handler)(ds, asan_block); 437 } 438 #endif 439 440 #endif // SANITIZER_MAC 441