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 <crt_externs.h> // for _NSGetArgv
28 #include <dlfcn.h> // for dladdr()
29 #include <mach-o/dyld.h>
30 #include <mach-o/loader.h>
31 #include <sys/mman.h>
32 #include <sys/resource.h>
33 #include <sys/sysctl.h>
34 #include <sys/ucontext.h>
35 #include <fcntl.h>
36 #include <pthread.h>
37 #include <stdlib.h> // for free()
38 #include <unistd.h>
39 #include <libkern/OSAtomic.h>
40
41 namespace __asan {
42
InitializePlatformInterceptors()43 void InitializePlatformInterceptors() {}
44
PlatformHasDifferentMemcpyAndMemmove()45 bool PlatformHasDifferentMemcpyAndMemmove() {
46 // On OS X 10.7 memcpy() and memmove() are both resolved
47 // into memmove$VARIANT$sse42.
48 // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
49 // TODO(glider): need to check dynamically that memcpy() and memmove() are
50 // actually the same function.
51 return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
52 }
53
54 extern "C"
55 void __asan_init();
56
57 static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
58 LowLevelAllocator allocator_for_env;
59
60 // Change the value of the env var |name|, leaking the original value.
61 // If |name_value| is NULL, the variable is deleted from the environment,
62 // otherwise the corresponding "NAME=value" string is replaced with
63 // |name_value|.
LeakyResetEnv(const char * name,const char * name_value)64 void LeakyResetEnv(const char *name, const char *name_value) {
65 char ***env_ptr = _NSGetEnviron();
66 CHECK(env_ptr);
67 char **environ = *env_ptr;
68 CHECK(environ);
69 uptr name_len = internal_strlen(name);
70 while (*environ != 0) {
71 uptr len = internal_strlen(*environ);
72 if (len > name_len) {
73 const char *p = *environ;
74 if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
75 // Match.
76 if (name_value) {
77 // Replace the old value with the new one.
78 *environ = const_cast<char*>(name_value);
79 } else {
80 // Shift the subsequent pointers back.
81 char **del = environ;
82 do {
83 del[0] = del[1];
84 } while (*del++);
85 }
86 }
87 }
88 environ++;
89 }
90 }
91
92 static bool reexec_disabled = false;
93
DisableReexec()94 void DisableReexec() {
95 reexec_disabled = true;
96 }
97
MaybeReexec()98 void MaybeReexec() {
99 if (reexec_disabled) return;
100
101 // Make sure the dynamic ASan runtime library is preloaded so that the
102 // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
103 // ourselves.
104 Dl_info info;
105 CHECK(dladdr((void*)((uptr)__asan_init), &info));
106 char *dyld_insert_libraries =
107 const_cast<char*>(GetEnv(kDyldInsertLibraries));
108 uptr old_env_len = dyld_insert_libraries ?
109 internal_strlen(dyld_insert_libraries) : 0;
110 uptr fname_len = internal_strlen(info.dli_fname);
111 const char *dylib_name = StripModuleName(info.dli_fname);
112 uptr dylib_name_len = internal_strlen(dylib_name);
113 if (!dyld_insert_libraries ||
114 !REAL(strstr)(dyld_insert_libraries, dylib_name)) {
115 // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
116 // library.
117 char program_name[1024];
118 uint32_t buf_size = sizeof(program_name);
119 _NSGetExecutablePath(program_name, &buf_size);
120 char *new_env = const_cast<char*>(info.dli_fname);
121 if (dyld_insert_libraries) {
122 // Append the runtime dylib name to the existing value of
123 // DYLD_INSERT_LIBRARIES.
124 new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
125 internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
126 new_env[old_env_len] = ':';
127 // Copy fname_len and add a trailing zero.
128 internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
129 fname_len + 1);
130 // Ok to use setenv() since the wrappers don't depend on the value of
131 // asan_inited.
132 setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
133 } else {
134 // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
135 setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
136 }
137 VReport(1, "exec()-ing the program with\n");
138 VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
139 VReport(1, "to enable ASan wrappers.\n");
140 execv(program_name, *_NSGetArgv());
141
142 // We get here only if execv() failed.
143 Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
144 "which is required for ASan to work. ASan tried to set the "
145 "environment variable and re-execute itself, but execv() failed, "
146 "possibly because of sandbox restrictions. Make sure to launch the "
147 "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
148 CHECK("execv failed" && 0);
149 }
150
151 // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
152 // the dylib from the environment variable, because interceptors are installed
153 // and we don't want our children to inherit the variable.
154
155 uptr env_name_len = internal_strlen(kDyldInsertLibraries);
156 // Allocate memory to hold the previous env var name, its value, the '='
157 // sign and the '\0' char.
158 char *new_env = (char*)allocator_for_env.Allocate(
159 old_env_len + 2 + env_name_len);
160 CHECK(new_env);
161 internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
162 internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
163 new_env[env_name_len] = '=';
164 char *new_env_pos = new_env + env_name_len + 1;
165
166 // Iterate over colon-separated pieces of |dyld_insert_libraries|.
167 char *piece_start = dyld_insert_libraries;
168 char *piece_end = NULL;
169 char *old_env_end = dyld_insert_libraries + old_env_len;
170 do {
171 if (piece_start[0] == ':') piece_start++;
172 piece_end = REAL(strchr)(piece_start, ':');
173 if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
174 if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
175 uptr piece_len = piece_end - piece_start;
176
177 char *filename_start =
178 (char *)internal_memrchr(piece_start, '/', piece_len);
179 uptr filename_len = piece_len;
180 if (filename_start) {
181 filename_start += 1;
182 filename_len = piece_len - (filename_start - piece_start);
183 } else {
184 filename_start = piece_start;
185 }
186
187 // If the current piece isn't the runtime library name,
188 // append it to new_env.
189 if ((dylib_name_len != filename_len) ||
190 (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
191 if (new_env_pos != new_env + env_name_len + 1) {
192 new_env_pos[0] = ':';
193 new_env_pos++;
194 }
195 internal_strncpy(new_env_pos, piece_start, piece_len);
196 new_env_pos += piece_len;
197 }
198 // Move on to the next piece.
199 piece_start = piece_end;
200 } while (piece_start < old_env_end);
201
202 // Can't use setenv() here, because it requires the allocator to be
203 // initialized.
204 // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
205 // a separate function called after InitializeAllocator().
206 if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
207 LeakyResetEnv(kDyldInsertLibraries, new_env);
208 }
209
210 // No-op. Mac does not support static linkage anyway.
AsanDoesNotSupportStaticLinkage()211 void *AsanDoesNotSupportStaticLinkage() {
212 return 0;
213 }
214
215 // No-op. Mac does not support static linkage anyway.
AsanCheckDynamicRTPrereqs()216 void AsanCheckDynamicRTPrereqs() {}
217
218 // No-op. Mac does not support static linkage anyway.
AsanCheckIncompatibleRT()219 void AsanCheckIncompatibleRT() {}
220
AsanPlatformThreadInit()221 void AsanPlatformThreadInit() {
222 }
223
ReadContextStack(void * context,uptr * stack,uptr * ssize)224 void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
225 UNIMPLEMENTED();
226 }
227
228 // Support for the following functions from libdispatch on Mac OS:
229 // dispatch_async_f()
230 // dispatch_async()
231 // dispatch_sync_f()
232 // dispatch_sync()
233 // dispatch_after_f()
234 // dispatch_after()
235 // dispatch_group_async_f()
236 // dispatch_group_async()
237 // TODO(glider): libdispatch API contains other functions that we don't support
238 // yet.
239 //
240 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
241 // they can cause jobs to run on a thread different from the current one.
242 // TODO(glider): if so, we need a test for this (otherwise we should remove
243 // them).
244 //
245 // The following functions use dispatch_barrier_async_f() (which isn't a library
246 // function but is exported) and are thus supported:
247 // dispatch_source_set_cancel_handler_f()
248 // dispatch_source_set_cancel_handler()
249 // dispatch_source_set_event_handler_f()
250 // dispatch_source_set_event_handler()
251 //
252 // The reference manual for Grand Central Dispatch is available at
253 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
254 // The implementation details are at
255 // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
256
257 typedef void* dispatch_group_t;
258 typedef void* dispatch_queue_t;
259 typedef void* dispatch_source_t;
260 typedef u64 dispatch_time_t;
261 typedef void (*dispatch_function_t)(void *block);
262 typedef void* (*worker_t)(void *block);
263
264 // A wrapper for the ObjC blocks used to support libdispatch.
265 typedef struct {
266 void *block;
267 dispatch_function_t func;
268 u32 parent_tid;
269 } asan_block_context_t;
270
271 ALWAYS_INLINE
asan_register_worker_thread(int parent_tid,StackTrace * stack)272 void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
273 AsanThread *t = GetCurrentThread();
274 if (!t) {
275 t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
276 parent_tid, stack, /* detached */ true);
277 t->Init();
278 asanThreadRegistry().StartThread(t->tid(), 0, 0);
279 SetCurrentThread(t);
280 }
281 }
282
283 // For use by only those functions that allocated the context via
284 // alloc_asan_context().
285 extern "C"
asan_dispatch_call_block_and_release(void * block)286 void asan_dispatch_call_block_and_release(void *block) {
287 GET_STACK_TRACE_THREAD;
288 asan_block_context_t *context = (asan_block_context_t*)block;
289 VReport(2,
290 "asan_dispatch_call_block_and_release(): "
291 "context: %p, pthread_self: %p\n",
292 block, pthread_self());
293 asan_register_worker_thread(context->parent_tid, &stack);
294 // Call the original dispatcher for the block.
295 context->func(context->block);
296 asan_free(context, &stack, FROM_MALLOC);
297 }
298
299 } // namespace __asan
300
301 using namespace __asan; // NOLINT
302
303 // Wrap |ctxt| and |func| into an asan_block_context_t.
304 // The caller retains control of the allocated context.
305 extern "C"
alloc_asan_context(void * ctxt,dispatch_function_t func,BufferedStackTrace * stack)306 asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
307 BufferedStackTrace *stack) {
308 asan_block_context_t *asan_ctxt =
309 (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
310 asan_ctxt->block = ctxt;
311 asan_ctxt->func = func;
312 asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
313 return asan_ctxt;
314 }
315
316 // Define interceptor for dispatch_*_f function with the three most common
317 // parameters: dispatch_queue_t, context, dispatch_function_t.
318 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
319 INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
320 dispatch_function_t func) { \
321 GET_STACK_TRACE_THREAD; \
322 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
323 if (Verbosity() >= 2) { \
324 Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
325 asan_ctxt, pthread_self()); \
326 PRINT_CURRENT_STACK(); \
327 } \
328 return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
329 asan_dispatch_call_block_and_release); \
330 }
331
332 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)333 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
334 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
335
336 INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
337 dispatch_queue_t dq, void *ctxt,
338 dispatch_function_t func) {
339 GET_STACK_TRACE_THREAD;
340 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
341 if (Verbosity() >= 2) {
342 Report("dispatch_after_f: %p\n", asan_ctxt);
343 PRINT_CURRENT_STACK();
344 }
345 return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
346 asan_dispatch_call_block_and_release);
347 }
348
INTERCEPTOR(void,dispatch_group_async_f,dispatch_group_t group,dispatch_queue_t dq,void * ctxt,dispatch_function_t func)349 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
350 dispatch_queue_t dq, void *ctxt,
351 dispatch_function_t func) {
352 GET_STACK_TRACE_THREAD;
353 asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
354 if (Verbosity() >= 2) {
355 Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
356 asan_ctxt, pthread_self());
357 PRINT_CURRENT_STACK();
358 }
359 REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
360 asan_dispatch_call_block_and_release);
361 }
362
363 #if !defined(MISSING_BLOCKS_SUPPORT)
364 extern "C" {
365 void dispatch_async(dispatch_queue_t dq, void(^work)(void));
366 void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
367 void(^work)(void));
368 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
369 void(^work)(void));
370 void dispatch_source_set_cancel_handler(dispatch_source_t ds,
371 void(^work)(void));
372 void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
373 }
374
375 #define GET_ASAN_BLOCK(work) \
376 void (^asan_block)(void); \
377 int parent_tid = GetCurrentTidOrInvalid(); \
378 asan_block = ^(void) { \
379 GET_STACK_TRACE_THREAD; \
380 asan_register_worker_thread(parent_tid, &stack); \
381 work(); \
382 }
383
384 INTERCEPTOR(void, dispatch_async,
385 dispatch_queue_t dq, void(^work)(void)) {
386 ENABLE_FRAME_POINTER;
387 GET_ASAN_BLOCK(work);
388 REAL(dispatch_async)(dq, asan_block);
389 }
390
391 INTERCEPTOR(void, dispatch_group_async,
392 dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
393 ENABLE_FRAME_POINTER;
394 GET_ASAN_BLOCK(work);
395 REAL(dispatch_group_async)(dg, dq, asan_block);
396 }
397
398 INTERCEPTOR(void, dispatch_after,
399 dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
400 ENABLE_FRAME_POINTER;
401 GET_ASAN_BLOCK(work);
402 REAL(dispatch_after)(when, queue, asan_block);
403 }
404
405 INTERCEPTOR(void, dispatch_source_set_cancel_handler,
406 dispatch_source_t ds, void(^work)(void)) {
407 if (!work) {
408 REAL(dispatch_source_set_cancel_handler)(ds, work);
409 return;
410 }
411 ENABLE_FRAME_POINTER;
412 GET_ASAN_BLOCK(work);
413 REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
414 }
415
416 INTERCEPTOR(void, dispatch_source_set_event_handler,
417 dispatch_source_t ds, void(^work)(void)) {
418 ENABLE_FRAME_POINTER;
419 GET_ASAN_BLOCK(work);
420 REAL(dispatch_source_set_event_handler)(ds, asan_block);
421 }
422 #endif
423
424 #endif // SANITIZER_MAC
425