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