123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- //===-- asan_mac.cc -------------------------------------------------------===//
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // This file is a part of AddressSanitizer, an address sanity checker.
- //
- // Mac-specific details.
- //===----------------------------------------------------------------------===//
- #include "sanitizer_common/sanitizer_platform.h"
- #if SANITIZER_MAC
- #include "asan_interceptors.h"
- #include "asan_internal.h"
- #include "asan_mapping.h"
- #include "asan_stack.h"
- #include "asan_thread.h"
- #include "sanitizer_common/sanitizer_atomic.h"
- #include "sanitizer_common/sanitizer_libc.h"
- #include "sanitizer_common/sanitizer_mac.h"
- #include <crt_externs.h> // for _NSGetArgv
- #include <dlfcn.h> // for dladdr()
- #include <mach-o/dyld.h>
- #include <mach-o/loader.h>
- #include <sys/mman.h>
- #include <sys/resource.h>
- #include <sys/sysctl.h>
- #include <sys/ucontext.h>
- #include <fcntl.h>
- #include <pthread.h>
- #include <stdlib.h> // for free()
- #include <unistd.h>
- #include <libkern/OSAtomic.h>
- namespace __asan {
- void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
- ucontext_t *ucontext = (ucontext_t*)context;
- # if SANITIZER_WORDSIZE == 64
- *pc = ucontext->uc_mcontext->__ss.__rip;
- *bp = ucontext->uc_mcontext->__ss.__rbp;
- *sp = ucontext->uc_mcontext->__ss.__rsp;
- # else
- *pc = ucontext->uc_mcontext->__ss.__eip;
- *bp = ucontext->uc_mcontext->__ss.__ebp;
- *sp = ucontext->uc_mcontext->__ss.__esp;
- # endif // SANITIZER_WORDSIZE
- }
- bool PlatformHasDifferentMemcpyAndMemmove() {
- // On OS X 10.7 memcpy() and memmove() are both resolved
- // into memmove$VARIANT$sse42.
- // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
- // TODO(glider): need to check dynamically that memcpy() and memmove() are
- // actually the same function.
- return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
- }
- extern "C"
- void __asan_init();
- static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
- LowLevelAllocator allocator_for_env;
- // Change the value of the env var |name|, leaking the original value.
- // If |name_value| is NULL, the variable is deleted from the environment,
- // otherwise the corresponding "NAME=value" string is replaced with
- // |name_value|.
- void LeakyResetEnv(const char *name, const char *name_value) {
- char ***env_ptr = _NSGetEnviron();
- CHECK(env_ptr);
- char **environ = *env_ptr;
- CHECK(environ);
- uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
- if (len > name_len) {
- const char *p = *environ;
- if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
- // Match.
- if (name_value) {
- // Replace the old value with the new one.
- *environ = const_cast<char*>(name_value);
- } else {
- // Shift the subsequent pointers back.
- char **del = environ;
- do {
- del[0] = del[1];
- } while (*del++);
- }
- }
- }
- environ++;
- }
- }
- void MaybeReexec() {
- if (!flags()->allow_reexec) return;
- // Make sure the dynamic ASan runtime library is preloaded so that the
- // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
- // ourselves.
- Dl_info info;
- CHECK(dladdr((void*)((uptr)__asan_init), &info));
- char *dyld_insert_libraries =
- const_cast<char*>(GetEnv(kDyldInsertLibraries));
- uptr old_env_len = dyld_insert_libraries ?
- internal_strlen(dyld_insert_libraries) : 0;
- uptr fname_len = internal_strlen(info.dli_fname);
- if (!dyld_insert_libraries ||
- !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
- // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
- // library.
- char program_name[1024];
- uint32_t buf_size = sizeof(program_name);
- _NSGetExecutablePath(program_name, &buf_size);
- char *new_env = const_cast<char*>(info.dli_fname);
- if (dyld_insert_libraries) {
- // Append the runtime dylib name to the existing value of
- // DYLD_INSERT_LIBRARIES.
- new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
- internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
- new_env[old_env_len] = ':';
- // Copy fname_len and add a trailing zero.
- internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
- fname_len + 1);
- // Ok to use setenv() since the wrappers don't depend on the value of
- // asan_inited.
- setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
- } else {
- // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
- setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
- }
- VReport(1, "exec()-ing the program with\n");
- VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
- VReport(1, "to enable ASan wrappers.\n");
- VReport(1, "Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
- execv(program_name, *_NSGetArgv());
- } else {
- // DYLD_INSERT_LIBRARIES is set and contains the runtime library.
- if (old_env_len == fname_len) {
- // It's just the runtime library name - fine to unset the variable.
- LeakyResetEnv(kDyldInsertLibraries, NULL);
- } else {
- uptr env_name_len = internal_strlen(kDyldInsertLibraries);
- // Allocate memory to hold the previous env var name, its value, the '='
- // sign and the '\0' char.
- char *new_env = (char*)allocator_for_env.Allocate(
- old_env_len + 2 + env_name_len);
- CHECK(new_env);
- internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
- internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
- new_env[env_name_len] = '=';
- char *new_env_pos = new_env + env_name_len + 1;
- // Iterate over colon-separated pieces of |dyld_insert_libraries|.
- char *piece_start = dyld_insert_libraries;
- char *piece_end = NULL;
- char *old_env_end = dyld_insert_libraries + old_env_len;
- do {
- if (piece_start[0] == ':') piece_start++;
- piece_end = REAL(strchr)(piece_start, ':');
- if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
- if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
- uptr piece_len = piece_end - piece_start;
- // If the current piece isn't the runtime library name,
- // append it to new_env.
- if ((piece_len != fname_len) ||
- (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) {
- if (new_env_pos != new_env + env_name_len + 1) {
- new_env_pos[0] = ':';
- new_env_pos++;
- }
- internal_strncpy(new_env_pos, piece_start, piece_len);
- }
- // Move on to the next piece.
- new_env_pos += piece_len;
- piece_start = piece_end;
- } while (piece_start < old_env_end);
- // Can't use setenv() here, because it requires the allocator to be
- // initialized.
- // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
- // a separate function called after InitializeAllocator().
- LeakyResetEnv(kDyldInsertLibraries, new_env);
- }
- }
- }
- // No-op. Mac does not support static linkage anyway.
- void *AsanDoesNotSupportStaticLinkage() {
- return 0;
- }
- // No-op. Mac does not support static linkage anyway.
- void AsanCheckDynamicRTPrereqs() {}
- // No-op. Mac does not support static linkage anyway.
- void AsanCheckIncompatibleRT() {}
- bool AsanInterceptsSignal(int signum) {
- return (signum == SIGSEGV || signum == SIGBUS) &&
- common_flags()->handle_segv;
- }
- void AsanPlatformThreadInit() {
- }
- void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
- UNIMPLEMENTED();
- }
- // Support for the following functions from libdispatch on Mac OS:
- // dispatch_async_f()
- // dispatch_async()
- // dispatch_sync_f()
- // dispatch_sync()
- // dispatch_after_f()
- // dispatch_after()
- // dispatch_group_async_f()
- // dispatch_group_async()
- // TODO(glider): libdispatch API contains other functions that we don't support
- // yet.
- //
- // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
- // they can cause jobs to run on a thread different from the current one.
- // TODO(glider): if so, we need a test for this (otherwise we should remove
- // them).
- //
- // The following functions use dispatch_barrier_async_f() (which isn't a library
- // function but is exported) and are thus supported:
- // dispatch_source_set_cancel_handler_f()
- // dispatch_source_set_cancel_handler()
- // dispatch_source_set_event_handler_f()
- // dispatch_source_set_event_handler()
- //
- // The reference manual for Grand Central Dispatch is available at
- // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
- // The implementation details are at
- // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
- typedef void* dispatch_group_t;
- typedef void* dispatch_queue_t;
- typedef void* dispatch_source_t;
- typedef u64 dispatch_time_t;
- typedef void (*dispatch_function_t)(void *block);
- typedef void* (*worker_t)(void *block);
- // A wrapper for the ObjC blocks used to support libdispatch.
- typedef struct {
- void *block;
- dispatch_function_t func;
- u32 parent_tid;
- } asan_block_context_t;
- ALWAYS_INLINE
- void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
- AsanThread *t = GetCurrentThread();
- if (!t) {
- t = AsanThread::Create(0, 0);
- CreateThreadContextArgs args = { t, stack };
- asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args);
- t->Init();
- asanThreadRegistry().StartThread(t->tid(), 0, 0);
- SetCurrentThread(t);
- }
- }
- // For use by only those functions that allocated the context via
- // alloc_asan_context().
- extern "C"
- void asan_dispatch_call_block_and_release(void *block) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *context = (asan_block_context_t*)block;
- VReport(2,
- "asan_dispatch_call_block_and_release(): "
- "context: %p, pthread_self: %p\n",
- block, pthread_self());
- asan_register_worker_thread(context->parent_tid, &stack);
- // Call the original dispatcher for the block.
- context->func(context->block);
- asan_free(context, &stack, FROM_MALLOC);
- }
- } // namespace __asan
- using namespace __asan; // NOLINT
- // Wrap |ctxt| and |func| into an asan_block_context_t.
- // The caller retains control of the allocated context.
- extern "C"
- asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- BufferedStackTrace *stack) {
- asan_block_context_t *asan_ctxt =
- (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
- asan_ctxt->block = ctxt;
- asan_ctxt->func = func;
- asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
- return asan_ctxt;
- }
- // Define interceptor for dispatch_*_f function with the three most common
- // parameters: dispatch_queue_t, context, dispatch_function_t.
- #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
- INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
- dispatch_function_t func) { \
- GET_STACK_TRACE_THREAD; \
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
- if (common_flags()->verbosity >= 2) { \
- Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
- asan_ctxt, pthread_self()); \
- PRINT_CURRENT_STACK(); \
- } \
- return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
- asan_dispatch_call_block_and_release); \
- }
- INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
- INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
- INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
- INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
- dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (common_flags()->verbosity >= 2) {
- Report("dispatch_after_f: %p\n", asan_ctxt);
- PRINT_CURRENT_STACK();
- }
- return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
- }
- INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
- dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (common_flags()->verbosity >= 2) {
- Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
- }
- #if !defined(MISSING_BLOCKS_SUPPORT)
- extern "C" {
- void dispatch_async(dispatch_queue_t dq, void(^work)(void));
- void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
- void(^work)(void));
- void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
- void(^work)(void));
- void dispatch_source_set_cancel_handler(dispatch_source_t ds,
- void(^work)(void));
- void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
- }
- #define GET_ASAN_BLOCK(work) \
- void (^asan_block)(void); \
- int parent_tid = GetCurrentTidOrInvalid(); \
- asan_block = ^(void) { \
- GET_STACK_TRACE_THREAD; \
- asan_register_worker_thread(parent_tid, &stack); \
- work(); \
- }
- // Forces the compiler to generate a frame pointer in the function.
- #define ENABLE_FRAME_POINTER \
- do { \
- volatile uptr enable_fp; \
- enable_fp = GET_CURRENT_FRAME(); \
- } while (0)
- INTERCEPTOR(void, dispatch_async,
- dispatch_queue_t dq, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_async)(dq, asan_block);
- }
- INTERCEPTOR(void, dispatch_group_async,
- dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_group_async)(dg, dq, asan_block);
- }
- INTERCEPTOR(void, dispatch_after,
- dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_after)(when, queue, asan_block);
- }
- INTERCEPTOR(void, dispatch_source_set_cancel_handler,
- dispatch_source_t ds, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
- }
- INTERCEPTOR(void, dispatch_source_set_event_handler,
- dispatch_source_t ds, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_source_set_event_handler)(ds, asan_block);
- }
- #endif
- #endif // SANITIZER_MAC
|