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