1//===-- asan_mac.cc -------------------------------------------------------===//
2//
3// This file is distributed under the University of Illinois Open Source
4// License. See LICENSE.TXT for details.
5//
6//===----------------------------------------------------------------------===//
7//
8// This file is a part of AddressSanitizer, an address sanity checker.
9//
10// Mac-specific details.
11//===----------------------------------------------------------------------===//
12
13#include "sanitizer_common/sanitizer_platform.h"
14#if SANITIZER_MAC
15
16#include "asan_interceptors.h"
17#include "asan_internal.h"
18#include "asan_mapping.h"
19#include "asan_stack.h"
20#include "asan_thread.h"
21#include "sanitizer_common/sanitizer_atomic.h"
22#include "sanitizer_common/sanitizer_libc.h"
23#include "sanitizer_common/sanitizer_mac.h"
24
25#include <dlfcn.h>
26#include <fcntl.h>
27#include <libkern/OSAtomic.h>
28#include <mach-o/dyld.h>
29#include <mach-o/getsect.h>
30#include <mach-o/loader.h>
31#include <pthread.h>
32#include <stdlib.h>  // for free()
33#include <sys/mman.h>
34#include <sys/resource.h>
35#include <sys/sysctl.h>
36#include <sys/ucontext.h>
37#include <unistd.h>
38
39// from <crt_externs.h>, but we don't have that file on iOS
40extern "C" {
41  extern char ***_NSGetArgv(void);
42  extern char ***_NSGetEnviron(void);
43}
44
45namespace __asan {
46
47void InitializePlatformInterceptors() {}
48void InitializePlatformExceptionHandlers() {}
49bool IsSystemHeapAddress (uptr addr) { return false; }
50
51// No-op. Mac does not support static linkage anyway.
52void *AsanDoesNotSupportStaticLinkage() {
53  return 0;
54}
55
56uptr FindDynamicShadowStart() {
57  uptr granularity = GetMmapGranularity();
58  uptr alignment = 8 * granularity;
59  uptr left_padding = granularity;
60  uptr space_size = kHighShadowEnd + left_padding;
61
62  uptr largest_gap_found = 0;
63  uptr max_occupied_addr = 0;
64  VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
65  uptr shadow_start =
66      FindAvailableMemoryRange(space_size, alignment, granularity,
67                               &largest_gap_found, &max_occupied_addr);
68  // If the shadow doesn't fit, restrict the address space to make it fit.
69  if (shadow_start == 0) {
70    VReport(
71        2,
72        "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n",
73        largest_gap_found, max_occupied_addr);
74    uptr new_max_vm = RoundDownTo(largest_gap_found << SHADOW_SCALE, alignment);
75    if (new_max_vm < max_occupied_addr) {
76      Report("Unable to find a memory range for dynamic shadow.\n");
77      Report(
78          "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, "
79          "new_max_vm = %p\n",
80          space_size, largest_gap_found, max_occupied_addr, new_max_vm);
81      CHECK(0 && "cannot place shadow");
82    }
83    RestrictMemoryToMaxAddress(new_max_vm);
84    kHighMemEnd = new_max_vm - 1;
85    space_size = kHighShadowEnd + left_padding;
86    VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
87    shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity,
88                                            nullptr, nullptr);
89    if (shadow_start == 0) {
90      Report("Unable to find a memory range after restricting VM.\n");
91      CHECK(0 && "cannot place shadow after restricting vm");
92    }
93  }
94  CHECK_NE((uptr)0, shadow_start);
95  CHECK(IsAligned(shadow_start, alignment));
96  return shadow_start;
97}
98
99// No-op. Mac does not support static linkage anyway.
100void AsanCheckDynamicRTPrereqs() {}
101
102// No-op. Mac does not support static linkage anyway.
103void AsanCheckIncompatibleRT() {}
104
105void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
106  // Find the Mach-O header for the image containing the needle
107  Dl_info info;
108  int err = dladdr(needle, &info);
109  if (err == 0) return;
110
111#if __LP64__
112  const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase;
113#else
114  const struct mach_header *mh = (struct mach_header *)info.dli_fbase;
115#endif
116
117  // Look up the __asan_globals section in that image and register its globals
118  unsigned long size = 0;
119  __asan_global *globals = (__asan_global *)getsectiondata(
120      mh,
121      "__DATA", "__asan_globals",
122      &size);
123
124  if (!globals) return;
125  if (size % sizeof(__asan_global) != 0) return;
126  op(globals, size / sizeof(__asan_global));
127}
128
129void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
130  UNIMPLEMENTED();
131}
132
133// Support for the following functions from libdispatch on Mac OS:
134//   dispatch_async_f()
135//   dispatch_async()
136//   dispatch_sync_f()
137//   dispatch_sync()
138//   dispatch_after_f()
139//   dispatch_after()
140//   dispatch_group_async_f()
141//   dispatch_group_async()
142// TODO(glider): libdispatch API contains other functions that we don't support
143// yet.
144//
145// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
146// they can cause jobs to run on a thread different from the current one.
147// TODO(glider): if so, we need a test for this (otherwise we should remove
148// them).
149//
150// The following functions use dispatch_barrier_async_f() (which isn't a library
151// function but is exported) and are thus supported:
152//   dispatch_source_set_cancel_handler_f()
153//   dispatch_source_set_cancel_handler()
154//   dispatch_source_set_event_handler_f()
155//   dispatch_source_set_event_handler()
156//
157// The reference manual for Grand Central Dispatch is available at
158//   http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
159// The implementation details are at
160//   http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
161
162typedef void* dispatch_group_t;
163typedef void* dispatch_queue_t;
164typedef void* dispatch_source_t;
165typedef u64 dispatch_time_t;
166typedef void (*dispatch_function_t)(void *block);
167typedef void* (*worker_t)(void *block);
168
169// A wrapper for the ObjC blocks used to support libdispatch.
170typedef struct {
171  void *block;
172  dispatch_function_t func;
173  u32 parent_tid;
174} asan_block_context_t;
175
176ALWAYS_INLINE
177void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
178  AsanThread *t = GetCurrentThread();
179  if (!t) {
180    t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
181                           parent_tid, stack, /* detached */ true);
182    t->Init();
183    asanThreadRegistry().StartThread(t->tid(), GetTid(),
184                                     /* workerthread */ true, 0);
185    SetCurrentThread(t);
186  }
187}
188
189// For use by only those functions that allocated the context via
190// alloc_asan_context().
191extern "C"
192void asan_dispatch_call_block_and_release(void *block) {
193  GET_STACK_TRACE_THREAD;
194  asan_block_context_t *context = (asan_block_context_t*)block;
195  VReport(2,
196          "asan_dispatch_call_block_and_release(): "
197          "context: %p, pthread_self: %p\n",
198          block, pthread_self());
199  asan_register_worker_thread(context->parent_tid, &stack);
200  // Call the original dispatcher for the block.
201  context->func(context->block);
202  asan_free(context, &stack, FROM_MALLOC);
203}
204
205}  // namespace __asan
206
207using namespace __asan;  // NOLINT
208
209// Wrap |ctxt| and |func| into an asan_block_context_t.
210// The caller retains control of the allocated context.
211extern "C"
212asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
213                                         BufferedStackTrace *stack) {
214  asan_block_context_t *asan_ctxt =
215      (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
216  asan_ctxt->block = ctxt;
217  asan_ctxt->func = func;
218  asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
219  return asan_ctxt;
220}
221
222// Define interceptor for dispatch_*_f function with the three most common
223// parameters: dispatch_queue_t, context, dispatch_function_t.
224#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f)                                \
225  INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt,            \
226                                  dispatch_function_t func) {                 \
227    GET_STACK_TRACE_THREAD;                                                   \
228    asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
229    if (Verbosity() >= 2) {                                     \
230      Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n",             \
231             asan_ctxt, pthread_self());                                      \
232      PRINT_CURRENT_STACK();                                                  \
233    }                                                                         \
234    return REAL(dispatch_x_f)(dq, (void*)asan_ctxt,                           \
235                              asan_dispatch_call_block_and_release);          \
236  }
237
238INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
239INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
240INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
241
242INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
243                                    dispatch_queue_t dq, void *ctxt,
244                                    dispatch_function_t func) {
245  GET_STACK_TRACE_THREAD;
246  asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
247  if (Verbosity() >= 2) {
248    Report("dispatch_after_f: %p\n", asan_ctxt);
249    PRINT_CURRENT_STACK();
250  }
251  return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
252                                asan_dispatch_call_block_and_release);
253}
254
255INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
256                                          dispatch_queue_t dq, void *ctxt,
257                                          dispatch_function_t func) {
258  GET_STACK_TRACE_THREAD;
259  asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
260  if (Verbosity() >= 2) {
261    Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
262           asan_ctxt, pthread_self());
263    PRINT_CURRENT_STACK();
264  }
265  REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
266                               asan_dispatch_call_block_and_release);
267}
268
269#if !defined(MISSING_BLOCKS_SUPPORT)
270extern "C" {
271void dispatch_async(dispatch_queue_t dq, void(^work)(void));
272void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
273                          void(^work)(void));
274void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
275                    void(^work)(void));
276void dispatch_source_set_cancel_handler(dispatch_source_t ds,
277                                        void(^work)(void));
278void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
279}
280
281#define GET_ASAN_BLOCK(work) \
282  void (^asan_block)(void);  \
283  int parent_tid = GetCurrentTidOrInvalid(); \
284  asan_block = ^(void) { \
285    GET_STACK_TRACE_THREAD; \
286    asan_register_worker_thread(parent_tid, &stack); \
287    work(); \
288  }
289
290INTERCEPTOR(void, dispatch_async,
291            dispatch_queue_t dq, void(^work)(void)) {
292  ENABLE_FRAME_POINTER;
293  GET_ASAN_BLOCK(work);
294  REAL(dispatch_async)(dq, asan_block);
295}
296
297INTERCEPTOR(void, dispatch_group_async,
298            dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
299  ENABLE_FRAME_POINTER;
300  GET_ASAN_BLOCK(work);
301  REAL(dispatch_group_async)(dg, dq, asan_block);
302}
303
304INTERCEPTOR(void, dispatch_after,
305            dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
306  ENABLE_FRAME_POINTER;
307  GET_ASAN_BLOCK(work);
308  REAL(dispatch_after)(when, queue, asan_block);
309}
310
311INTERCEPTOR(void, dispatch_source_set_cancel_handler,
312            dispatch_source_t ds, void(^work)(void)) {
313  if (!work) {
314    REAL(dispatch_source_set_cancel_handler)(ds, work);
315    return;
316  }
317  ENABLE_FRAME_POINTER;
318  GET_ASAN_BLOCK(work);
319  REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
320}
321
322INTERCEPTOR(void, dispatch_source_set_event_handler,
323            dispatch_source_t ds, void(^work)(void)) {
324  ENABLE_FRAME_POINTER;
325  GET_ASAN_BLOCK(work);
326  REAL(dispatch_source_set_event_handler)(ds, asan_block);
327}
328#endif
329
330#endif  // SANITIZER_MAC
331