123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file contains Mac-specific malloc interceptors and a custom zone
- // implementation, which together replace the system allocator.
- //
- //===----------------------------------------------------------------------===//
- #include "sanitizer_common/sanitizer_platform.h"
- #if !SANITIZER_APPLE
- #error "This file should only be compiled on Darwin."
- #endif
- #include <AvailabilityMacros.h>
- #include <CoreFoundation/CFBase.h>
- #include <dlfcn.h>
- #include <malloc/malloc.h>
- #include <sys/mman.h>
- #include "interception/interception.h"
- #include "sanitizer_common/sanitizer_allocator_dlsym.h"
- #include "sanitizer_common/sanitizer_mac.h"
- // Similar code is used in Google Perftools,
- // https://github.com/gperftools/gperftools.
- namespace __sanitizer {
- extern malloc_zone_t sanitizer_zone;
- struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
- // IMPORTANT: Do not change the order, alignment, or types of these fields to
- // maintain binary compatibility. You should only add fields to this struct.
- // Used to track changes to the allocator that will affect
- // zone enumeration.
- u64 allocator_enumeration_version;
- uptr allocator_ptr;
- uptr allocator_size;
- };
- u64 GetMallocZoneAllocatorEnumerationVersion() {
- // This represents the current allocator ABI version.
- // This field should be incremented every time the Allocator
- // ABI changes in a way that breaks allocator enumeration.
- return 0;
- }
- } // namespace __sanitizer
- INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
- vm_size_t start_size, unsigned zone_flags) {
- COMMON_MALLOC_ENTER();
- uptr page_size = GetPageSizeCached();
- uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
- COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
- malloc_zone_t *new_zone = (malloc_zone_t *)p;
- internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
- new_zone->zone_name = NULL; // The name will be changed anyway.
- // Prevent the client app from overwriting the zone contents.
- // Library functions that need to modify the zone will set PROT_WRITE on it.
- // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
- mprotect(new_zone, allocated_size, PROT_READ);
- // We're explicitly *NOT* registering the zone.
- return new_zone;
- }
- INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
- COMMON_MALLOC_ENTER();
- // We don't need to do anything here. We're not registering new zones, so we
- // don't to unregister. Just un-mprotect and free() the zone.
- uptr page_size = GetPageSizeCached();
- uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
- mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
- if (zone->zone_name) {
- COMMON_MALLOC_FREE((void *)zone->zone_name);
- }
- COMMON_MALLOC_FREE(zone);
- }
- INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
- COMMON_MALLOC_ENTER();
- return &sanitizer_zone;
- }
- INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
- COMMON_MALLOC_ENTER();
- size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
- if (size) { // Claimed by sanitizer zone?
- return &sanitizer_zone;
- }
- return REAL(malloc_zone_from_ptr)(ptr);
- }
- INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
- // FIXME: ASan should support purgeable allocations.
- // https://github.com/google/sanitizers/issues/139
- COMMON_MALLOC_ENTER();
- return &sanitizer_zone;
- }
- INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
- // FIXME: ASan should support purgeable allocations. Ignoring them is fine
- // for now.
- COMMON_MALLOC_ENTER();
- }
- INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
- // FIXME: ASan should support purgeable allocations. Ignoring them is fine
- // for now.
- COMMON_MALLOC_ENTER();
- // Must return 0 if the contents were not purged since the last call to
- // malloc_make_purgeable().
- return 0;
- }
- INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
- COMMON_MALLOC_ENTER();
- InternalScopedString new_name;
- if (name && zone->introspect == sanitizer_zone.introspect) {
- new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
- name = new_name.data();
- }
- // Call the system malloc's implementation for both external and our zones,
- // since that appropriately changes VM region protections on the zone.
- REAL(malloc_set_zone_name)(zone, name);
- }
- INTERCEPTOR(void *, malloc, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_MALLOC(size);
- return p;
- }
- INTERCEPTOR(void, free, void *ptr) {
- COMMON_MALLOC_ENTER();
- if (!ptr) return;
- COMMON_MALLOC_FREE(ptr);
- }
- INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_REALLOC(ptr, size);
- return p;
- }
- INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_CALLOC(nmemb, size);
- return p;
- }
- INTERCEPTOR(void *, valloc, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_VALLOC(size);
- return p;
- }
- INTERCEPTOR(size_t, malloc_good_size, size_t size) {
- COMMON_MALLOC_ENTER();
- return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
- }
- INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
- COMMON_MALLOC_ENTER();
- CHECK(memptr);
- COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
- return res;
- }
- namespace {
- // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
- // wrappers, as they are basically copied from there.
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
- COMMON_MALLOC_SIZE(ptr);
- return size;
- }
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_MALLOC(size);
- return p;
- }
- struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
- static bool UseImpl() { return !COMMON_MALLOC_SANITIZER_INITIALIZED; }
- };
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
- if (DlsymAlloc::Use())
- return DlsymAlloc::Callocate(nmemb, size);
- COMMON_MALLOC_CALLOC(nmemb, size);
- return p;
- }
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_VALLOC(size);
- return p;
- }
- // TODO(glider): the allocation callbacks need to be refactored.
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
- if (!ptr) return;
- if (DlsymAlloc::PointerIsMine(ptr))
- return DlsymAlloc::Free(ptr);
- COMMON_MALLOC_FREE(ptr);
- }
- #define GET_ZONE_FOR_PTR(ptr) \
- malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
- const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
- if (!ptr) {
- COMMON_MALLOC_MALLOC(new_size);
- return p;
- } else {
- COMMON_MALLOC_SIZE(ptr);
- if (size) {
- COMMON_MALLOC_REALLOC(ptr, new_size);
- return p;
- } else {
- // We can't recover from reallocating an unknown address, because
- // this would require reading at most |new_size| bytes from
- // potentially unaccessible memory.
- GET_ZONE_FOR_PTR(ptr);
- COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
- return nullptr;
- }
- }
- }
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_mz_destroy(malloc_zone_t* zone) {
- // A no-op -- we will not be destroyed!
- Report("__sanitizer_mz_destroy() called -- ignoring\n");
- }
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
- COMMON_MALLOC_ENTER();
- COMMON_MALLOC_MEMALIGN(align, size);
- return p;
- }
- // This public API exists purely for testing purposes.
- extern "C"
- SANITIZER_INTERFACE_ATTRIBUTE
- malloc_zone_t* __sanitizer_mz_default_zone() {
- return &sanitizer_zone;
- }
- // This function is currently unused, and we build with -Werror.
- #if 0
- void __sanitizer_mz_free_definite_size(
- malloc_zone_t* zone, void *ptr, size_t size) {
- // TODO(glider): check that |size| is valid.
- UNIMPLEMENTED();
- }
- #endif
- #ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
- #error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
- #endif
- static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
- (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
- "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
- #if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
- // Forward declare and expect the implementation to provided by
- // includer.
- kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
- vm_address_t zone_address, memory_reader_t reader,
- vm_range_recorder_t recorder);
- #else
- // Provide stub implementation that fails.
- kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
- vm_address_t zone_address, memory_reader_t reader,
- vm_range_recorder_t recorder) {
- // Not supported.
- return KERN_FAILURE;
- }
- #endif
- #ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
- #error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
- #endif
- static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
- (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
- "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
- #if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
- // Forward declare and expect the implementation to provided by
- // includer.
- void mi_extra_init(
- sanitizer_malloc_introspection_t *mi);
- #else
- void mi_extra_init(
- sanitizer_malloc_introspection_t *mi) {
- // Just zero initialize the fields.
- mi->allocator_ptr = 0;
- mi->allocator_size = 0;
- }
- #endif
- size_t mi_good_size(malloc_zone_t *zone, size_t size) {
- // I think it's always safe to return size, but we maybe could do better.
- return size;
- }
- boolean_t mi_check(malloc_zone_t *zone) {
- UNIMPLEMENTED();
- }
- void mi_print(malloc_zone_t *zone, boolean_t verbose) {
- UNIMPLEMENTED();
- }
- void mi_log(malloc_zone_t *zone, void *address) {
- // I don't think we support anything like this
- }
- void mi_force_lock(malloc_zone_t *zone) {
- COMMON_MALLOC_FORCE_LOCK();
- }
- void mi_force_unlock(malloc_zone_t *zone) {
- COMMON_MALLOC_FORCE_UNLOCK();
- }
- void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
- COMMON_MALLOC_FILL_STATS(zone, stats);
- }
- boolean_t mi_zone_locked(malloc_zone_t *zone) {
- // UNIMPLEMENTED();
- return false;
- }
- } // unnamed namespace
- namespace COMMON_MALLOC_NAMESPACE {
- void InitMallocZoneFields() {
- static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
- // Ok to use internal_memset, these places are not performance-critical.
- internal_memset(&sanitizer_zone_introspection, 0,
- sizeof(sanitizer_zone_introspection));
- sanitizer_zone_introspection.enumerator = &mi_enumerator;
- sanitizer_zone_introspection.good_size = &mi_good_size;
- sanitizer_zone_introspection.check = &mi_check;
- sanitizer_zone_introspection.print = &mi_print;
- sanitizer_zone_introspection.log = &mi_log;
- sanitizer_zone_introspection.force_lock = &mi_force_lock;
- sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
- sanitizer_zone_introspection.statistics = &mi_statistics;
- sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
- // Set current allocator enumeration version.
- sanitizer_zone_introspection.allocator_enumeration_version =
- GetMallocZoneAllocatorEnumerationVersion();
- // Perform any sanitizer specific initialization.
- mi_extra_init(&sanitizer_zone_introspection);
- internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
- // Use version 6 for OSX >= 10.6.
- sanitizer_zone.version = 6;
- sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
- sanitizer_zone.size = &__sanitizer_mz_size;
- sanitizer_zone.malloc = &__sanitizer_mz_malloc;
- sanitizer_zone.calloc = &__sanitizer_mz_calloc;
- sanitizer_zone.valloc = &__sanitizer_mz_valloc;
- sanitizer_zone.free = &__sanitizer_mz_free;
- sanitizer_zone.realloc = &__sanitizer_mz_realloc;
- sanitizer_zone.destroy = &__sanitizer_mz_destroy;
- sanitizer_zone.batch_malloc = 0;
- sanitizer_zone.batch_free = 0;
- sanitizer_zone.free_definite_size = 0;
- sanitizer_zone.memalign = &__sanitizer_mz_memalign;
- sanitizer_zone.introspect = &sanitizer_zone_introspection;
- }
- void ReplaceSystemMalloc() {
- InitMallocZoneFields();
- // Register the zone.
- malloc_zone_register(&sanitizer_zone);
- }
- } // namespace COMMON_MALLOC_NAMESPACE
|