• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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