Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
      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 contains Mac-specific malloc interceptors and a custom zone
     11 // implementation, which together replace the system allocator.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "sanitizer_common/sanitizer_platform.h"
     16 #if !SANITIZER_MAC
     17 #error "This file should only be compiled on Darwin."
     18 #endif
     19 
     20 #include <AvailabilityMacros.h>
     21 #include <CoreFoundation/CFBase.h>
     22 #include <dlfcn.h>
     23 #include <malloc/malloc.h>
     24 #include <sys/mman.h>
     25 
     26 #include "interception/interception.h"
     27 #include "sanitizer_common/sanitizer_mac.h"
     28 
     29 // Similar code is used in Google Perftools,
     30 // https://github.com/gperftools/gperftools.
     31 
     32 static malloc_zone_t sanitizer_zone;
     33 
     34 INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
     35                              vm_size_t start_size, unsigned zone_flags) {
     36   COMMON_MALLOC_ENTER();
     37   uptr page_size = GetPageSizeCached();
     38   uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
     39   COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
     40   malloc_zone_t *new_zone = (malloc_zone_t *)p;
     41   internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
     42   new_zone->zone_name = NULL;  // The name will be changed anyway.
     43   if (GetMacosVersion() >= MACOS_VERSION_LION) {
     44     // Prevent the client app from overwriting the zone contents.
     45     // Library functions that need to modify the zone will set PROT_WRITE on it.
     46     // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
     47     mprotect(new_zone, allocated_size, PROT_READ);
     48   }
     49   return new_zone;
     50 }
     51 
     52 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
     53   COMMON_MALLOC_ENTER();
     54   return &sanitizer_zone;
     55 }
     56 
     57 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
     58   // FIXME: ASan should support purgeable allocations.
     59   // https://github.com/google/sanitizers/issues/139
     60   COMMON_MALLOC_ENTER();
     61   return &sanitizer_zone;
     62 }
     63 
     64 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
     65   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
     66   // for now.
     67   COMMON_MALLOC_ENTER();
     68 }
     69 
     70 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
     71   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
     72   // for now.
     73   COMMON_MALLOC_ENTER();
     74   // Must return 0 if the contents were not purged since the last call to
     75   // malloc_make_purgeable().
     76   return 0;
     77 }
     78 
     79 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
     80   COMMON_MALLOC_ENTER();
     81   // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
     82   // bytes.
     83   size_t buflen =
     84       sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
     85   InternalScopedString new_name(buflen);
     86   if (name && zone->introspect == sanitizer_zone.introspect) {
     87     new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
     88     name = new_name.data();
     89   }
     90 
     91   // Call the system malloc's implementation for both external and our zones,
     92   // since that appropriately changes VM region protections on the zone.
     93   REAL(malloc_set_zone_name)(zone, name);
     94 }
     95 
     96 INTERCEPTOR(void *, malloc, size_t size) {
     97   COMMON_MALLOC_ENTER();
     98   COMMON_MALLOC_MALLOC(size);
     99   return p;
    100 }
    101 
    102 INTERCEPTOR(void, free, void *ptr) {
    103   COMMON_MALLOC_ENTER();
    104   if (!ptr) return;
    105   COMMON_MALLOC_FREE(ptr);
    106 }
    107 
    108 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
    109   COMMON_MALLOC_ENTER();
    110   COMMON_MALLOC_REALLOC(ptr, size);
    111   return p;
    112 }
    113 
    114 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
    115   COMMON_MALLOC_ENTER();
    116   COMMON_MALLOC_CALLOC(nmemb, size);
    117   return p;
    118 }
    119 
    120 INTERCEPTOR(void *, valloc, size_t size) {
    121   COMMON_MALLOC_ENTER();
    122   COMMON_MALLOC_VALLOC(size);
    123   return p;
    124 }
    125 
    126 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
    127   COMMON_MALLOC_ENTER();
    128   return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
    129 }
    130 
    131 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
    132   COMMON_MALLOC_ENTER();
    133   CHECK(memptr);
    134   COMMON_MALLOC_MEMALIGN(alignment, size);
    135   if (p) {
    136     *memptr = p;
    137     return 0;
    138   }
    139   return -1;
    140 }
    141 
    142 namespace {
    143 
    144 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
    145 // wrappers, as they are basically copied from there.
    146 extern "C"
    147 SANITIZER_INTERFACE_ATTRIBUTE
    148 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
    149   COMMON_MALLOC_SIZE(ptr);
    150   return size;
    151 }
    152 
    153 extern "C"
    154 SANITIZER_INTERFACE_ATTRIBUTE
    155 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
    156   COMMON_MALLOC_ENTER();
    157   COMMON_MALLOC_MALLOC(size);
    158   return p;
    159 }
    160 
    161 extern "C"
    162 SANITIZER_INTERFACE_ATTRIBUTE
    163 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
    164   if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
    165     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
    166     const size_t kCallocPoolSize = 1024;
    167     static uptr calloc_memory_for_dlsym[kCallocPoolSize];
    168     static size_t allocated;
    169     size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
    170     void *mem = (void*)&calloc_memory_for_dlsym[allocated];
    171     allocated += size_in_words;
    172     CHECK(allocated < kCallocPoolSize);
    173     return mem;
    174   }
    175   COMMON_MALLOC_CALLOC(nmemb, size);
    176   return p;
    177 }
    178 
    179 extern "C"
    180 SANITIZER_INTERFACE_ATTRIBUTE
    181 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
    182   COMMON_MALLOC_ENTER();
    183   COMMON_MALLOC_VALLOC(size);
    184   return p;
    185 }
    186 
    187 // TODO(glider): the allocation callbacks need to be refactored.
    188 extern "C"
    189 SANITIZER_INTERFACE_ATTRIBUTE
    190 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
    191   if (!ptr) return;
    192   COMMON_MALLOC_FREE(ptr);
    193 }
    194 
    195 #define GET_ZONE_FOR_PTR(ptr) \
    196   malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
    197   const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
    198 
    199 extern "C"
    200 SANITIZER_INTERFACE_ATTRIBUTE
    201 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
    202   if (!ptr) {
    203     COMMON_MALLOC_MALLOC(new_size);
    204     return p;
    205   } else {
    206     COMMON_MALLOC_SIZE(ptr);
    207     if (size) {
    208       COMMON_MALLOC_REALLOC(ptr, new_size);
    209       return p;
    210     } else {
    211       // We can't recover from reallocating an unknown address, because
    212       // this would require reading at most |new_size| bytes from
    213       // potentially unaccessible memory.
    214       GET_ZONE_FOR_PTR(ptr);
    215       COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
    216       return nullptr;
    217     }
    218   }
    219 }
    220 
    221 extern "C"
    222 SANITIZER_INTERFACE_ATTRIBUTE
    223 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
    224   // A no-op -- we will not be destroyed!
    225   Report("__sanitizer_mz_destroy() called -- ignoring\n");
    226 }
    227 
    228 extern "C"
    229 SANITIZER_INTERFACE_ATTRIBUTE
    230 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
    231   COMMON_MALLOC_ENTER();
    232   COMMON_MALLOC_MEMALIGN(align, size);
    233   return p;
    234 }
    235 
    236 // This function is currently unused, and we build with -Werror.
    237 #if 0
    238 void __sanitizer_mz_free_definite_size(
    239     malloc_zone_t* zone, void *ptr, size_t size) {
    240   // TODO(glider): check that |size| is valid.
    241   UNIMPLEMENTED();
    242 }
    243 #endif
    244 
    245 kern_return_t mi_enumerator(task_t task, void *,
    246                             unsigned type_mask, vm_address_t zone_address,
    247                             memory_reader_t reader,
    248                             vm_range_recorder_t recorder) {
    249   // Should enumerate all the pointers we have.  Seems like a lot of work.
    250   return KERN_FAILURE;
    251 }
    252 
    253 size_t mi_good_size(malloc_zone_t *zone, size_t size) {
    254   // I think it's always safe to return size, but we maybe could do better.
    255   return size;
    256 }
    257 
    258 boolean_t mi_check(malloc_zone_t *zone) {
    259   UNIMPLEMENTED();
    260 }
    261 
    262 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
    263   UNIMPLEMENTED();
    264 }
    265 
    266 void mi_log(malloc_zone_t *zone, void *address) {
    267   // I don't think we support anything like this
    268 }
    269 
    270 void mi_force_lock(malloc_zone_t *zone) {
    271   COMMON_MALLOC_FORCE_LOCK();
    272 }
    273 
    274 void mi_force_unlock(malloc_zone_t *zone) {
    275   COMMON_MALLOC_FORCE_UNLOCK();
    276 }
    277 
    278 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
    279   COMMON_MALLOC_FILL_STATS(zone, stats);
    280 }
    281 
    282 boolean_t mi_zone_locked(malloc_zone_t *zone) {
    283   // UNIMPLEMENTED();
    284   return false;
    285 }
    286 
    287 }  // unnamed namespace
    288 
    289 namespace COMMON_MALLOC_NAMESPACE {
    290 
    291 void ReplaceSystemMalloc() {
    292   static malloc_introspection_t sanitizer_zone_introspection;
    293   // Ok to use internal_memset, these places are not performance-critical.
    294   internal_memset(&sanitizer_zone_introspection, 0,
    295                   sizeof(sanitizer_zone_introspection));
    296 
    297   sanitizer_zone_introspection.enumerator = &mi_enumerator;
    298   sanitizer_zone_introspection.good_size = &mi_good_size;
    299   sanitizer_zone_introspection.check = &mi_check;
    300   sanitizer_zone_introspection.print = &mi_print;
    301   sanitizer_zone_introspection.log = &mi_log;
    302   sanitizer_zone_introspection.force_lock = &mi_force_lock;
    303   sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
    304   sanitizer_zone_introspection.statistics = &mi_statistics;
    305   sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
    306 
    307   internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
    308 
    309   // Use version 6 for OSX >= 10.6.
    310   sanitizer_zone.version = 6;
    311   sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
    312   sanitizer_zone.size = &__sanitizer_mz_size;
    313   sanitizer_zone.malloc = &__sanitizer_mz_malloc;
    314   sanitizer_zone.calloc = &__sanitizer_mz_calloc;
    315   sanitizer_zone.valloc = &__sanitizer_mz_valloc;
    316   sanitizer_zone.free = &__sanitizer_mz_free;
    317   sanitizer_zone.realloc = &__sanitizer_mz_realloc;
    318   sanitizer_zone.destroy = &__sanitizer_mz_destroy;
    319   sanitizer_zone.batch_malloc = 0;
    320   sanitizer_zone.batch_free = 0;
    321   sanitizer_zone.free_definite_size = 0;
    322   sanitizer_zone.memalign = &__sanitizer_mz_memalign;
    323   sanitizer_zone.introspect = &sanitizer_zone_introspection;
    324 
    325   // Register the zone.
    326   malloc_zone_register(&sanitizer_zone);
    327 }
    328 
    329 }  // namespace COMMON_MALLOC_NAMESPACE
    330