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 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_platform.h"
16 #include "asan_interceptors.h"
17 #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"
25 #include <crt_externs.h> // for _NSGetArgv
26 #include <dlfcn.h> // for dladdr()
27 #include <mach-o/dyld.h>
28 #include <mach-o/loader.h>
30 #include <sys/resource.h>
31 #include <sys/sysctl.h>
32 #include <sys/ucontext.h>
35 #include <stdlib.h> // for free()
37 #include <libkern/OSAtomic.h>
41 void GetPcSpBp(void *context
, uptr
*pc
, uptr
*sp
, uptr
*bp
) {
42 ucontext_t
*ucontext
= (ucontext_t
*)context
;
43 # if SANITIZER_WORDSIZE == 64
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
;
51 # endif // SANITIZER_WORDSIZE
54 MacosVersion cached_macos_version
= MACOS_VERSION_UNINITIALIZED
;
56 MacosVersion
GetMacosVersionInternal() {
57 int mib
[2] = { CTL_KERN
, KERN_OSRELEASE
};
59 uptr len
= 0, maxlen
= sizeof(version
) / sizeof(version
[0]);
60 for (uptr i
= 0; i
< maxlen
; i
++) version
[i
] = '\0';
61 // Get the version length.
62 CHECK_NE(sysctl(mib
, 2, 0, &len
, 0, 0), -1);
63 CHECK_LT(len
, maxlen
);
64 CHECK_NE(sysctl(mib
, 2, version
, &len
, 0, 0), -1);
66 case '9': return MACOS_VERSION_LEOPARD
;
69 case '0': return MACOS_VERSION_SNOW_LEOPARD
;
70 case '1': return MACOS_VERSION_LION
;
71 case '2': return MACOS_VERSION_MOUNTAIN_LION
;
72 case '3': return MACOS_VERSION_MAVERICKS
;
73 default: return MACOS_VERSION_UNKNOWN
;
76 default: return MACOS_VERSION_UNKNOWN
;
80 MacosVersion
GetMacosVersion() {
81 atomic_uint32_t
*cache
=
82 reinterpret_cast<atomic_uint32_t
*>(&cached_macos_version
);
84 static_cast<MacosVersion
>(atomic_load(cache
, memory_order_acquire
));
85 if (result
== MACOS_VERSION_UNINITIALIZED
) {
86 result
= GetMacosVersionInternal();
87 atomic_store(cache
, result
, memory_order_release
);
92 bool PlatformHasDifferentMemcpyAndMemmove() {
93 // On OS X 10.7 memcpy() and memmove() are both resolved
94 // into memmove$VARIANT$sse42.
95 // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
96 // TODO(glider): need to check dynamically that memcpy() and memmove() are
97 // actually the same function.
98 return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD
;
104 static const char kDyldInsertLibraries
[] = "DYLD_INSERT_LIBRARIES";
105 LowLevelAllocator allocator_for_env
;
107 // Change the value of the env var |name|, leaking the original value.
108 // If |name_value| is NULL, the variable is deleted from the environment,
109 // otherwise the corresponding "NAME=value" string is replaced with
111 void LeakyResetEnv(const char *name
, const char *name_value
) {
112 char ***env_ptr
= _NSGetEnviron();
114 char **environ
= *env_ptr
;
116 uptr name_len
= internal_strlen(name
);
117 while (*environ
!= 0) {
118 uptr len
= internal_strlen(*environ
);
119 if (len
> name_len
) {
120 const char *p
= *environ
;
121 if (!internal_memcmp(p
, name
, name_len
) && p
[name_len
] == '=') {
124 // Replace the old value with the new one.
125 *environ
= const_cast<char*>(name_value
);
127 // Shift the subsequent pointers back.
128 char **del
= environ
;
140 if (!flags()->allow_reexec
) return;
141 // Make sure the dynamic ASan runtime library is preloaded so that the
142 // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
145 CHECK(dladdr((void*)((uptr
)__asan_init
), &info
));
146 char *dyld_insert_libraries
=
147 const_cast<char*>(GetEnv(kDyldInsertLibraries
));
148 uptr old_env_len
= dyld_insert_libraries
?
149 internal_strlen(dyld_insert_libraries
) : 0;
150 uptr fname_len
= internal_strlen(info
.dli_fname
);
151 if (!dyld_insert_libraries
||
152 !REAL(strstr
)(dyld_insert_libraries
, info
.dli_fname
)) {
153 // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
155 char program_name
[1024];
156 uint32_t buf_size
= sizeof(program_name
);
157 _NSGetExecutablePath(program_name
, &buf_size
);
158 char *new_env
= const_cast<char*>(info
.dli_fname
);
159 if (dyld_insert_libraries
) {
160 // Append the runtime dylib name to the existing value of
161 // DYLD_INSERT_LIBRARIES.
162 new_env
= (char*)allocator_for_env
.Allocate(old_env_len
+ fname_len
+ 2);
163 internal_strncpy(new_env
, dyld_insert_libraries
, old_env_len
);
164 new_env
[old_env_len
] = ':';
165 // Copy fname_len and add a trailing zero.
166 internal_strncpy(new_env
+ old_env_len
+ 1, info
.dli_fname
,
168 // Ok to use setenv() since the wrappers don't depend on the value of
170 setenv(kDyldInsertLibraries
, new_env
, /*overwrite*/1);
172 // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
173 setenv(kDyldInsertLibraries
, info
.dli_fname
, /*overwrite*/0);
175 if (common_flags()->verbosity
>= 1) {
176 Report("exec()-ing the program with\n");
177 Report("%s=%s\n", kDyldInsertLibraries
, new_env
);
178 Report("to enable ASan wrappers.\n");
179 Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
181 execv(program_name
, *_NSGetArgv());
183 // DYLD_INSERT_LIBRARIES is set and contains the runtime library.
184 if (old_env_len
== fname_len
) {
185 // It's just the runtime library name - fine to unset the variable.
186 LeakyResetEnv(kDyldInsertLibraries
, NULL
);
188 uptr env_name_len
= internal_strlen(kDyldInsertLibraries
);
189 // Allocate memory to hold the previous env var name, its value, the '='
190 // sign and the '\0' char.
191 char *new_env
= (char*)allocator_for_env
.Allocate(
192 old_env_len
+ 2 + env_name_len
);
194 internal_memset(new_env
, '\0', old_env_len
+ 2 + env_name_len
);
195 internal_strncpy(new_env
, kDyldInsertLibraries
, env_name_len
);
196 new_env
[env_name_len
] = '=';
197 char *new_env_pos
= new_env
+ env_name_len
+ 1;
199 // Iterate over colon-separated pieces of |dyld_insert_libraries|.
200 char *piece_start
= dyld_insert_libraries
;
201 char *piece_end
= NULL
;
202 char *old_env_end
= dyld_insert_libraries
+ old_env_len
;
204 if (piece_start
[0] == ':') piece_start
++;
205 piece_end
= REAL(strchr
)(piece_start
, ':');
206 if (!piece_end
) piece_end
= dyld_insert_libraries
+ old_env_len
;
207 if ((uptr
)(piece_start
- dyld_insert_libraries
) > old_env_len
) break;
208 uptr piece_len
= piece_end
- piece_start
;
210 // If the current piece isn't the runtime library name,
211 // append it to new_env.
212 if ((piece_len
!= fname_len
) ||
213 (internal_strncmp(piece_start
, info
.dli_fname
, fname_len
) != 0)) {
214 if (new_env_pos
!= new_env
+ env_name_len
+ 1) {
215 new_env_pos
[0] = ':';
218 internal_strncpy(new_env_pos
, piece_start
, piece_len
);
220 // Move on to the next piece.
221 new_env_pos
+= piece_len
;
222 piece_start
= piece_end
;
223 } while (piece_start
< old_env_end
);
225 // Can't use setenv() here, because it requires the allocator to be
227 // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
228 // a separate function called after InitializeAllocator().
229 LeakyResetEnv(kDyldInsertLibraries
, new_env
);
234 // No-op. Mac does not support static linkage anyway.
235 void *AsanDoesNotSupportStaticLinkage() {
239 bool AsanInterceptsSignal(int signum
) {
240 return (signum
== SIGSEGV
|| signum
== SIGBUS
) && flags()->handle_segv
;
243 void AsanPlatformThreadInit() {
246 void ReadContextStack(void *context
, uptr
*stack
, uptr
*ssize
) {
250 // Support for the following functions from libdispatch on Mac OS:
251 // dispatch_async_f()
255 // dispatch_after_f()
257 // dispatch_group_async_f()
258 // dispatch_group_async()
259 // TODO(glider): libdispatch API contains other functions that we don't support
262 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
263 // they can cause jobs to run on a thread different from the current one.
264 // TODO(glider): if so, we need a test for this (otherwise we should remove
267 // The following functions use dispatch_barrier_async_f() (which isn't a library
268 // function but is exported) and are thus supported:
269 // dispatch_source_set_cancel_handler_f()
270 // dispatch_source_set_cancel_handler()
271 // dispatch_source_set_event_handler_f()
272 // dispatch_source_set_event_handler()
274 // The reference manual for Grand Central Dispatch is available at
275 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
276 // The implementation details are at
277 // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
279 typedef void* dispatch_group_t
;
280 typedef void* dispatch_queue_t
;
281 typedef void* dispatch_source_t
;
282 typedef u64 dispatch_time_t
;
283 typedef void (*dispatch_function_t
)(void *block
);
284 typedef void* (*worker_t
)(void *block
);
286 // A wrapper for the ObjC blocks used to support libdispatch.
289 dispatch_function_t func
;
291 } asan_block_context_t
;
294 void asan_register_worker_thread(int parent_tid
, StackTrace
*stack
) {
295 AsanThread
*t
= GetCurrentThread();
297 t
= AsanThread::Create(0, 0);
298 CreateThreadContextArgs args
= { t
, stack
};
299 asanThreadRegistry().CreateThread(*(uptr
*)t
, true, parent_tid
, &args
);
301 asanThreadRegistry().StartThread(t
->tid(), 0, 0);
306 // For use by only those functions that allocated the context via
307 // alloc_asan_context().
309 void asan_dispatch_call_block_and_release(void *block
) {
310 GET_STACK_TRACE_THREAD
;
311 asan_block_context_t
*context
= (asan_block_context_t
*)block
;
312 if (common_flags()->verbosity
>= 2) {
313 Report("asan_dispatch_call_block_and_release(): "
314 "context: %p, pthread_self: %p\n",
315 block
, pthread_self());
317 asan_register_worker_thread(context
->parent_tid
, &stack
);
318 // Call the original dispatcher for the block.
319 context
->func(context
->block
);
320 asan_free(context
, &stack
, FROM_MALLOC
);
323 } // namespace __asan
325 using namespace __asan
; // NOLINT
327 // Wrap |ctxt| and |func| into an asan_block_context_t.
328 // The caller retains control of the allocated context.
330 asan_block_context_t
*alloc_asan_context(void *ctxt
, dispatch_function_t func
,
332 asan_block_context_t
*asan_ctxt
=
333 (asan_block_context_t
*) asan_malloc(sizeof(asan_block_context_t
), stack
);
334 asan_ctxt
->block
= ctxt
;
335 asan_ctxt
->func
= func
;
336 asan_ctxt
->parent_tid
= GetCurrentTidOrInvalid();
340 // Define interceptor for dispatch_*_f function with the three most common
341 // parameters: dispatch_queue_t, context, dispatch_function_t.
342 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
343 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
344 dispatch_function_t func) { \
345 GET_STACK_TRACE_THREAD; \
346 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
347 if (common_flags()->verbosity >= 2) { \
348 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
349 asan_ctxt, pthread_self()); \
350 PRINT_CURRENT_STACK(); \
352 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
353 asan_dispatch_call_block_and_release); \
356 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f
)
357 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f
)
358 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f
)
360 INTERCEPTOR(void, dispatch_after_f
, dispatch_time_t when
,
361 dispatch_queue_t dq
, void *ctxt
,
362 dispatch_function_t func
) {
363 GET_STACK_TRACE_THREAD
;
364 asan_block_context_t
*asan_ctxt
= alloc_asan_context(ctxt
, func
, &stack
);
365 if (common_flags()->verbosity
>= 2) {
366 Report("dispatch_after_f: %p\n", asan_ctxt
);
367 PRINT_CURRENT_STACK();
369 return REAL(dispatch_after_f
)(when
, dq
, (void*)asan_ctxt
,
370 asan_dispatch_call_block_and_release
);
373 INTERCEPTOR(void, dispatch_group_async_f
, dispatch_group_t group
,
374 dispatch_queue_t dq
, void *ctxt
,
375 dispatch_function_t func
) {
376 GET_STACK_TRACE_THREAD
;
377 asan_block_context_t
*asan_ctxt
= alloc_asan_context(ctxt
, func
, &stack
);
378 if (common_flags()->verbosity
>= 2) {
379 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
380 asan_ctxt
, pthread_self());
381 PRINT_CURRENT_STACK();
383 REAL(dispatch_group_async_f
)(group
, dq
, (void*)asan_ctxt
,
384 asan_dispatch_call_block_and_release
);
387 #if !defined(MISSING_BLOCKS_SUPPORT)
389 // FIXME: consolidate these declarations with asan_intercepted_functions.h.
390 void dispatch_async(dispatch_queue_t dq
, void(^work
)(void));
391 void dispatch_group_async(dispatch_group_t dg
, dispatch_queue_t dq
,
393 void dispatch_after(dispatch_time_t when
, dispatch_queue_t queue
,
395 void dispatch_source_set_cancel_handler(dispatch_source_t ds
,
397 void dispatch_source_set_event_handler(dispatch_source_t ds
, void(^work
)(void));
400 #define GET_ASAN_BLOCK(work) \
401 void (^asan_block)(void); \
402 int parent_tid = GetCurrentTidOrInvalid(); \
403 asan_block = ^(void) { \
404 GET_STACK_TRACE_THREAD; \
405 asan_register_worker_thread(parent_tid, &stack); \
409 INTERCEPTOR(void, dispatch_async
,
410 dispatch_queue_t dq
, void(^work
)(void)) {
411 GET_ASAN_BLOCK(work
);
412 REAL(dispatch_async
)(dq
, asan_block
);
415 INTERCEPTOR(void, dispatch_group_async
,
416 dispatch_group_t dg
, dispatch_queue_t dq
, void(^work
)(void)) {
417 GET_ASAN_BLOCK(work
);
418 REAL(dispatch_group_async
)(dg
, dq
, asan_block
);
421 INTERCEPTOR(void, dispatch_after
,
422 dispatch_time_t when
, dispatch_queue_t queue
, void(^work
)(void)) {
423 GET_ASAN_BLOCK(work
);
424 REAL(dispatch_after
)(when
, queue
, asan_block
);
427 INTERCEPTOR(void, dispatch_source_set_cancel_handler
,
428 dispatch_source_t ds
, void(^work
)(void)) {
429 GET_ASAN_BLOCK(work
);
430 REAL(dispatch_source_set_cancel_handler
)(ds
, asan_block
);
433 INTERCEPTOR(void, dispatch_source_set_event_handler
,
434 dispatch_source_t ds
, void(^work
)(void)) {
435 GET_ASAN_BLOCK(work
);
436 REAL(dispatch_source_set_event_handler
)(ds
, asan_block
);
440 #endif // SANITIZER_MAC