Index: lib/asan/asan_malloc_mac.cc =================================================================== --- lib/asan/asan_malloc_mac.cc +++ lib/asan/asan_malloc_mac.cc @@ -15,348 +15,50 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC -#include -#include -#include -#include -#include - -#include "asan_allocator.h" #include "asan_interceptors.h" -#include "asan_internal.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "sanitizer_common/sanitizer_mac.h" - -// Similar code is used in Google Perftools, -// http://code.google.com/p/google-perftools. - -// ---------------------- Replacement functions ---------------- {{{1 -using namespace __asan; // NOLINT - -// TODO(glider): do we need both zones? -static malloc_zone_t *system_malloc_zone = 0; -static malloc_zone_t asan_zone; - -INTERCEPTOR(malloc_zone_t *, malloc_create_zone, - vm_size_t start_size, unsigned zone_flags) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - uptr page_size = GetPageSizeCached(); - uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); - malloc_zone_t *new_zone = - (malloc_zone_t*)asan_memalign(page_size, allocated_size, - &stack, FROM_MALLOC); - internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); - new_zone->zone_name = NULL; // The name will be changed anyway. - if (GetMacosVersion() >= MACOS_VERSION_LION) { - // 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); - } - return new_zone; -} - -INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { - ENSURE_ASAN_INITED(); - return &asan_zone; -} - -INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { - // FIXME: ASan should support purgeable allocations. - // https://code.google.com/p/address-sanitizer/issues/detail?id=139 - ENSURE_ASAN_INITED(); - return &asan_zone; -} - -INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { - // FIXME: ASan should support purgeable allocations. Ignoring them is fine - // for now. - ENSURE_ASAN_INITED(); -} - -INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { - // FIXME: ASan should support purgeable allocations. Ignoring them is fine - // for now. - ENSURE_ASAN_INITED(); - // 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) { - ENSURE_ASAN_INITED(); - // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. - size_t buflen = 6 + (name ? internal_strlen(name) : 0); - InternalScopedString new_name(buflen); - if (name && zone->introspect == asan_zone.introspect) { - new_name.append("asan-%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) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - void *res = asan_malloc(size, &stack); - return res; -} -INTERCEPTOR(void, free, void *ptr) { - ENSURE_ASAN_INITED(); - if (!ptr) return; - GET_STACK_TRACE_FREE; +using namespace __asan; +#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED() +#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited +#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock() +#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock() +#define COMMON_MALLOC_MEMALIGN(alignment, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC) +#define COMMON_MALLOC_MALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_malloc(size, &stack) +#define COMMON_MALLOC_REALLOC(ptr, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_realloc(ptr, size, &stack); +#define COMMON_MALLOC_CALLOC(count, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_calloc(count, size, &stack); +#define COMMON_MALLOC_VALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +#define COMMON_MALLOC_FREE(ptr) \ + GET_STACK_TRACE_FREE; \ asan_free(ptr, &stack, FROM_MALLOC); -} - -INTERCEPTOR(void *, realloc, void *ptr, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); -} - -INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -INTERCEPTOR(void *, valloc, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); -} - -INTERCEPTOR(size_t, malloc_good_size, size_t size) { - ENSURE_ASAN_INITED(); - return asan_zone.introspect->good_size(&asan_zone, size); -} - -INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { - ENSURE_ASAN_INITED(); - CHECK(memptr); - GET_STACK_TRACE_MALLOC; - void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); - if (result) { - *memptr = result; - return 0; - } - return -1; -} - -namespace { - -// TODO(glider): the __asan_mz_* functions should be united with the Linux -// wrappers, as they are basically copied from there. -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) { - return asan_mz_size(ptr); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_malloc(system_malloc_zone, size); - } - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { - if (UNLIKELY(!asan_inited)) { - // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. - const size_t kCallocPoolSize = 1024; - static uptr calloc_memory_for_dlsym[kCallocPoolSize]; - static size_t allocated; - size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; - void *mem = (void*)&calloc_memory_for_dlsym[allocated]; - allocated += size_in_words; - CHECK(allocated < kCallocPoolSize); - return mem; - } - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_valloc(system_malloc_zone, size); - } - GET_STACK_TRACE_MALLOC; - return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); -} - -#define GET_ZONE_FOR_PTR(ptr) \ - malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ - const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name - -void ALWAYS_INLINE free_common(void *context, void *ptr) { - if (!ptr) return; - GET_STACK_TRACE_FREE; - // FIXME: need to retire this flag. - if (!flags()->mac_ignore_invalid_free) { - asan_free(ptr, &stack, FROM_MALLOC); - } else { - GET_ZONE_FOR_PTR(ptr); - WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - return; - } -} - -// TODO(glider): the allocation callbacks need to be refactored. -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_mz_free(malloc_zone_t *zone, void *ptr) { - free_common(zone, ptr); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { - if (!ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); - } else { - if (asan_mz_size(ptr)) { - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); - } else { - // We can't recover from reallocating an unknown address, because - // this would require reading at most |size| bytes from - // potentially unaccessible memory. - GET_STACK_TRACE_FREE; - GET_ZONE_FOR_PTR(ptr); - ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - } - } -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_mz_destroy(malloc_zone_t* zone) { - // A no-op -- we will not be destroyed! - Report("__asan_mz_destroy() called -- ignoring\n"); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_memalign(system_malloc_zone, align, size); - } - GET_STACK_TRACE_MALLOC; - return asan_memalign(align, size, &stack, FROM_MALLOC); -} - -// This function is currently unused, and we build with -Werror. -#if 0 -void __asan_mz_free_definite_size( - malloc_zone_t* zone, void *ptr, size_t size) { - // TODO(glider): check that |size| is valid. - UNIMPLEMENTED(); -} -#endif - -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) { - // Should enumerate all the pointers we have. Seems like a lot of work. - return KERN_FAILURE; -} - -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) { - asan_mz_force_lock(); -} - -void mi_force_unlock(malloc_zone_t *zone) { - asan_mz_force_unlock(); -} - -void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { - AsanMallocStats malloc_stats; - FillMallocStatistics(&malloc_stats); - CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); +#define COMMON_MALLOC_SIZE(ptr) \ + uptr size = asan_mz_size(ptr); +#define COMMON_MALLOC_FILL_STATS(zone, stats) \ + AsanMallocStats malloc_stats; \ + FillMallocStatistics(&malloc_stats); \ + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \ internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); -} - -boolean_t mi_zone_locked(malloc_zone_t *zone) { - // UNIMPLEMENTED(); - return false; -} - -} // unnamed namespace - -namespace __asan { +#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ + GET_STACK_TRACE_FREE; \ + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); +#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free +#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \ + GET_STACK_TRACE_FREE; \ + WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); +#define COMMON_MALLOC_NAMESPACE __asan -void ReplaceSystemMalloc() { - static malloc_introspection_t asan_introspection; - // Ok to use internal_memset, these places are not performance-critical. - internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); +#include "sanitizer_common/sanitizer_malloc_mac.inc" - asan_introspection.enumerator = &mi_enumerator; - asan_introspection.good_size = &mi_good_size; - asan_introspection.check = &mi_check; - asan_introspection.print = &mi_print; - asan_introspection.log = &mi_log; - asan_introspection.force_lock = &mi_force_lock; - asan_introspection.force_unlock = &mi_force_unlock; - asan_introspection.statistics = &mi_statistics; - asan_introspection.zone_locked = &mi_zone_locked; - - internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); - - // Use version 6 for OSX >= 10.6. - asan_zone.version = 6; - asan_zone.zone_name = "asan"; - asan_zone.size = &__asan_mz_size; - asan_zone.malloc = &__asan_mz_malloc; - asan_zone.calloc = &__asan_mz_calloc; - asan_zone.valloc = &__asan_mz_valloc; - asan_zone.free = &__asan_mz_free; - asan_zone.realloc = &__asan_mz_realloc; - asan_zone.destroy = &__asan_mz_destroy; - asan_zone.batch_malloc = 0; - asan_zone.batch_free = 0; - asan_zone.free_definite_size = 0; - asan_zone.memalign = &__asan_mz_memalign; - asan_zone.introspect = &asan_introspection; - - // Register the ASan zone. - malloc_zone_register(&asan_zone); -} -} // namespace __asan - -#endif // SANITIZER_MAC +#endif Index: lib/sanitizer_common/sanitizer_malloc_mac.inc =================================================================== --- lib/sanitizer_common/sanitizer_malloc_mac.inc +++ lib/sanitizer_common/sanitizer_malloc_mac.inc @@ -0,0 +1,348 @@ +//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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_MAC +#error "This file should only be compiled on Darwin." +#endif + +#include +#include +#include +#include +#include + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_mac.h" + +// Similar code is used in Google Perftools, +// http://code.google.com/p/google-perftools. + +// TODO(glider): do we need both zones? +static malloc_zone_t *system_malloc_zone = 0; +static malloc_zone_t sanitizer_zone; + +#ifndef COMMON_MALLOC_ENTER +#define COMMON_MALLOC_ENTER() +#endif + +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. + if (GetMacosVersion() >= MACOS_VERSION_LION) { + // 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); + } + return new_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { + COMMON_MALLOC_ENTER(); + return &sanitizer_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { + // FIXME: ASan should support purgeable allocations. + // https://code.google.com/p/address-sanitizer/issues/detail?id=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(); + // Allocate |strlen("sanitizer-") + 1 + internal_strlen(name)| bytes. + size_t buflen = 11 + (name ? internal_strlen(name) : 0); + InternalScopedString new_name(buflen); + if (name && zone->introspect == sanitizer_zone.introspect) { + new_name.append("sanitizer-%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_MEMALIGN(alignment, size); + if (p) { + *memptr = p; + return 0; + } + return -1; +} + +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) { + if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) { + CHECK(system_malloc_zone); + return malloc_zone_malloc(system_malloc_zone, size); + } + COMMON_MALLOC_MALLOC(size); + return p; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { + if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const size_t kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static size_t allocated; + size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + COMMON_MALLOC_CALLOC(nmemb, size); + return p; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) { + if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) { + CHECK(system_malloc_zone); + return malloc_zone_valloc(system_malloc_zone, size); + } + COMMON_MALLOC_VALLOC(size); + return p; +} + +#define GET_ZONE_FOR_PTR(ptr) \ + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ + const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name + +void ALWAYS_INLINE free_common(void *context, void *ptr) { + if (!ptr) return; + // FIXME: need to retire this flag. + if (!COMMON_MALLOC_IGNORE_INVALID_FREE) { + COMMON_MALLOC_FREE(ptr); + } else { + GET_ZONE_FOR_PTR(ptr); + COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name); + } +} + +// TODO(glider): the allocation callbacks need to be refactored. +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) { + free_common(zone, ptr); +} + +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 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 + +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) { + // Should enumerate all the pointers we have. Seems like a lot of work. + return KERN_FAILURE; +} + +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 ReplaceSystemMalloc() { + static 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; + + internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t)); + + // Use version 6 for OSX >= 10.6. + sanitizer_zone.version = 6; + sanitizer_zone.zone_name = "sanitizer"; + 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; + + // Register the zone. + malloc_zone_register(&sanitizer_zone); +} + +} Index: lib/tsan/CMakeLists.txt =================================================================== --- lib/tsan/CMakeLists.txt +++ lib/tsan/CMakeLists.txt @@ -26,6 +26,7 @@ rtl/tsan_interface_atomic.cc rtl/tsan_interface.cc rtl/tsan_interface_java.cc + rtl/tsan_malloc_mac.cc rtl/tsan_md5.cc rtl/tsan_mman.cc rtl/tsan_mutex.cc Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -97,8 +97,8 @@ // REAL(sigfillset) defined in common interceptors. DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) DECLARE_REAL(int, fflush, __sanitizer_FILE *fp) -DECLARE_REAL(void *, malloc, uptr size) -DECLARE_REAL(void, free, void *ptr) +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); @@ -534,6 +534,7 @@ REAL(siglongjmp)(env, val); } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(void*, malloc, uptr size) { if (cur_thread()->in_symbolizer) return __libc_malloc(size); @@ -546,12 +547,10 @@ return p; } -#if !SANITIZER_MAC TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); return user_alloc(thr, pc, sz, align); } -#endif TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) @@ -588,7 +587,6 @@ user_free(thr, pc, p); } -#if !SANITIZER_MAC TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; @@ -598,9 +596,7 @@ SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); } -#endif -#if !SANITIZER_MAC TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); return user_alloc_usable_size(p); @@ -760,12 +756,12 @@ SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } -#endif TSAN_INTERCEPTOR(void*, valloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } +#endif #if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { @@ -778,11 +774,13 @@ #define TSAN_MAYBE_INTERCEPT_PVALLOC #endif +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } +#endif // Used in thread-safe function static initialization. extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { Index: lib/tsan/rtl/tsan_malloc_mac.cc =================================================================== --- lib/tsan/rtl/tsan_malloc_mac.cc +++ lib/tsan/rtl/tsan_malloc_mac.cc @@ -0,0 +1,57 @@ +//===-- tsan_malloc_mac.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "tsan_interceptors.h" +#include "tsan_stack_trace.h" + +using namespace __tsan; +#define COMMON_MALLOC_ENTER() +#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited) +#define COMMON_MALLOC_FORCE_LOCK() +#define COMMON_MALLOC_FORCE_UNLOCK() +#define COMMON_MALLOC_MEMALIGN(alignment, size) \ + void *p = user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment) +#define COMMON_MALLOC_MALLOC(size) \ + SCOPED_INTERCEPTOR_RAW(malloc, size); \ + void *p = user_alloc(thr, pc, size) +#define COMMON_MALLOC_REALLOC(ptr, size) \ + SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \ + void *p = user_realloc(thr, pc, ptr, size); +#define COMMON_MALLOC_CALLOC(count, size) \ + SCOPED_INTERCEPTOR_RAW(calloc, size, count); \ + void *p = user_calloc(thr, pc, size, count); +#define COMMON_MALLOC_VALLOC(size) \ + SCOPED_INTERCEPTOR_RAW(valloc, size); \ + void *p = user_alloc(thr, pc, size, GetPageSizeCached()); +#define COMMON_MALLOC_FREE(ptr) \ + SCOPED_INTERCEPTOR_RAW(free, ptr); \ + user_free(thr, pc, ptr); +#define COMMON_MALLOC_SIZE(ptr) \ + uptr size = user_alloc_usable_size(ptr); +#define COMMON_MALLOC_FILL_STATS(zone, stats) +#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ + (void)zone_name; \ + Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr); +#define COMMON_MALLOC_IGNORE_INVALID_FREE false +#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \ + (void)zone_name; \ + Report("free_common(%p) -- attempting to free unallocated memory.\n", ptr); +#define COMMON_MALLOC_NAMESPACE __tsan + +#include "sanitizer_common/sanitizer_malloc_mac.inc" + +#endif Index: lib/tsan/rtl/tsan_mman.h =================================================================== --- lib/tsan/rtl/tsan_mman.h +++ lib/tsan/rtl/tsan_mman.h @@ -20,6 +20,7 @@ const uptr kDefaultAlignment = 16; void InitializeAllocator(); +void ReplaceSystemMalloc(); void AllocatorThreadStart(ThreadState *thr); void AllocatorThreadFinish(ThreadState *thr); void AllocatorPrintStats(); Index: lib/tsan/rtl/tsan_platform_linux.cc =================================================================== --- lib/tsan/rtl/tsan_platform_linux.cc +++ lib/tsan/rtl/tsan_platform_linux.cc @@ -331,6 +331,10 @@ } #endif +#ifndef SANITIZER_GO +void ReplaceSystemMalloc() { } +#endif + } // namespace __tsan #endif // SANITIZER_LINUX || SANITIZER_FREEBSD Index: lib/tsan/rtl/tsan_rtl.cc =================================================================== --- lib/tsan/rtl/tsan_rtl.cc +++ lib/tsan/rtl/tsan_rtl.cc @@ -325,6 +325,7 @@ CheckVMASize(); #ifndef SANITIZER_GO InitializeAllocator(); + ReplaceSystemMalloc(); #endif InitializeInterceptors(); CheckShadowMapping(); Index: test/asan/TestCases/Darwin/interface_symbols_darwin.c =================================================================== --- test/asan/TestCases/Darwin/interface_symbols_darwin.c +++ test/asan/TestCases/Darwin/interface_symbols_darwin.c @@ -43,14 +43,6 @@ // RUN: echo __asan_report_exp_store_n >> %t.interface // RUN: echo __asan_get_current_fake_stack >> %t.interface // RUN: echo __asan_addr_is_in_fake_stack >> %t.interface -// RUN: echo __asan_mz_calloc >> %t.interface -// RUN: echo __asan_mz_destroy >> %t.interface -// RUN: echo __asan_mz_free >> %t.interface -// RUN: echo __asan_mz_malloc >> %t.interface -// RUN: echo __asan_mz_memalign >> %t.interface -// RUN: echo __asan_mz_realloc >> %t.interface -// RUN: echo __asan_mz_size >> %t.interface -// RUN: echo __asan_mz_valloc >> %t.interface // RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done // RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done