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