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#include "sanitizer_common/sanitizer_platform.h" 16#if SANITIZER_MAC 17 18#include "asan_interceptors.h" 19#include "asan_internal.h" 20#include "asan_mapping.h" 21#include "asan_stack.h" 22#include "asan_thread.h" 23#include "sanitizer_common/sanitizer_atomic.h" 24#include "sanitizer_common/sanitizer_libc.h" 25#include "sanitizer_common/sanitizer_mac.h" 26 27#include <fcntl.h> 28#include <libkern/OSAtomic.h> 29#include <mach-o/dyld.h> 30#include <mach-o/loader.h> 31#include <pthread.h> 32#include <stdlib.h> // for free() 33#include <sys/mman.h> 34#include <sys/resource.h> 35#include <sys/sysctl.h> 36#include <sys/ucontext.h> 37#include <unistd.h> 38 39namespace __asan { 40 41void InitializePlatformInterceptors() {} 42 43bool PlatformHasDifferentMemcpyAndMemmove() { 44 // On OS X 10.7 memcpy() and memmove() are both resolved 45 // into memmove$VARIANT$sse42. 46 // See also https://github.com/google/sanitizers/issues/34. 47 // TODO(glider): need to check dynamically that memcpy() and memmove() are 48 // actually the same function. 49 return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; 50} 51 52// No-op. Mac does not support static linkage anyway. 53void *AsanDoesNotSupportStaticLinkage() { 54 return 0; 55} 56 57// No-op. Mac does not support static linkage anyway. 58void AsanCheckDynamicRTPrereqs() {} 59 60// No-op. Mac does not support static linkage anyway. 61void AsanCheckIncompatibleRT() {} 62 63void ReadContextStack(void *context, uptr *stack, uptr *ssize) { 64 UNIMPLEMENTED(); 65} 66 67// Support for the following functions from libdispatch on Mac OS: 68// dispatch_async_f() 69// dispatch_async() 70// dispatch_sync_f() 71// dispatch_sync() 72// dispatch_after_f() 73// dispatch_after() 74// dispatch_group_async_f() 75// dispatch_group_async() 76// TODO(glider): libdispatch API contains other functions that we don't support 77// yet. 78// 79// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 80// they can cause jobs to run on a thread different from the current one. 81// TODO(glider): if so, we need a test for this (otherwise we should remove 82// them). 83// 84// The following functions use dispatch_barrier_async_f() (which isn't a library 85// function but is exported) and are thus supported: 86// dispatch_source_set_cancel_handler_f() 87// dispatch_source_set_cancel_handler() 88// dispatch_source_set_event_handler_f() 89// dispatch_source_set_event_handler() 90// 91// The reference manual for Grand Central Dispatch is available at 92// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 93// The implementation details are at 94// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 95 96typedef void* dispatch_group_t; 97typedef void* dispatch_queue_t; 98typedef void* dispatch_source_t; 99typedef u64 dispatch_time_t; 100typedef void (*dispatch_function_t)(void *block); 101typedef void* (*worker_t)(void *block); 102 103// A wrapper for the ObjC blocks used to support libdispatch. 104typedef struct { 105 void *block; 106 dispatch_function_t func; 107 u32 parent_tid; 108} asan_block_context_t; 109 110ALWAYS_INLINE 111void asan_register_worker_thread(int parent_tid, StackTrace *stack) { 112 AsanThread *t = GetCurrentThread(); 113 if (!t) { 114 t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr, 115 parent_tid, stack, /* detached */ true); 116 t->Init(); 117 asanThreadRegistry().StartThread(t->tid(), 0, 0); 118 SetCurrentThread(t); 119 } 120} 121 122// For use by only those functions that allocated the context via 123// alloc_asan_context(). 124extern "C" 125void asan_dispatch_call_block_and_release(void *block) { 126 GET_STACK_TRACE_THREAD; 127 asan_block_context_t *context = (asan_block_context_t*)block; 128 VReport(2, 129 "asan_dispatch_call_block_and_release(): " 130 "context: %p, pthread_self: %p\n", 131 block, pthread_self()); 132 asan_register_worker_thread(context->parent_tid, &stack); 133 // Call the original dispatcher for the block. 134 context->func(context->block); 135 asan_free(context, &stack, FROM_MALLOC); 136} 137 138} // namespace __asan 139 140using namespace __asan; // NOLINT 141 142// Wrap |ctxt| and |func| into an asan_block_context_t. 143// The caller retains control of the allocated context. 144extern "C" 145asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, 146 BufferedStackTrace *stack) { 147 asan_block_context_t *asan_ctxt = 148 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); 149 asan_ctxt->block = ctxt; 150 asan_ctxt->func = func; 151 asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); 152 return asan_ctxt; 153} 154 155// Define interceptor for dispatch_*_f function with the three most common 156// parameters: dispatch_queue_t, context, dispatch_function_t. 157#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ 158 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ 159 dispatch_function_t func) { \ 160 GET_STACK_TRACE_THREAD; \ 161 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ 162 if (Verbosity() >= 2) { \ 163 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ 164 asan_ctxt, pthread_self()); \ 165 PRINT_CURRENT_STACK(); \ 166 } \ 167 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ 168 asan_dispatch_call_block_and_release); \ 169 } 170 171INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) 172INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) 173INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) 174 175INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, 176 dispatch_queue_t dq, void *ctxt, 177 dispatch_function_t func) { 178 GET_STACK_TRACE_THREAD; 179 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 180 if (Verbosity() >= 2) { 181 Report("dispatch_after_f: %p\n", asan_ctxt); 182 PRINT_CURRENT_STACK(); 183 } 184 return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, 185 asan_dispatch_call_block_and_release); 186} 187 188INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, 189 dispatch_queue_t dq, void *ctxt, 190 dispatch_function_t func) { 191 GET_STACK_TRACE_THREAD; 192 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 193 if (Verbosity() >= 2) { 194 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", 195 asan_ctxt, pthread_self()); 196 PRINT_CURRENT_STACK(); 197 } 198 REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, 199 asan_dispatch_call_block_and_release); 200} 201 202#if !defined(MISSING_BLOCKS_SUPPORT) 203extern "C" { 204void dispatch_async(dispatch_queue_t dq, void(^work)(void)); 205void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, 206 void(^work)(void)); 207void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, 208 void(^work)(void)); 209void dispatch_source_set_cancel_handler(dispatch_source_t ds, 210 void(^work)(void)); 211void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); 212} 213 214#define GET_ASAN_BLOCK(work) \ 215 void (^asan_block)(void); \ 216 int parent_tid = GetCurrentTidOrInvalid(); \ 217 asan_block = ^(void) { \ 218 GET_STACK_TRACE_THREAD; \ 219 asan_register_worker_thread(parent_tid, &stack); \ 220 work(); \ 221 } 222 223INTERCEPTOR(void, dispatch_async, 224 dispatch_queue_t dq, void(^work)(void)) { 225 ENABLE_FRAME_POINTER; 226 GET_ASAN_BLOCK(work); 227 REAL(dispatch_async)(dq, asan_block); 228} 229 230INTERCEPTOR(void, dispatch_group_async, 231 dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { 232 ENABLE_FRAME_POINTER; 233 GET_ASAN_BLOCK(work); 234 REAL(dispatch_group_async)(dg, dq, asan_block); 235} 236 237INTERCEPTOR(void, dispatch_after, 238 dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { 239 ENABLE_FRAME_POINTER; 240 GET_ASAN_BLOCK(work); 241 REAL(dispatch_after)(when, queue, asan_block); 242} 243 244INTERCEPTOR(void, dispatch_source_set_cancel_handler, 245 dispatch_source_t ds, void(^work)(void)) { 246 if (!work) { 247 REAL(dispatch_source_set_cancel_handler)(ds, work); 248 return; 249 } 250 ENABLE_FRAME_POINTER; 251 GET_ASAN_BLOCK(work); 252 REAL(dispatch_source_set_cancel_handler)(ds, asan_block); 253} 254 255INTERCEPTOR(void, dispatch_source_set_event_handler, 256 dispatch_source_t ds, void(^work)(void)) { 257 ENABLE_FRAME_POINTER; 258 GET_ASAN_BLOCK(work); 259 REAL(dispatch_source_set_event_handler)(ds, asan_block); 260} 261#endif 262 263#endif // SANITIZER_MAC 264