asan_mac.cc revision 9cfa194cc62026fc7c6e82f7303eee8ad4d10cf4
1//===-- asan_mac.cc -------------------------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file is a part of AddressSanitizer, an address sanity checker. 11// 12// Mac-specific details. 13//===----------------------------------------------------------------------===// 14 15#ifdef __APPLE__ 16 17#include "asan_mac.h" 18 19#include "asan_internal.h" 20#include "asan_procmaps.h" 21#include "asan_stack.h" 22#include "asan_thread.h" 23#include "asan_thread_registry.h" 24 25#include <crt_externs.h> // for _NSGetEnviron 26#include <mach-o/dyld.h> 27#include <sys/mman.h> 28#include <sys/resource.h> 29#include <sys/ucontext.h> 30#include <pthread.h> 31#include <fcntl.h> 32#include <unistd.h> 33#include <libkern/OSAtomic.h> 34 35namespace __asan { 36 37void *island_allocator_pos = NULL; 38 39extern dispatch_async_f_f real_dispatch_async_f; 40extern dispatch_sync_f_f real_dispatch_sync_f; 41extern dispatch_after_f_f real_dispatch_after_f; 42extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f; 43extern dispatch_group_async_f_f real_dispatch_group_async_f; 44extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np; 45 46void GetPcSpBp(void *context, uintptr_t *pc, uintptr_t *sp, uintptr_t *bp) { 47 ucontext_t *ucontext = (ucontext_t*)context; 48# if __WORDSIZE == 64 49 *pc = ucontext->uc_mcontext->__ss.__rip; 50 *bp = ucontext->uc_mcontext->__ss.__rbp; 51 *sp = ucontext->uc_mcontext->__ss.__rsp; 52# else 53 *pc = ucontext->uc_mcontext->__ss.__eip; 54 *bp = ucontext->uc_mcontext->__ss.__ebp; 55 *sp = ucontext->uc_mcontext->__ss.__esp; 56# endif // __WORDSIZE 57} 58 59// No-op. Mac does not support static linkage anyway. 60void *AsanDoesNotSupportStaticLinkage() { 61 return NULL; 62} 63 64bool AsanInterceptsSignal(int signum) { 65 return (signum == SIGSEGV || signum == SIGBUS) && FLAG_handle_segv; 66} 67 68static void *asan_mmap(void *addr, size_t length, int prot, int flags, 69 int fd, uint64_t offset) { 70 return mmap(addr, length, prot, flags, fd, offset); 71} 72 73size_t AsanWrite(int fd, const void *buf, size_t count) { 74 return write(fd, buf, count); 75} 76 77void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) { 78 size = RoundUpTo(size, kPageSize); 79 void *res = asan_mmap(0, size, 80 PROT_READ | PROT_WRITE, 81 MAP_PRIVATE | MAP_ANON, -1, 0); 82 if (res == (void*)-1) { 83 OutOfMemoryMessageAndDie(mem_type, size); 84 } 85 return res; 86} 87 88void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) { 89 return asan_mmap((void*)fixed_addr, size, 90 PROT_READ | PROT_WRITE, 91 MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 92 0, 0); 93} 94 95void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) { 96 return asan_mmap((void*)fixed_addr, size, 97 PROT_READ | PROT_WRITE, 98 MAP_PRIVATE | MAP_ANON | MAP_FIXED, 99 0, 0); 100} 101 102void *AsanMprotect(uintptr_t fixed_addr, size_t size) { 103 return asan_mmap((void*)fixed_addr, size, 104 PROT_NONE, 105 MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 106 0, 0); 107} 108 109void AsanUnmapOrDie(void *addr, size_t size) { 110 if (!addr || !size) return; 111 int res = munmap(addr, size); 112 if (res != 0) { 113 Report("Failed to unmap\n"); 114 AsanDie(); 115 } 116} 117 118int AsanOpenReadonly(const char* filename) { 119 return open(filename, O_RDONLY); 120} 121 122const char *AsanGetEnv(const char *name) { 123 char ***env_ptr = _NSGetEnviron(); 124 CHECK(env_ptr); 125 char **environ = *env_ptr; 126 CHECK(environ); 127 size_t name_len = internal_strlen(name); 128 while (*environ != NULL) { 129 size_t len = internal_strlen(*environ); 130 if (len > name_len) { 131 const char *p = *environ; 132 if (!internal_memcmp(p, name, name_len) && 133 p[name_len] == '=') { // Match. 134 return *environ + name_len + 1; // String starting after =. 135 } 136 } 137 environ++; 138 } 139 return NULL; 140} 141 142size_t AsanRead(int fd, void *buf, size_t count) { 143 return read(fd, buf, count); 144} 145 146int AsanClose(int fd) { 147 return close(fd); 148} 149 150AsanProcMaps::AsanProcMaps() { 151 Reset(); 152} 153 154AsanProcMaps::~AsanProcMaps() { 155} 156 157void AsanProcMaps::Reset() { 158 // Count down from the top. 159 // TODO(glider): as per man 3 dyld, iterating over the headers with 160 // _dyld_image_count is thread-unsafe. 161 current_image_ = _dyld_image_count(); 162 current_load_cmd_ = -1; 163} 164 165// Similar code is used in Google Perftools, 166// http://code.google.com/p/google-perftools. 167template<uint32_t kMagic, uint32_t kLCSegment, 168 typename MachHeader, typename SegmentCommand> 169static bool NextExtMachHelper(const mach_header* hdr, 170 int current_image, int current_load_cmd, 171 uintptr_t *start, uintptr_t *end, 172 uintptr_t *offset, 173 char filename[], size_t filename_size) { 174 if (hdr->magic != kMagic) 175 return false; 176 const char* lc = (const char *)hdr + sizeof(MachHeader); 177 // TODO(csilvers): make this not-quadradic (increment and hold state) 178 for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd 179 lc += ((const load_command *)lc)->cmdsize; 180 if (((const load_command *)lc)->cmd == kLCSegment) { 181 const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image); 182 const SegmentCommand* sc = (const SegmentCommand *)lc; 183 if (start) *start = sc->vmaddr + dlloff; 184 if (end) *end = sc->vmaddr + sc->vmsize + dlloff; 185 if (offset) *offset = sc->fileoff; 186 if (filename) { 187 real_strncpy(filename, _dyld_get_image_name(current_image), 188 filename_size); 189 } 190 return true; 191 } 192 return false; 193} 194 195bool AsanProcMaps::Next(uintptr_t *start, uintptr_t *end, 196 uintptr_t *offset, char filename[], 197 size_t filename_size) { 198 // We return a separate entry for each segment in the DLL. (TODO(csilvers): 199 // can we do better?) A DLL ("image") has load-commands, some of which 200 // talk about segment boundaries. 201 // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912 202 for (; current_image_ >= 0; current_image_--) { 203 const mach_header* hdr = _dyld_get_image_header(current_image_); 204 if (!hdr) continue; 205 if (current_load_cmd_ < 0) // set up for this image 206 current_load_cmd_ = hdr->ncmds; // again, go from the top down 207 208 // We start with the next load command (we've already looked at this one). 209 for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) { 210#ifdef MH_MAGIC_64 211 if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64, 212 struct mach_header_64, struct segment_command_64>( 213 hdr, current_image_, current_load_cmd_, 214 start, end, offset, filename, filename_size)) { 215 return true; 216 } 217#endif 218 if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT, 219 struct mach_header, struct segment_command>( 220 hdr, current_image_, current_load_cmd_, 221 start, end, offset, filename, filename_size)) { 222 return true; 223 } 224 } 225 // If we get here, no more load_cmd's in this image talk about 226 // segments. Go on to the next image. 227 } 228 // We didn't find anything. 229 return false; 230} 231 232bool AsanProcMaps::GetObjectNameAndOffset(uintptr_t addr, uintptr_t *offset, 233 char filename[], 234 size_t filename_size) { 235 return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); 236} 237 238void AsanThread::SetThreadStackTopAndBottom() { 239 size_t stacksize = pthread_get_stacksize_np(pthread_self()); 240 void *stackaddr = pthread_get_stackaddr_np(pthread_self()); 241 stack_top_ = (uintptr_t)stackaddr; 242 stack_bottom_ = stack_top_ - stacksize; 243 int local; 244 CHECK(AddrIsInStack((uintptr_t)&local)); 245} 246 247AsanLock::AsanLock(LinkerInitialized) { 248 // We assume that OS_SPINLOCK_INIT is zero 249} 250 251void AsanLock::Lock() { 252 CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); 253 CHECK(OS_SPINLOCK_INIT == 0); 254 CHECK(owner_ != (uintptr_t)pthread_self()); 255 OSSpinLockLock((OSSpinLock*)&opaque_storage_); 256 CHECK(!owner_); 257 owner_ = (uintptr_t)pthread_self(); 258} 259 260void AsanLock::Unlock() { 261 CHECK(owner_ == (uintptr_t)pthread_self()); 262 owner_ = 0; 263 OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); 264} 265 266void AsanStackTrace::GetStackTrace(size_t max_s, uintptr_t pc, uintptr_t bp) { 267 size = 0; 268 trace[0] = pc; 269 if ((max_s) > 1) { 270 max_size = max_s; 271 FastUnwindStack(pc, bp); 272 } 273} 274 275// The range of pages to be used by __asan_mach_override_ptr for escape 276// islands. 277// TODO(glider): instead of mapping a fixed range we must find a range of 278// unmapped pages in vmmap and take them. 279// These constants were chosen empirically and may not work if the shadow 280// memory layout changes. Unfortunately they do necessarily depend on 281// kHighMemBeg or kHighMemEnd. 282#if __WORDSIZE == 32 283#define kIslandEnd (0xffdf0000 - kPageSize) 284#define kIslandBeg (kIslandEnd - 256 * kPageSize) 285#else 286#define kIslandEnd (0x7fffffdf0000 - kPageSize) 287#define kIslandBeg (kIslandEnd - 256 * kPageSize) 288#endif 289 290extern "C" 291mach_error_t __asan_allocate_island(void **ptr, 292 size_t unused_size, 293 void *unused_hint) { 294 if (!island_allocator_pos) { 295 island_allocator_pos = 296 asan_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, 297 PROT_READ | PROT_WRITE | PROT_EXEC, 298 MAP_PRIVATE | MAP_ANON | MAP_FIXED, 299 -1, 0); 300 if (island_allocator_pos != (void*)kIslandBeg) { 301 return KERN_NO_SPACE; 302 } 303 }; 304 *ptr = island_allocator_pos; 305 island_allocator_pos = (char*)island_allocator_pos + kPageSize; 306 return err_none; 307} 308 309extern "C" 310mach_error_t __asan_deallocate_island(void *ptr) { 311 // Do nothing. 312 // TODO(glider): allow to free and reuse the island memory. 313 return err_none; 314} 315 316// Support for the following functions from libdispatch on Mac OS: 317// dispatch_async_f() 318// dispatch_async() 319// dispatch_sync_f() 320// dispatch_sync() 321// dispatch_after_f() 322// dispatch_after() 323// dispatch_group_async_f() 324// dispatch_group_async() 325// TODO(glider): libdispatch API contains other functions that we don't support 326// yet. 327// 328// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 329// they can cause jobs to run on a thread different from the current one. 330// TODO(glider): if so, we need a test for this (otherwise we should remove 331// them). 332// 333// The following functions use dispatch_barrier_async_f() (which isn't a library 334// function but is exported) and are thus supported: 335// dispatch_source_set_cancel_handler_f() 336// dispatch_source_set_cancel_handler() 337// dispatch_source_set_event_handler_f() 338// dispatch_source_set_event_handler() 339// 340// The reference manual for Grand Central Dispatch is available at 341// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 342// The implementation details are at 343// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 344 345extern "C" 346void asan_dispatch_call_block_and_release(void *block) { 347 GET_STACK_TRACE_HERE(kStackTraceMax); 348 asan_block_context_t *context = (asan_block_context_t*)block; 349 if (FLAG_v >= 2) { 350 Report("asan_dispatch_call_block_and_release(): " 351 "context: %p, pthread_self: %p\n", 352 block, pthread_self()); 353 } 354 AsanThread *t = asanThreadRegistry().GetCurrent(); 355 if (!t) { 356 t = AsanThread::Create(context->parent_tid, NULL, NULL, &stack); 357 asanThreadRegistry().RegisterThread(t); 358 t->Init(); 359 asanThreadRegistry().SetCurrent(t); 360 } 361 // Call the original dispatcher for the block. 362 context->func(context->block); 363 asan_free(context, &stack); 364} 365 366} // namespace __asan 367 368using namespace __asan; // NOLINT 369 370// Wrap |ctxt| and |func| into an asan_block_context_t. 371// The caller retains control of the allocated context. 372extern "C" 373asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, 374 AsanStackTrace *stack) { 375 asan_block_context_t *asan_ctxt = 376 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); 377 asan_ctxt->block = ctxt; 378 asan_ctxt->func = func; 379 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); 380 return asan_ctxt; 381} 382 383// TODO(glider): can we reduce code duplication by introducing a macro? 384extern "C" 385int WRAP(dispatch_async_f)(dispatch_queue_t dq, 386 void *ctxt, 387 dispatch_function_t func) { 388 GET_STACK_TRACE_HERE(kStackTraceMax); 389 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 390 if (FLAG_v >= 2) { 391 Report("dispatch_async_f(): context: %p, pthread_self: %p\n", 392 asan_ctxt, pthread_self()); 393 PRINT_CURRENT_STACK(); 394 } 395 return real_dispatch_async_f(dq, (void*)asan_ctxt, 396 asan_dispatch_call_block_and_release); 397} 398 399extern "C" 400int WRAP(dispatch_sync_f)(dispatch_queue_t dq, 401 void *ctxt, 402 dispatch_function_t func) { 403 GET_STACK_TRACE_HERE(kStackTraceMax); 404 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 405 if (FLAG_v >= 2) { 406 Report("dispatch_sync_f(): context: %p, pthread_self: %p\n", 407 asan_ctxt, pthread_self()); 408 PRINT_CURRENT_STACK(); 409 } 410 return real_dispatch_sync_f(dq, (void*)asan_ctxt, 411 asan_dispatch_call_block_and_release); 412} 413 414extern "C" 415int WRAP(dispatch_after_f)(dispatch_time_t when, 416 dispatch_queue_t dq, 417 void *ctxt, 418 dispatch_function_t func) { 419 GET_STACK_TRACE_HERE(kStackTraceMax); 420 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 421 if (FLAG_v >= 2) { 422 Report("dispatch_after_f: %p\n", asan_ctxt); 423 PRINT_CURRENT_STACK(); 424 } 425 return real_dispatch_after_f(when, dq, (void*)asan_ctxt, 426 asan_dispatch_call_block_and_release); 427} 428 429extern "C" 430void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq, 431 void *ctxt, dispatch_function_t func) { 432 GET_STACK_TRACE_HERE(kStackTraceMax); 433 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 434 if (FLAG_v >= 2) { 435 Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n", 436 asan_ctxt, pthread_self()); 437 PRINT_CURRENT_STACK(); 438 } 439 real_dispatch_barrier_async_f(dq, (void*)asan_ctxt, 440 asan_dispatch_call_block_and_release); 441} 442 443extern "C" 444void WRAP(dispatch_group_async_f)(dispatch_group_t group, 445 dispatch_queue_t dq, 446 void *ctxt, dispatch_function_t func) { 447 GET_STACK_TRACE_HERE(kStackTraceMax); 448 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 449 if (FLAG_v >= 2) { 450 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", 451 asan_ctxt, pthread_self()); 452 PRINT_CURRENT_STACK(); 453 } 454 real_dispatch_group_async_f(group, dq, (void*)asan_ctxt, 455 asan_dispatch_call_block_and_release); 456} 457 458// The following stuff has been extremely helpful while looking for the 459// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity 460// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to 461// find the points of worker thread creation (each of such threads may be used 462// to run several tasks, that's why this is not enough to support the whole 463// libdispatch API. 464extern "C" 465void *wrap_workitem_func(void *arg) { 466 if (FLAG_v >= 2) { 467 Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); 468 } 469 asan_block_context_t *ctxt = (asan_block_context_t*)arg; 470 worker_t fn = (worker_t)(ctxt->func); 471 void *result = fn(ctxt->block); 472 GET_STACK_TRACE_HERE(kStackTraceMax); 473 asan_free(arg, &stack); 474 return result; 475} 476 477extern "C" 478int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq, 479 void *(*workitem_func)(void *), void * workitem_arg, 480 pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { 481 GET_STACK_TRACE_HERE(kStackTraceMax); 482 asan_block_context_t *asan_ctxt = 483 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); 484 asan_ctxt->block = workitem_arg; 485 asan_ctxt->func = (dispatch_function_t)workitem_func; 486 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); 487 if (FLAG_v >= 2) { 488 Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); 489 PRINT_CURRENT_STACK(); 490 } 491 return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt, 492 itemhandlep, gencountp); 493} 494 495#endif // __APPLE__ 496