asan_mac.cc revision 2d8b3bdb112ebb8ed3f15ee41d4cebcd683b41b0
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_stack.h" 21#include "asan_thread.h" 22#include "asan_thread_registry.h" 23 24#include <sys/mman.h> 25#include <unistd.h> 26 27namespace __asan { 28 29extern dispatch_async_f_f real_dispatch_async_f; 30extern dispatch_sync_f_f real_dispatch_sync_f; 31extern dispatch_after_f_f real_dispatch_after_f; 32extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f; 33extern dispatch_group_async_f_f real_dispatch_group_async_f; 34extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np; 35 36// No-op. Mac does not support static linkage anyway. 37void *AsanDoesNotSupportStaticLinkage() { 38 return NULL; 39} 40 41void *asan_mmap(void *addr, size_t length, int prot, int flags, 42 int fd, uint64_t offset) { 43 return mmap(addr, length, prot, flags, fd, offset); 44} 45 46ssize_t asan_write(int fd, const void *buf, size_t count) { 47 return write(fd, buf, count); 48} 49 50// Support for the following functions from libdispatch on Mac OS: 51// dispatch_async_f() 52// dispatch_async() 53// dispatch_sync_f() 54// dispatch_sync() 55// dispatch_after_f() 56// dispatch_after() 57// dispatch_group_async_f() 58// dispatch_group_async() 59// TODO(glider): libdispatch API contains other functions that we don't support 60// yet. 61// 62// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are 63// they can cause jobs to run on a thread different from the current one. 64// TODO(glider): if so, we need a test for this (otherwise we should remove 65// them). 66// 67// The following functions use dispatch_barrier_async_f() (which isn't a library 68// function but is exported) and are thus supported: 69// dispatch_source_set_cancel_handler_f() 70// dispatch_source_set_cancel_handler() 71// dispatch_source_set_event_handler_f() 72// dispatch_source_set_event_handler() 73// 74// The reference manual for Grand Central Dispatch is available at 75// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html 76// The implementation details are at 77// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c 78 79extern "C" 80void asan_dispatch_call_block_and_release(void *block) { 81 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 82 asan_block_context_t *context = (asan_block_context_t*)block; 83 if (FLAG_v >= 2) { 84 Report("asan_dispatch_call_block_and_release(): " 85 "context: %p, pthread_self: %p\n", 86 block, pthread_self()); 87 } 88 AsanThread *t = asanThreadRegistry().GetCurrent(); 89 if (t) { 90 // We've already executed a job on this worker thread. Let's reuse the 91 // AsanThread object. 92 if (t != asanThreadRegistry().GetMain()) { 93 // Flush the statistics and update the current thread's tid. 94 asanThreadRegistry().UnregisterThread(t); 95 asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack); 96 } 97 // Otherwise the worker is being executed on the main thread -- we are 98 // draining the dispatch queue. 99 // TODO(glider): any checks for that? 100 } else { 101 // It's incorrect to assert that the current thread is not dying: at least 102 // the callbacks from dispatch_sync() are sometimes called after the TSD is 103 // destroyed. 104 t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack); 105 new(t) AsanThread(context->parent_tid, 106 /*start_routine*/NULL, /*arg*/NULL, &stack); 107 asanThreadRegistry().SetCurrent(t); 108 } 109 // Call the original dispatcher for the block. 110 context->func(context->block); 111 asan_free(context, &stack); 112} 113 114} // namespace __asan 115 116using namespace __asan; // NOLINT 117 118// Wrap |ctxt| and |func| into an asan_block_context_t. 119// The caller retains control of the allocated context. 120extern "C" 121asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, 122 AsanStackTrace *stack) { 123 asan_block_context_t *asan_ctxt = 124 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); 125 asan_ctxt->block = ctxt; 126 asan_ctxt->func = func; 127 AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); 128 if (FLAG_debug) { 129 // Sometimes at Chromium teardown this assertion is violated: 130 // -- a task is created via dispatch_async() on the "CFMachPort" 131 // thread while doing _dispatch_queue_drain(); 132 // -- a task is created via dispatch_async_f() on the 133 // "com.apple.root.default-overcommit-priority" thread while doing 134 // _dispatch_dispose(). 135 // TODO(glider): find out what's going on. 136 CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying()); 137 } 138 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); 139 return asan_ctxt; 140} 141 142// TODO(glider): can we reduce code duplication by introducing a macro? 143extern "C" 144int WRAP(dispatch_async_f)(dispatch_queue_t dq, 145 void *ctxt, 146 dispatch_function_t func) { 147 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 148 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 149 if (FLAG_v >= 2) { 150 Report("dispatch_async_f(): context: %p, pthread_self: %p\n", 151 asan_ctxt, pthread_self()); 152 PRINT_CURRENT_STACK(); 153 } 154 return real_dispatch_async_f(dq, (void*)asan_ctxt, 155 asan_dispatch_call_block_and_release); 156} 157 158extern "C" 159int WRAP(dispatch_sync_f)(dispatch_queue_t dq, 160 void *ctxt, 161 dispatch_function_t func) { 162 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 163 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 164 if (FLAG_v >= 2) { 165 Report("dispatch_sync_f(): context: %p, pthread_self: %p\n", 166 asan_ctxt, pthread_self()); 167 PRINT_CURRENT_STACK(); 168 } 169 return real_dispatch_sync_f(dq, (void*)asan_ctxt, 170 asan_dispatch_call_block_and_release); 171} 172 173extern "C" 174int WRAP(dispatch_after_f)(dispatch_time_t when, 175 dispatch_queue_t dq, 176 void *ctxt, 177 dispatch_function_t func) { 178 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 179 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 180 if (FLAG_v >= 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 188extern "C" 189void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq, 190 void *ctxt, dispatch_function_t func) { 191 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 192 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 193 if (FLAG_v >= 2) { 194 Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n", 195 asan_ctxt, pthread_self()); 196 PRINT_CURRENT_STACK(); 197 } 198 real_dispatch_barrier_async_f(dq, (void*)asan_ctxt, 199 asan_dispatch_call_block_and_release); 200} 201 202extern "C" 203void WRAP(dispatch_group_async_f)(dispatch_group_t group, 204 dispatch_queue_t dq, 205 void *ctxt, dispatch_function_t func) { 206 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 207 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); 208 if (FLAG_v >= 2) { 209 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", 210 asan_ctxt, pthread_self()); 211 PRINT_CURRENT_STACK(); 212 } 213 real_dispatch_group_async_f(group, dq, (void*)asan_ctxt, 214 asan_dispatch_call_block_and_release); 215} 216 217// The following stuff has been extremely helpful while looking for the 218// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity 219// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to 220// find the points of worker thread creation (each of such threads may be used 221// to run several tasks, that's why this is not enough to support the whole 222// libdispatch API. 223extern "C" 224void *wrap_workitem_func(void *arg) { 225 if (FLAG_v >= 2) { 226 Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); 227 } 228 asan_block_context_t *ctxt = (asan_block_context_t*)arg; 229 worker_t fn = (worker_t)(ctxt->func); 230 void *result = fn(ctxt->block); 231 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 232 asan_free(arg, &stack); 233 return result; 234} 235 236extern "C" 237int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq, 238 void *(*workitem_func)(void *), void * workitem_arg, 239 pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { 240 GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); 241 asan_block_context_t *asan_ctxt = 242 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); 243 asan_ctxt->block = workitem_arg; 244 asan_ctxt->func = (dispatch_function_t)workitem_func; 245 asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); 246 if (FLAG_v >= 2) { 247 Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); 248 PRINT_CURRENT_STACK(); 249 } 250 return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt, 251 itemhandlep, gencountp); 252} 253 254#endif // __APPLE__ 255