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