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