1 //===-- asan_mac.cc -------------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of AddressSanitizer, an address sanity checker.
10 // Mac-specific details.
11 //===----------------------------------------------------------------------===//
15 #include "asan_interceptors.h"
16 #include "asan_internal.h"
18 #include "asan_mapping.h"
19 #include "asan_stack.h"
20 #include "asan_thread.h"
21 #include "asan_thread_registry.h"
22 #include "sanitizer_common/sanitizer_libc.h"
24 #include <crt_externs.h> // for _NSGetArgv
25 #include <dlfcn.h> // for dladdr()
26 #include <mach-o/dyld.h>
27 #include <mach-o/loader.h>
29 #include <sys/resource.h>
30 #include <sys/sysctl.h>
31 #include <sys/ucontext.h>
34 #include <stdlib.h> // for free()
36 #include <libkern/OSAtomic.h>
37 #include <CoreFoundation/CFString.h>
41 void GetPcSpBp(void *context
, uptr
*pc
, uptr
*sp
, uptr
*bp
) {
42 ucontext_t
*ucontext
= (ucontext_t
*)context
;
44 *pc
= ucontext
->uc_mcontext
->__ss
.__rip
;
45 *bp
= ucontext
->uc_mcontext
->__ss
.__rbp
;
46 *sp
= ucontext
->uc_mcontext
->__ss
.__rsp
;
48 *pc
= ucontext
->uc_mcontext
->__ss
.__eip
;
49 *bp
= ucontext
->uc_mcontext
->__ss
.__ebp
;
50 *sp
= ucontext
->uc_mcontext
->__ss
.__esp
;
54 int GetMacosVersion() {
55 int mib
[2] = { CTL_KERN
, KERN_OSRELEASE
};
57 uptr len
= 0, maxlen
= sizeof(version
) / sizeof(version
[0]);
58 for (uptr i
= 0; i
< maxlen
; i
++) version
[i
] = '\0';
59 // Get the version length.
60 CHECK(sysctl(mib
, 2, 0, &len
, 0, 0) != -1);
62 CHECK(sysctl(mib
, 2, version
, &len
, 0, 0) != -1);
64 case '9': return MACOS_VERSION_LEOPARD
;
67 case '0': return MACOS_VERSION_SNOW_LEOPARD
;
68 case '1': return MACOS_VERSION_LION
;
69 default: return MACOS_VERSION_UNKNOWN
;
72 default: return MACOS_VERSION_UNKNOWN
;
76 bool PlatformHasDifferentMemcpyAndMemmove() {
77 // On OS X 10.7 memcpy() and memmove() are both resolved
78 // into memmove$VARIANT$sse42.
79 // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
80 // TODO(glider): need to check dynamically that memcpy() and memmove() are
81 // actually the same function.
82 return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD
;
88 static const char kDyldInsertLibraries
[] = "DYLD_INSERT_LIBRARIES";
91 if (!flags()->allow_reexec
) return;
92 #if MAC_INTERPOSE_FUNCTIONS
93 // If the program is linked with the dynamic ASan runtime library, make sure
94 // the library is preloaded so that the wrappers work. If it is not, set
95 // DYLD_INSERT_LIBRARIES and re-exec ourselves.
97 CHECK(dladdr((void*)((uptr
)__asan_init
), &info
));
98 const char *dyld_insert_libraries
= GetEnv(kDyldInsertLibraries
);
99 if (!dyld_insert_libraries
||
100 !REAL(strstr
)(dyld_insert_libraries
, info
.dli_fname
)) {
101 // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
103 char program_name
[1024];
104 uint32_t buf_size
= sizeof(program_name
);
105 _NSGetExecutablePath(program_name
, &buf_size
);
106 // Ok to use setenv() since the wrappers don't depend on the value of
108 setenv(kDyldInsertLibraries
, info
.dli_fname
, /*overwrite*/0);
109 if (flags()->verbosity
>= 1) {
110 Report("exec()-ing the program with\n");
111 Report("%s=%s\n", kDyldInsertLibraries
, info
.dli_fname
);
112 Report("to enable ASan wrappers.\n");
113 Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
115 execv(program_name
, *_NSGetArgv());
117 #endif // MAC_INTERPOSE_FUNCTIONS
118 // If we're not using the dynamic runtime, do nothing.
121 // No-op. Mac does not support static linkage anyway.
122 void *AsanDoesNotSupportStaticLinkage() {
126 bool AsanInterceptsSignal(int signum
) {
127 return (signum
== SIGSEGV
|| signum
== SIGBUS
) && flags()->handle_segv
;
130 void AsanPlatformThreadInit() {
131 ReplaceCFAllocator();
134 AsanLock::AsanLock(LinkerInitialized
) {
135 // We assume that OS_SPINLOCK_INIT is zero
138 void AsanLock::Lock() {
139 CHECK(sizeof(OSSpinLock
) <= sizeof(opaque_storage_
));
140 CHECK(OS_SPINLOCK_INIT
== 0);
141 CHECK(owner_
!= (uptr
)pthread_self());
142 OSSpinLockLock((OSSpinLock
*)&opaque_storage_
);
144 owner_
= (uptr
)pthread_self();
147 void AsanLock::Unlock() {
148 CHECK(owner_
== (uptr
)pthread_self());
150 OSSpinLockUnlock((OSSpinLock
*)&opaque_storage_
);
153 void GetStackTrace(StackTrace
*stack
, uptr max_s
, uptr pc
, uptr bp
) {
155 stack
->trace
[0] = pc
;
157 stack
->max_size
= max_s
;
158 if (!asan_inited
) return;
159 if (AsanThread
*t
= asanThreadRegistry().GetCurrent())
160 stack
->FastUnwindStack(pc
, bp
, t
->stack_top(), t
->stack_bottom());
164 // The range of pages to be used for escape islands.
165 // TODO(glider): instead of mapping a fixed range we must find a range of
166 // unmapped pages in vmmap and take them.
167 // These constants were chosen empirically and may not work if the shadow
168 // memory layout changes. Unfortunately they do necessarily depend on
169 // kHighMemBeg or kHighMemEnd.
170 static void *island_allocator_pos
= 0;
173 # define kIslandEnd (0xffdf0000 - kPageSize)
174 # define kIslandBeg (kIslandEnd - 256 * kPageSize)
176 # define kIslandEnd (0x7fffffdf0000 - kPageSize)
177 # define kIslandBeg (kIslandEnd - 256 * kPageSize)
181 mach_error_t
__interception_allocate_island(void **ptr
,
184 if (!island_allocator_pos
) {
185 island_allocator_pos
=
186 internal_mmap((void*)kIslandBeg
, kIslandEnd
- kIslandBeg
,
187 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
188 MAP_PRIVATE
| MAP_ANON
| MAP_FIXED
,
190 if (island_allocator_pos
!= (void*)kIslandBeg
) {
191 return KERN_NO_SPACE
;
193 if (flags()->verbosity
) {
194 Report("Mapped pages %p--%p for branch islands.\n",
195 (void*)kIslandBeg
, (void*)kIslandEnd
);
197 // Should not be very performance-critical.
198 internal_memset(island_allocator_pos
, 0xCC, kIslandEnd
- kIslandBeg
);
200 *ptr
= island_allocator_pos
;
201 island_allocator_pos
= (char*)island_allocator_pos
+ kPageSize
;
202 if (flags()->verbosity
) {
203 Report("Branch island allocated at %p\n", *ptr
);
209 mach_error_t
__interception_deallocate_island(void *ptr
) {
211 // TODO(glider): allow to free and reuse the island memory.
215 // Support for the following functions from libdispatch on Mac OS:
216 // dispatch_async_f()
220 // dispatch_after_f()
222 // dispatch_group_async_f()
223 // dispatch_group_async()
224 // TODO(glider): libdispatch API contains other functions that we don't support
227 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
228 // they can cause jobs to run on a thread different from the current one.
229 // TODO(glider): if so, we need a test for this (otherwise we should remove
232 // The following functions use dispatch_barrier_async_f() (which isn't a library
233 // function but is exported) and are thus supported:
234 // dispatch_source_set_cancel_handler_f()
235 // dispatch_source_set_cancel_handler()
236 // dispatch_source_set_event_handler_f()
237 // dispatch_source_set_event_handler()
239 // The reference manual for Grand Central Dispatch is available at
240 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
241 // The implementation details are at
242 // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
244 typedef void* pthread_workqueue_t
;
245 typedef void* pthread_workitem_handle_t
;
247 typedef void* dispatch_group_t
;
248 typedef void* dispatch_queue_t
;
249 typedef void* dispatch_source_t
;
250 typedef u64 dispatch_time_t
;
251 typedef void (*dispatch_function_t
)(void *block
);
252 typedef void* (*worker_t
)(void *block
);
254 // A wrapper for the ObjC blocks used to support libdispatch.
257 dispatch_function_t func
;
259 } asan_block_context_t
;
261 // We use extern declarations of libdispatch functions here instead
262 // of including <dispatch/dispatch.h>. This header is not present on
263 // Mac OS X Leopard and eariler, and although we don't expect ASan to
264 // work on legacy systems, it's bad to break the build of
265 // LLVM compiler-rt there.
267 void dispatch_async_f(dispatch_queue_t dq
, void *ctxt
,
268 dispatch_function_t func
);
269 void dispatch_sync_f(dispatch_queue_t dq
, void *ctxt
,
270 dispatch_function_t func
);
271 void dispatch_after_f(dispatch_time_t when
, dispatch_queue_t dq
, void *ctxt
,
272 dispatch_function_t func
);
273 void dispatch_barrier_async_f(dispatch_queue_t dq
, void *ctxt
,
274 dispatch_function_t func
);
275 void dispatch_group_async_f(dispatch_group_t group
, dispatch_queue_t dq
,
276 void *ctxt
, dispatch_function_t func
);
277 int pthread_workqueue_additem_np(pthread_workqueue_t workq
,
278 void *(*workitem_func
)(void *), void * workitem_arg
,
279 pthread_workitem_handle_t
* itemhandlep
, unsigned int *gencountp
);
283 void asan_register_worker_thread(int parent_tid
, StackTrace
*stack
) {
284 AsanThread
*t
= asanThreadRegistry().GetCurrent();
286 t
= AsanThread::Create(parent_tid
, 0, 0, stack
);
287 asanThreadRegistry().RegisterThread(t
);
289 asanThreadRegistry().SetCurrent(t
);
293 // For use by only those functions that allocated the context via
294 // alloc_asan_context().
296 void asan_dispatch_call_block_and_release(void *block
) {
297 GET_STACK_TRACE_HERE(kStackTraceMax
);
298 asan_block_context_t
*context
= (asan_block_context_t
*)block
;
299 if (flags()->verbosity
>= 2) {
300 Report("asan_dispatch_call_block_and_release(): "
301 "context: %p, pthread_self: %p\n",
302 block
, pthread_self());
304 asan_register_worker_thread(context
->parent_tid
, &stack
);
305 // Call the original dispatcher for the block.
306 context
->func(context
->block
);
307 asan_free(context
, &stack
);
310 } // namespace __asan
312 using namespace __asan
; // NOLINT
314 // Wrap |ctxt| and |func| into an asan_block_context_t.
315 // The caller retains control of the allocated context.
317 asan_block_context_t
*alloc_asan_context(void *ctxt
, dispatch_function_t func
,
319 asan_block_context_t
*asan_ctxt
=
320 (asan_block_context_t
*) asan_malloc(sizeof(asan_block_context_t
), stack
);
321 asan_ctxt
->block
= ctxt
;
322 asan_ctxt
->func
= func
;
323 asan_ctxt
->parent_tid
= asanThreadRegistry().GetCurrentTidOrInvalid();
327 // Define interceptor for dispatch_*_f function with the three most common
328 // parameters: dispatch_queue_t, context, dispatch_function_t.
329 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
330 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
331 dispatch_function_t func) { \
332 GET_STACK_TRACE_HERE(kStackTraceMax); \
333 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
334 if (flags()->verbosity >= 2) { \
335 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
336 asan_ctxt, pthread_self()); \
337 PRINT_CURRENT_STACK(); \
339 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
340 asan_dispatch_call_block_and_release); \
343 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f
)
344 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f
)
345 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f
)
347 INTERCEPTOR(void, dispatch_after_f
, dispatch_time_t when
,
348 dispatch_queue_t dq
, void *ctxt
,
349 dispatch_function_t func
) {
350 GET_STACK_TRACE_HERE(kStackTraceMax
);
351 asan_block_context_t
*asan_ctxt
= alloc_asan_context(ctxt
, func
, &stack
);
352 if (flags()->verbosity
>= 2) {
353 Report("dispatch_after_f: %p\n", asan_ctxt
);
354 PRINT_CURRENT_STACK();
356 return REAL(dispatch_after_f
)(when
, dq
, (void*)asan_ctxt
,
357 asan_dispatch_call_block_and_release
);
360 INTERCEPTOR(void, dispatch_group_async_f
, dispatch_group_t group
,
361 dispatch_queue_t dq
, void *ctxt
,
362 dispatch_function_t func
) {
363 GET_STACK_TRACE_HERE(kStackTraceMax
);
364 asan_block_context_t
*asan_ctxt
= alloc_asan_context(ctxt
, func
, &stack
);
365 if (flags()->verbosity
>= 2) {
366 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
367 asan_ctxt
, pthread_self());
368 PRINT_CURRENT_STACK();
370 REAL(dispatch_group_async_f
)(group
, dq
, (void*)asan_ctxt
,
371 asan_dispatch_call_block_and_release
);
374 #if MAC_INTERPOSE_FUNCTIONS
375 // dispatch_async, dispatch_group_async and others tailcall the corresponding
376 // dispatch_*_f functions. When wrapping functions with mach_override, those
377 // dispatch_*_f are intercepted automatically. But with dylib interposition
378 // this does not work, because the calls within the same library are not
380 // Therefore we need to re-implement dispatch_async and friends.
383 // FIXME: consolidate these declarations with asan_intercepted_functions.h.
384 void dispatch_async(dispatch_queue_t dq
, void(^work
)(void));
385 void dispatch_group_async(dispatch_group_t dg
, dispatch_queue_t dq
,
387 void dispatch_after(dispatch_time_t when
, dispatch_queue_t queue
,
389 void dispatch_source_set_cancel_handler(dispatch_source_t ds
,
391 void dispatch_source_set_event_handler(dispatch_source_t ds
, void(^work
)(void));
394 #define GET_ASAN_BLOCK(work) \
395 void (^asan_block)(void); \
396 int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
397 asan_block = ^(void) { \
398 GET_STACK_TRACE_HERE(kStackTraceMax); \
399 asan_register_worker_thread(parent_tid, &stack); \
403 INTERCEPTOR(void, dispatch_async
,
404 dispatch_queue_t dq
, void(^work
)(void)) {
405 GET_ASAN_BLOCK(work
);
406 REAL(dispatch_async
)(dq
, asan_block
);
409 INTERCEPTOR(void, dispatch_group_async
,
410 dispatch_group_t dg
, dispatch_queue_t dq
, void(^work
)(void)) {
411 GET_ASAN_BLOCK(work
);
412 REAL(dispatch_group_async
)(dg
, dq
, asan_block
);
415 INTERCEPTOR(void, dispatch_after
,
416 dispatch_time_t when
, dispatch_queue_t queue
, void(^work
)(void)) {
417 GET_ASAN_BLOCK(work
);
418 REAL(dispatch_after
)(when
, queue
, asan_block
);
421 INTERCEPTOR(void, dispatch_source_set_cancel_handler
,
422 dispatch_source_t ds
, void(^work
)(void)) {
423 GET_ASAN_BLOCK(work
);
424 REAL(dispatch_source_set_cancel_handler
)(ds
, asan_block
);
427 INTERCEPTOR(void, dispatch_source_set_event_handler
,
428 dispatch_source_t ds
, void(^work
)(void)) {
429 GET_ASAN_BLOCK(work
);
430 REAL(dispatch_source_set_event_handler
)(ds
, asan_block
);
434 // The following stuff has been extremely helpful while looking for the
435 // unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
436 // level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
437 // find the points of worker thread creation (each of such threads may be used
438 // to run several tasks, that's why this is not enough to support the whole
441 void *wrap_workitem_func(void *arg
) {
442 if (flags()->verbosity
>= 2) {
443 Report("wrap_workitem_func: %p, pthread_self: %p\n", arg
, pthread_self());
445 asan_block_context_t
*ctxt
= (asan_block_context_t
*)arg
;
446 worker_t fn
= (worker_t
)(ctxt
->func
);
447 void *result
= fn(ctxt
->block
);
448 GET_STACK_TRACE_HERE(kStackTraceMax
);
449 asan_free(arg
, &stack
);
453 INTERCEPTOR(int, pthread_workqueue_additem_np
, pthread_workqueue_t workq
,
454 void *(*workitem_func
)(void *), void * workitem_arg
,
455 pthread_workitem_handle_t
* itemhandlep
, unsigned int *gencountp
) {
456 GET_STACK_TRACE_HERE(kStackTraceMax
);
457 asan_block_context_t
*asan_ctxt
=
458 (asan_block_context_t
*) asan_malloc(sizeof(asan_block_context_t
), &stack
);
459 asan_ctxt
->block
= workitem_arg
;
460 asan_ctxt
->func
= (dispatch_function_t
)workitem_func
;
461 asan_ctxt
->parent_tid
= asanThreadRegistry().GetCurrentTidOrInvalid();
462 if (flags()->verbosity
>= 2) {
463 Report("pthread_workqueue_additem_np: %p\n", asan_ctxt
);
464 PRINT_CURRENT_STACK();
466 return REAL(pthread_workqueue_additem_np
)(workq
, wrap_workitem_func
,
467 asan_ctxt
, itemhandlep
,
471 // See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
472 int __CFStrIsConstant(CFStringRef str
) {
473 CFRuntimeBase
*base
= (CFRuntimeBase
*)str
;
475 return base
->_rc
== 0;
477 return (base
->_cfinfo
[CF_RC_BITS
]) == 0;
481 INTERCEPTOR(CFStringRef
, CFStringCreateCopy
, CFAllocatorRef alloc
,
483 if (__CFStrIsConstant(str
)) {
486 return REAL(CFStringCreateCopy
)(alloc
, str
);
490 DECLARE_REAL_AND_INTERCEPTOR(void, free
, void *ptr
)
492 DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize
, void)
496 void InitializeMacInterceptors() {
497 CHECK(INTERCEPT_FUNCTION(dispatch_async_f
));
498 CHECK(INTERCEPT_FUNCTION(dispatch_sync_f
));
499 CHECK(INTERCEPT_FUNCTION(dispatch_after_f
));
500 CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f
));
501 CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f
));
502 // We don't need to intercept pthread_workqueue_additem_np() to support the
503 // libdispatch API, but it helps us to debug the unsupported functions. Let's
504 // intercept it only during verbose runs.
505 if (flags()->verbosity
>= 2) {
506 CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np
));
508 // Normally CFStringCreateCopy should not copy constant CF strings.
509 // Replacing the default CFAllocator causes constant strings to be copied
510 // rather than just returned, which leads to bugs in big applications like
511 // Chromium and WebKit, see
512 // http://code.google.com/p/address-sanitizer/issues/detail?id=10
513 // Until this problem is fixed we need to check that the string is
514 // non-constant before calling CFStringCreateCopy.
515 CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy
));
516 // Some of the library functions call free() directly, so we have to
518 CHECK(INTERCEPT_FUNCTION(free
));
519 if (flags()->replace_cfallocator
) {
520 CHECK(INTERCEPT_FUNCTION(__CFInitialize
));
524 } // namespace __asan