Home | History | Annotate | Download | only in asan
      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 #ifdef __APPLE__
     16 
     17 #include "asan_interceptors.h"
     18 #include "asan_internal.h"
     19 #include "asan_mac.h"
     20 #include "asan_mapping.h"
     21 #include "asan_stack.h"
     22 #include "asan_thread.h"
     23 #include "asan_thread_registry.h"
     24 #include "sanitizer_common/sanitizer_libc.h"
     25 
     26 #include <crt_externs.h>  // for _NSGetArgv
     27 #include <dlfcn.h>  // for dladdr()
     28 #include <mach-o/dyld.h>
     29 #include <mach-o/loader.h>
     30 #include <sys/mman.h>
     31 #include <sys/resource.h>
     32 #include <sys/sysctl.h>
     33 #include <sys/ucontext.h>
     34 #include <fcntl.h>
     35 #include <pthread.h>
     36 #include <stdlib.h>  // for free()
     37 #include <unistd.h>
     38 #include <libkern/OSAtomic.h>
     39 #include <CoreFoundation/CFString.h>
     40 
     41 namespace __asan {
     42 
     43 void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
     44   ucontext_t *ucontext = (ucontext_t*)context;
     45 # if __WORDSIZE == 64
     46   *pc = ucontext->uc_mcontext->__ss.__rip;
     47   *bp = ucontext->uc_mcontext->__ss.__rbp;
     48   *sp = ucontext->uc_mcontext->__ss.__rsp;
     49 # else
     50   *pc = ucontext->uc_mcontext->__ss.__eip;
     51   *bp = ucontext->uc_mcontext->__ss.__ebp;
     52   *sp = ucontext->uc_mcontext->__ss.__esp;
     53 # endif  // __WORDSIZE
     54 }
     55 
     56 int GetMacosVersion() {
     57   int mib[2] = { CTL_KERN, KERN_OSRELEASE };
     58   char version[100];
     59   uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
     60   for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
     61   // Get the version length.
     62   CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1);
     63   CHECK(len < maxlen);
     64   CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1);
     65   switch (version[0]) {
     66     case '9': return MACOS_VERSION_LEOPARD;
     67     case '1': {
     68       switch (version[1]) {
     69         case '0': return MACOS_VERSION_SNOW_LEOPARD;
     70         case '1': return MACOS_VERSION_LION;
     71         default: return MACOS_VERSION_UNKNOWN;
     72       }
     73     }
     74     default: return MACOS_VERSION_UNKNOWN;
     75   }
     76 }
     77 
     78 bool PlatformHasDifferentMemcpyAndMemmove() {
     79   // On OS X 10.7 memcpy() and memmove() are both resolved
     80   // into memmove$VARIANT$sse42.
     81   // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
     82   // TODO(glider): need to check dynamically that memcpy() and memmove() are
     83   // actually the same function.
     84   return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
     85 }
     86 
     87 extern "C"
     88 void __asan_init();
     89 
     90 static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
     91 
     92 void MaybeReexec() {
     93   if (!flags()->allow_reexec) return;
     94 #if MAC_INTERPOSE_FUNCTIONS
     95   // If the program is linked with the dynamic ASan runtime library, make sure
     96   // the library is preloaded so that the wrappers work. If it is not, set
     97   // DYLD_INSERT_LIBRARIES and re-exec ourselves.
     98   Dl_info info;
     99   CHECK(dladdr((void*)__asan_init, &info));
    100   const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
    101   if (!dyld_insert_libraries ||
    102       !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
    103     // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
    104     // library.
    105     char program_name[1024];
    106     uint32_t buf_size = sizeof(program_name);
    107     _NSGetExecutablePath(program_name, &buf_size);
    108     // Ok to use setenv() since the wrappers don't depend on the value of
    109     // asan_inited.
    110     setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
    111     if (flags()->verbosity >= 1) {
    112       Report("exec()-ing the program with\n");
    113       Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname);
    114       Report("to enable ASan wrappers.\n");
    115       Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
    116     }
    117     execv(program_name, *_NSGetArgv());
    118   }
    119 #endif  // MAC_INTERPOSE_FUNCTIONS
    120   // If we're not using the dynamic runtime, do nothing.
    121 }
    122 
    123 // No-op. Mac does not support static linkage anyway.
    124 void *AsanDoesNotSupportStaticLinkage() {
    125   return 0;
    126 }
    127 
    128 bool AsanInterceptsSignal(int signum) {
    129   return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv;
    130 }
    131 
    132 void AsanPlatformThreadInit() {
    133   ReplaceCFAllocator();
    134 }
    135 
    136 AsanLock::AsanLock(LinkerInitialized) {
    137   // We assume that OS_SPINLOCK_INIT is zero
    138 }
    139 
    140 void AsanLock::Lock() {
    141   CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
    142   CHECK(OS_SPINLOCK_INIT == 0);
    143   CHECK(owner_ != (uptr)pthread_self());
    144   OSSpinLockLock((OSSpinLock*)&opaque_storage_);
    145   CHECK(!owner_);
    146   owner_ = (uptr)pthread_self();
    147 }
    148 
    149 void AsanLock::Unlock() {
    150   CHECK(owner_ == (uptr)pthread_self());
    151   owner_ = 0;
    152   OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
    153 }
    154 
    155 void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) {
    156   stack->size = 0;
    157   stack->trace[0] = pc;
    158   if ((max_s) > 1) {
    159     stack->max_size = max_s;
    160     if (!asan_inited) return;
    161     if (AsanThread *t = asanThreadRegistry().GetCurrent())
    162       stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
    163   }
    164 }
    165 
    166 // The range of pages to be used for escape islands.
    167 // TODO(glider): instead of mapping a fixed range we must find a range of
    168 // unmapped pages in vmmap and take them.
    169 // These constants were chosen empirically and may not work if the shadow
    170 // memory layout changes. Unfortunately they do necessarily depend on
    171 // kHighMemBeg or kHighMemEnd.
    172 static void *island_allocator_pos = 0;
    173 
    174 #if __WORDSIZE == 32
    175 # define kIslandEnd (0xffdf0000 - kPageSize)
    176 # define kIslandBeg (kIslandEnd - 256 * kPageSize)
    177 #else
    178 # define kIslandEnd (0x7fffffdf0000 - kPageSize)
    179 # define kIslandBeg (kIslandEnd - 256 * kPageSize)
    180 #endif
    181 
    182 extern "C"
    183 mach_error_t __interception_allocate_island(void **ptr,
    184                                             uptr unused_size,
    185                                             void *unused_hint) {
    186   if (!island_allocator_pos) {
    187     island_allocator_pos =
    188         internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg,
    189                       PROT_READ | PROT_WRITE | PROT_EXEC,
    190                       MAP_PRIVATE | MAP_ANON | MAP_FIXED,
    191                       -1, 0);
    192     if (island_allocator_pos != (void*)kIslandBeg) {
    193       return KERN_NO_SPACE;
    194     }
    195     if (flags()->verbosity) {
    196       Report("Mapped pages %p--%p for branch islands.\n",
    197              (void*)kIslandBeg, (void*)kIslandEnd);
    198     }
    199     // Should not be very performance-critical.
    200     internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
    201   };
    202   *ptr = island_allocator_pos;
    203   island_allocator_pos = (char*)island_allocator_pos + kPageSize;
    204   if (flags()->verbosity) {
    205     Report("Branch island allocated at %p\n", *ptr);
    206   }
    207   return err_none;
    208 }
    209 
    210 extern "C"
    211 mach_error_t __interception_deallocate_island(void *ptr) {
    212   // Do nothing.
    213   // TODO(glider): allow to free and reuse the island memory.
    214   return err_none;
    215 }
    216 
    217 // Support for the following functions from libdispatch on Mac OS:
    218 //   dispatch_async_f()
    219 //   dispatch_async()
    220 //   dispatch_sync_f()
    221 //   dispatch_sync()
    222 //   dispatch_after_f()
    223 //   dispatch_after()
    224 //   dispatch_group_async_f()
    225 //   dispatch_group_async()
    226 // TODO(glider): libdispatch API contains other functions that we don't support
    227 // yet.
    228 //
    229 // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
    230 // they can cause jobs to run on a thread different from the current one.
    231 // TODO(glider): if so, we need a test for this (otherwise we should remove
    232 // them).
    233 //
    234 // The following functions use dispatch_barrier_async_f() (which isn't a library
    235 // function but is exported) and are thus supported:
    236 //   dispatch_source_set_cancel_handler_f()
    237 //   dispatch_source_set_cancel_handler()
    238 //   dispatch_source_set_event_handler_f()
    239 //   dispatch_source_set_event_handler()
    240 //
    241 // The reference manual for Grand Central Dispatch is available at
    242 //   http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
    243 // The implementation details are at
    244 //   http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
    245 
    246 typedef void* pthread_workqueue_t;
    247 typedef void* pthread_workitem_handle_t;
    248 
    249 typedef void* dispatch_group_t;
    250 typedef void* dispatch_queue_t;
    251 typedef void* dispatch_source_t;
    252 typedef u64 dispatch_time_t;
    253 typedef void (*dispatch_function_t)(void *block);
    254 typedef void* (*worker_t)(void *block);
    255 
    256 // A wrapper for the ObjC blocks used to support libdispatch.
    257 typedef struct {
    258   void *block;
    259   dispatch_function_t func;
    260   u32 parent_tid;
    261 } asan_block_context_t;
    262 
    263 // We use extern declarations of libdispatch functions here instead
    264 // of including <dispatch/dispatch.h>. This header is not present on
    265 // Mac OS X Leopard and eariler, and although we don't expect ASan to
    266 // work on legacy systems, it's bad to break the build of
    267 // LLVM compiler-rt there.
    268 extern "C" {
    269 void dispatch_async_f(dispatch_queue_t dq, void *ctxt,
    270                       dispatch_function_t func);
    271 void dispatch_sync_f(dispatch_queue_t dq, void *ctxt,
    272                      dispatch_function_t func);
    273 void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt,
    274                       dispatch_function_t func);
    275 void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
    276                               dispatch_function_t func);
    277 void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq,
    278                             void *ctxt, dispatch_function_t func);
    279 int pthread_workqueue_additem_np(pthread_workqueue_t workq,
    280     void *(*workitem_func)(void *), void * workitem_arg,
    281     pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
    282 }  // extern "C"
    283 
    284 static ALWAYS_INLINE
    285 void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
    286   AsanThread *t = asanThreadRegistry().GetCurrent();
    287   if (!t) {
    288     t = AsanThread::Create(parent_tid, 0, 0, stack);
    289     asanThreadRegistry().RegisterThread(t);
    290     t->Init();
    291     asanThreadRegistry().SetCurrent(t);
    292   }
    293 }
    294 
    295 // For use by only those functions that allocated the context via
    296 // alloc_asan_context().
    297 extern "C"
    298 void asan_dispatch_call_block_and_release(void *block) {
    299   GET_STACK_TRACE_HERE(kStackTraceMax);
    300   asan_block_context_t *context = (asan_block_context_t*)block;
    301   if (flags()->verbosity >= 2) {
    302     Report("asan_dispatch_call_block_and_release(): "
    303            "context: %p, pthread_self: %p\n",
    304            block, pthread_self());
    305   }
    306   asan_register_worker_thread(context->parent_tid, &stack);
    307   // Call the original dispatcher for the block.
    308   context->func(context->block);
    309   asan_free(context, &stack);
    310 }
    311 
    312 }  // namespace __asan
    313 
    314 using namespace __asan;  // NOLINT
    315 
    316 // Wrap |ctxt| and |func| into an asan_block_context_t.
    317 // The caller retains control of the allocated context.
    318 extern "C"
    319 asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
    320                                          StackTrace *stack) {
    321   asan_block_context_t *asan_ctxt =
    322       (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
    323   asan_ctxt->block = ctxt;
    324   asan_ctxt->func = func;
    325   asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
    326   return asan_ctxt;
    327 }
    328 
    329 // Define interceptor for dispatch_*_f function with the three most common
    330 // parameters: dispatch_queue_t, context, dispatch_function_t.
    331 #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f)                                \
    332   INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt,            \
    333                                   dispatch_function_t func) {                 \
    334     GET_STACK_TRACE_HERE(kStackTraceMax);                                     \
    335     asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
    336     if (flags()->verbosity >= 2) {                                            \
    337       Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n",             \
    338              asan_ctxt, pthread_self());                                      \
    339        PRINT_CURRENT_STACK();                                                 \
    340      }                                                                        \
    341      return REAL(dispatch_x_f)(dq, (void*)asan_ctxt,                          \
    342                                asan_dispatch_call_block_and_release);         \
    343   }
    344 
    345 INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
    346 INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
    347 INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
    348 
    349 INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
    350                                     dispatch_queue_t dq, void *ctxt,
    351                                     dispatch_function_t func) {
    352   GET_STACK_TRACE_HERE(kStackTraceMax);
    353   asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
    354   if (flags()->verbosity >= 2) {
    355     Report("dispatch_after_f: %p\n", asan_ctxt);
    356     PRINT_CURRENT_STACK();
    357   }
    358   return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
    359                                 asan_dispatch_call_block_and_release);
    360 }
    361 
    362 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
    363                                           dispatch_queue_t dq, void *ctxt,
    364                                           dispatch_function_t func) {
    365   GET_STACK_TRACE_HERE(kStackTraceMax);
    366   asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
    367   if (flags()->verbosity >= 2) {
    368     Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
    369            asan_ctxt, pthread_self());
    370     PRINT_CURRENT_STACK();
    371   }
    372   REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
    373                                asan_dispatch_call_block_and_release);
    374 }
    375 
    376 #if MAC_INTERPOSE_FUNCTIONS
    377 // dispatch_async, dispatch_group_async and others tailcall the corresponding
    378 // dispatch_*_f functions. When wrapping functions with mach_override, those
    379 // dispatch_*_f are intercepted automatically. But with dylib interposition
    380 // this does not work, because the calls within the same library are not
    381 // interposed.
    382 // Therefore we need to re-implement dispatch_async and friends.
    383 
    384 extern "C" {
    385 // FIXME: consolidate these declarations with asan_intercepted_functions.h.
    386 void dispatch_async(dispatch_queue_t dq, void(^work)(void));
    387 void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
    388                           void(^work)(void));
    389 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
    390                     void(^work)(void));
    391 void dispatch_source_set_cancel_handler(dispatch_source_t ds,
    392                                         void(^work)(void));
    393 void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
    394 }
    395 
    396 #define GET_ASAN_BLOCK(work) \
    397   void (^asan_block)(void);  \
    398   int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
    399   asan_block = ^(void) { \
    400     GET_STACK_TRACE_HERE(kStackTraceMax); \
    401     asan_register_worker_thread(parent_tid, &stack); \
    402     work(); \
    403   }
    404 
    405 INTERCEPTOR(void, dispatch_async,
    406             dispatch_queue_t dq, void(^work)(void)) {
    407   GET_ASAN_BLOCK(work);
    408   REAL(dispatch_async)(dq, asan_block);
    409 }
    410 
    411 INTERCEPTOR(void, dispatch_group_async,
    412             dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
    413   GET_ASAN_BLOCK(work);
    414   REAL(dispatch_group_async)(dg, dq, asan_block);
    415 }
    416 
    417 INTERCEPTOR(void, dispatch_after,
    418             dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
    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)) {
    425   GET_ASAN_BLOCK(work);
    426   REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
    427 }
    428 
    429 INTERCEPTOR(void, dispatch_source_set_event_handler,
    430             dispatch_source_t ds, void(^work)(void)) {
    431   GET_ASAN_BLOCK(work);
    432   REAL(dispatch_source_set_event_handler)(ds, asan_block);
    433 }
    434 #endif
    435 
    436 // The following stuff has been extremely helpful while looking for the
    437 // unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
    438 // level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
    439 // find the points of worker thread creation (each of such threads may be used
    440 // to run several tasks, that's why this is not enough to support the whole
    441 // libdispatch API.
    442 extern "C"
    443 void *wrap_workitem_func(void *arg) {
    444   if (flags()->verbosity >= 2) {
    445     Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
    446   }
    447   asan_block_context_t *ctxt = (asan_block_context_t*)arg;
    448   worker_t fn = (worker_t)(ctxt->func);
    449   void *result =  fn(ctxt->block);
    450   GET_STACK_TRACE_HERE(kStackTraceMax);
    451   asan_free(arg, &stack);
    452   return result;
    453 }
    454 
    455 INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq,
    456     void *(*workitem_func)(void *), void * workitem_arg,
    457     pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
    458   GET_STACK_TRACE_HERE(kStackTraceMax);
    459   asan_block_context_t *asan_ctxt =
    460       (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
    461   asan_ctxt->block = workitem_arg;
    462   asan_ctxt->func = (dispatch_function_t)workitem_func;
    463   asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
    464   if (flags()->verbosity >= 2) {
    465     Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
    466     PRINT_CURRENT_STACK();
    467   }
    468   return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func,
    469                                             asan_ctxt, itemhandlep,
    470                                             gencountp);
    471 }
    472 
    473 // See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
    474 int __CFStrIsConstant(CFStringRef str) {
    475   CFRuntimeBase *base = (CFRuntimeBase*)str;
    476 #if __LP64__
    477   return base->_rc == 0;
    478 #else
    479   return (base->_cfinfo[CF_RC_BITS]) == 0;
    480 #endif
    481 }
    482 
    483 INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
    484                                              CFStringRef str) {
    485   if (__CFStrIsConstant(str)) {
    486     return str;
    487   } else {
    488     return REAL(CFStringCreateCopy)(alloc, str);
    489   }
    490 }
    491 
    492 DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
    493 
    494 DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize)
    495 
    496 namespace __asan {
    497 
    498 void InitializeMacInterceptors() {
    499   CHECK(INTERCEPT_FUNCTION(dispatch_async_f));
    500   CHECK(INTERCEPT_FUNCTION(dispatch_sync_f));
    501   CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
    502   CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
    503   CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
    504   // We don't need to intercept pthread_workqueue_additem_np() to support the
    505   // libdispatch API, but it helps us to debug the unsupported functions. Let's
    506   // intercept it only during verbose runs.
    507   if (flags()->verbosity >= 2) {
    508     CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np));
    509   }
    510   // Normally CFStringCreateCopy should not copy constant CF strings.
    511   // Replacing the default CFAllocator causes constant strings to be copied
    512   // rather than just returned, which leads to bugs in big applications like
    513   // Chromium and WebKit, see
    514   // http://code.google.com/p/address-sanitizer/issues/detail?id=10
    515   // Until this problem is fixed we need to check that the string is
    516   // non-constant before calling CFStringCreateCopy.
    517   CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy));
    518   // Some of the library functions call free() directly, so we have to
    519   // intercept it.
    520   CHECK(INTERCEPT_FUNCTION(free));
    521   if (flags()->replace_cfallocator) {
    522     CHECK(INTERCEPT_FUNCTION(__CFInitialize));
    523   }
    524 }
    525 
    526 }  // namespace __asan
    527 
    528 #endif  // __APPLE__
    529