brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.2 KiB · be27584 Raw
431 lines · plain
1//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file contains Mac-specific malloc interceptors and a custom zone10// implementation, which together replace the system allocator.11//12//===----------------------------------------------------------------------===//13 14#include "sanitizer_common/sanitizer_platform.h"15#if !SANITIZER_APPLE16#error "This file should only be compiled on Darwin."17#endif18 19#include <AvailabilityMacros.h>20#include <CoreFoundation/CFBase.h>21#include <dlfcn.h>22#include <malloc/malloc.h>23#include <sys/mman.h>24 25#include "interception/interception.h"26#include "sanitizer_common/sanitizer_allocator_dlsym.h"27#include "sanitizer_common/sanitizer_mac.h"28 29// Similar code is used in Google Perftools,30// https://github.com/gperftools/gperftools.31 32namespace __sanitizer {33 34extern malloc_zone_t sanitizer_zone;35 36struct sanitizer_malloc_introspection_t : public malloc_introspection_t {37  // IMPORTANT: Do not change the order, alignment, or types of these fields to38  // maintain binary compatibility. You should only add fields to this struct.39 40  // Used to track changes to the allocator that will affect41  // zone enumeration.42  u64 allocator_enumeration_version;43  uptr allocator_ptr;44  uptr allocator_size;45};46 47u64 GetMallocZoneAllocatorEnumerationVersion() {48  // This represents the current allocator ABI version.49  // This field should be incremented every time the Allocator50  // ABI changes in a way that breaks allocator enumeration.51  return 0;52}53 54}  // namespace __sanitizer55 56INTERCEPTOR(malloc_zone_t *, malloc_create_zone,57                             vm_size_t start_size, unsigned zone_flags) {58  COMMON_MALLOC_ENTER();59  uptr page_size = GetPageSizeCached();60  uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);61  COMMON_MALLOC_MEMALIGN(page_size, allocated_size);62  malloc_zone_t *new_zone = (malloc_zone_t *)p;63  internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));64  new_zone->zone_name = NULL;  // The name will be changed anyway.65  // Prevent the client app from overwriting the zone contents.66  // Library functions that need to modify the zone will set PROT_WRITE on it.67  // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.68  mprotect(new_zone, allocated_size, PROT_READ);69  // We're explicitly *NOT* registering the zone.70  return new_zone;71}72 73INTERCEPTOR(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 we76  // don't to unregister.  Just un-mprotect and free() the zone.77  uptr page_size = GetPageSizeCached();78  uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);79  mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);80  if (zone->zone_name) {81    COMMON_MALLOC_FREE((void *)zone->zone_name);82  }83  COMMON_MALLOC_FREE(zone);84}85 86INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {87  COMMON_MALLOC_ENTER();88  return &sanitizer_zone;89}90 91INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {92  COMMON_MALLOC_ENTER();93  size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);94  if (size) { // Claimed by sanitizer zone?95    return &sanitizer_zone;96  }97  return REAL(malloc_zone_from_ptr)(ptr);98}99 100INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {101  // FIXME: ASan should support purgeable allocations.102  // https://github.com/google/sanitizers/issues/139103  COMMON_MALLOC_ENTER();104  return &sanitizer_zone;105}106 107INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {108  // FIXME: ASan should support purgeable allocations. Ignoring them is fine109  // for now.110  COMMON_MALLOC_ENTER();111}112 113INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {114  // FIXME: ASan should support purgeable allocations. Ignoring them is fine115  // for now.116  COMMON_MALLOC_ENTER();117  // Must return 0 if the contents were not purged since the last call to118  // malloc_make_purgeable().119  return 0;120}121 122INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {123  COMMON_MALLOC_ENTER();124  InternalScopedString new_name;125  if (name && zone->introspect == sanitizer_zone.introspect) {126    new_name.AppendF(COMMON_MALLOC_ZONE_NAME "-%s", name);127    name = new_name.data();128  }129 130  // Call the system malloc's implementation for both external and our zones,131  // since that appropriately changes VM region protections on the zone.132  REAL(malloc_set_zone_name)(zone, name);133}134 135INTERCEPTOR(void *, malloc, size_t size) {136  COMMON_MALLOC_ENTER();137  COMMON_MALLOC_MALLOC(size);138  return p;139}140 141INTERCEPTOR(void, free, void *ptr) {142  COMMON_MALLOC_ENTER();143  if (!ptr) return;144  COMMON_MALLOC_FREE(ptr);145}146 147#if SANITIZER_INTERCEPT_FREE_SIZED && defined(COMMON_MALLOC_FREE_SIZED)148INTERCEPTOR(void, free_sized, void *ptr, size_t size) {149  COMMON_MALLOC_ENTER();150  COMMON_MALLOC_FREE_SIZED(ptr, size);151}152#endif153 154#if SANITIZER_INTERCEPT_FREE_ALIGNED_SIZED && \155    defined(COMMON_MALLOC_FREE_ALIGNED_SIZED)156INTERCEPTOR(void, free_aligned_sized, void *ptr, size_t alignment,157            size_t size) {158  COMMON_MALLOC_ENTER();159  COMMON_MALLOC_FREE_ALIGNED_SIZED(ptr, alignment, size);160}161#endif162 163INTERCEPTOR(void *, realloc, void *ptr, size_t size) {164  COMMON_MALLOC_ENTER();165  COMMON_MALLOC_REALLOC(ptr, size);166  return p;167}168 169INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {170  COMMON_MALLOC_ENTER();171  COMMON_MALLOC_CALLOC(nmemb, size);172  return p;173}174 175INTERCEPTOR(void *, valloc, size_t size) {176  COMMON_MALLOC_ENTER();177  COMMON_MALLOC_VALLOC(size);178  return p;179}180 181INTERCEPTOR(size_t, malloc_good_size, size_t size) {182  COMMON_MALLOC_ENTER();183  return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);184}185 186INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {187  COMMON_MALLOC_ENTER();188  CHECK(memptr);189  COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);190  return res;191}192 193namespace {194 195// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux196// wrappers, as they are basically copied from there.197extern "C"198SANITIZER_INTERFACE_ATTRIBUTE199size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {200  COMMON_MALLOC_SIZE(ptr);201  return size;202}203 204extern "C"205SANITIZER_INTERFACE_ATTRIBUTE206void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {207  COMMON_MALLOC_ENTER();208  COMMON_MALLOC_MALLOC(size);209  return p;210}211 212struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {213  static bool UseImpl() { return !COMMON_MALLOC_SANITIZER_INITIALIZED; }214};215 216extern "C"217SANITIZER_INTERFACE_ATTRIBUTE218void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {219  if (DlsymAlloc::Use())220    return DlsymAlloc::Callocate(nmemb, size);221  COMMON_MALLOC_CALLOC(nmemb, size);222  return p;223}224 225extern "C"226SANITIZER_INTERFACE_ATTRIBUTE227void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {228  COMMON_MALLOC_ENTER();229  COMMON_MALLOC_VALLOC(size);230  return p;231}232 233// TODO(glider): the allocation callbacks need to be refactored.234extern "C"235SANITIZER_INTERFACE_ATTRIBUTE236void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {237  if (!ptr) return;238  if (DlsymAlloc::PointerIsMine(ptr))239    return DlsymAlloc::Free(ptr);240  COMMON_MALLOC_FREE(ptr);241}242 243#define GET_ZONE_FOR_PTR(ptr) \244  malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \245  const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name246 247extern "C"248SANITIZER_INTERFACE_ATTRIBUTE249void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {250  if (!ptr) {251    COMMON_MALLOC_MALLOC(new_size);252    return p;253  } else {254    COMMON_MALLOC_SIZE(ptr);255    if (size) {256      COMMON_MALLOC_REALLOC(ptr, new_size);257      return p;258    } else {259      // We can't recover from reallocating an unknown address, because260      // this would require reading at most |new_size| bytes from261      // potentially unaccessible memory.262      GET_ZONE_FOR_PTR(ptr);263      COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);264      return nullptr;265    }266  }267}268 269extern "C"270SANITIZER_INTERFACE_ATTRIBUTE271void __sanitizer_mz_destroy(malloc_zone_t* zone) {272  // A no-op -- we will not be destroyed!273  Report("__sanitizer_mz_destroy() called -- ignoring\n");274}275 276extern "C"277SANITIZER_INTERFACE_ATTRIBUTE278void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {279  COMMON_MALLOC_ENTER();280  COMMON_MALLOC_MEMALIGN(align, size);281  return p;282}283 284// This public API exists purely for testing purposes.285extern "C"286SANITIZER_INTERFACE_ATTRIBUTE287malloc_zone_t* __sanitizer_mz_default_zone() {288  return &sanitizer_zone;289}290 291// This function is currently unused, and we build with -Werror.292#if 0293void __sanitizer_mz_free_definite_size(294    malloc_zone_t* zone, void *ptr, size_t size) {295  // TODO(glider): check that |size| is valid.296  UNIMPLEMENTED();297}298#endif299 300#ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR301#error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"302#endif303static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||304                  (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,305              "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");306 307#if COMMON_MALLOC_HAS_ZONE_ENUMERATOR308// Forward declare and expect the implementation to provided by309// includer.310kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,311                            vm_address_t zone_address, memory_reader_t reader,312                            vm_range_recorder_t recorder);313#else314// Provide stub implementation that fails.315kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,316                            vm_address_t zone_address, memory_reader_t reader,317                            vm_range_recorder_t recorder) {318  // Not supported.319  return KERN_FAILURE;320}321#endif322 323#ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT324#error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"325#endif326static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||327                  (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,328              "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");329#if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT330// Forward declare and expect the implementation to provided by331// includer.332void mi_extra_init(333    sanitizer_malloc_introspection_t *mi);334#else335void mi_extra_init(336    sanitizer_malloc_introspection_t *mi) {337  // Just zero initialize the fields.338  mi->allocator_ptr = 0;339  mi->allocator_size = 0;340}341#endif342 343size_t mi_good_size(malloc_zone_t *zone, size_t size) {344  // I think it's always safe to return size, but we maybe could do better.345  return size;346}347 348boolean_t mi_check(malloc_zone_t *zone) {349  UNIMPLEMENTED();350}351 352void mi_print(malloc_zone_t *zone, boolean_t verbose) {353  UNIMPLEMENTED();354}355 356void mi_log(malloc_zone_t *zone, void *address) {357  // I don't think we support anything like this358}359 360void mi_force_lock(malloc_zone_t *zone) {361  COMMON_MALLOC_FORCE_LOCK();362}363 364void mi_force_unlock(malloc_zone_t *zone) {365  COMMON_MALLOC_FORCE_UNLOCK();366}367 368void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {369  COMMON_MALLOC_FILL_STATS(zone, stats);370}371 372boolean_t mi_zone_locked(malloc_zone_t *zone) {373  // UNIMPLEMENTED();374  return false;375}376 377}  // unnamed namespace378 379namespace COMMON_MALLOC_NAMESPACE {380 381void InitMallocZoneFields() {382  static sanitizer_malloc_introspection_t sanitizer_zone_introspection;383  // Ok to use internal_memset, these places are not performance-critical.384  internal_memset(&sanitizer_zone_introspection, 0,385                  sizeof(sanitizer_zone_introspection));386 387  sanitizer_zone_introspection.enumerator = &mi_enumerator;388  sanitizer_zone_introspection.good_size = &mi_good_size;389  sanitizer_zone_introspection.check = &mi_check;390  sanitizer_zone_introspection.print = &mi_print;391  sanitizer_zone_introspection.log = &mi_log;392  sanitizer_zone_introspection.force_lock = &mi_force_lock;393  sanitizer_zone_introspection.force_unlock = &mi_force_unlock;394  sanitizer_zone_introspection.statistics = &mi_statistics;395  sanitizer_zone_introspection.zone_locked = &mi_zone_locked;396 397  // Set current allocator enumeration version.398  sanitizer_zone_introspection.allocator_enumeration_version =399      GetMallocZoneAllocatorEnumerationVersion();400 401  // Perform any sanitizer specific initialization.402  mi_extra_init(&sanitizer_zone_introspection);403 404  internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));405 406  // Use version 6 for OSX >= 10.6.407  sanitizer_zone.version = 6;408  sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;409  sanitizer_zone.size = &__sanitizer_mz_size;410  sanitizer_zone.malloc = &__sanitizer_mz_malloc;411  sanitizer_zone.calloc = &__sanitizer_mz_calloc;412  sanitizer_zone.valloc = &__sanitizer_mz_valloc;413  sanitizer_zone.free = &__sanitizer_mz_free;414  sanitizer_zone.realloc = &__sanitizer_mz_realloc;415  sanitizer_zone.destroy = &__sanitizer_mz_destroy;416  sanitizer_zone.batch_malloc = 0;417  sanitizer_zone.batch_free = 0;418  sanitizer_zone.free_definite_size = 0;419  sanitizer_zone.memalign = &__sanitizer_mz_memalign;420  sanitizer_zone.introspect = &sanitizer_zone_introspection;421}422 423void ReplaceSystemMalloc() {424  InitMallocZoneFields();425 426  // Register the zone.427  malloc_zone_register(&sanitizer_zone);428}429 430}  // namespace COMMON_MALLOC_NAMESPACE431