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 32static malloc_zone_t sanitizer_zone; 33 34INTERCEPTOR(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 52INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { 53 COMMON_MALLOC_ENTER(); 54 return &sanitizer_zone; 55} 56 57INTERCEPTOR(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 64INTERCEPTOR(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 70INTERCEPTOR(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 79INTERCEPTOR(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 96INTERCEPTOR(void *, malloc, size_t size) { 97 COMMON_MALLOC_ENTER(); 98 COMMON_MALLOC_MALLOC(size); 99 return p; 100} 101 102INTERCEPTOR(void, free, void *ptr) { 103 COMMON_MALLOC_ENTER(); 104 if (!ptr) return; 105 COMMON_MALLOC_FREE(ptr); 106} 107 108INTERCEPTOR(void *, realloc, void *ptr, size_t size) { 109 COMMON_MALLOC_ENTER(); 110 COMMON_MALLOC_REALLOC(ptr, size); 111 return p; 112} 113 114INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { 115 COMMON_MALLOC_ENTER(); 116 COMMON_MALLOC_CALLOC(nmemb, size); 117 return p; 118} 119 120INTERCEPTOR(void *, valloc, size_t size) { 121 COMMON_MALLOC_ENTER(); 122 COMMON_MALLOC_VALLOC(size); 123 return p; 124} 125 126INTERCEPTOR(size_t, malloc_good_size, size_t size) { 127 COMMON_MALLOC_ENTER(); 128 return sanitizer_zone.introspect->good_size(&sanitizer_zone, size); 129} 130 131INTERCEPTOR(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 142namespace { 143 144// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux 145// wrappers, as they are basically copied from there. 146extern "C" 147SANITIZER_INTERFACE_ATTRIBUTE 148size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) { 149 COMMON_MALLOC_SIZE(ptr); 150 return size; 151} 152 153extern "C" 154SANITIZER_INTERFACE_ATTRIBUTE 155void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) { 156 COMMON_MALLOC_ENTER(); 157 COMMON_MALLOC_MALLOC(size); 158 return p; 159} 160 161extern "C" 162SANITIZER_INTERFACE_ATTRIBUTE 163void *__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 179extern "C" 180SANITIZER_INTERFACE_ATTRIBUTE 181void *__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. 188extern "C" 189SANITIZER_INTERFACE_ATTRIBUTE 190void __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 199extern "C" 200SANITIZER_INTERFACE_ATTRIBUTE 201void *__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 221extern "C" 222SANITIZER_INTERFACE_ATTRIBUTE 223void __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 228extern "C" 229SANITIZER_INTERFACE_ATTRIBUTE 230void *__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 238void __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 245kern_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 253size_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 258boolean_t mi_check(malloc_zone_t *zone) { 259 UNIMPLEMENTED(); 260} 261 262void mi_print(malloc_zone_t *zone, boolean_t verbose) { 263 UNIMPLEMENTED(); 264} 265 266void mi_log(malloc_zone_t *zone, void *address) { 267 // I don't think we support anything like this 268} 269 270void mi_force_lock(malloc_zone_t *zone) { 271 COMMON_MALLOC_FORCE_LOCK(); 272} 273 274void mi_force_unlock(malloc_zone_t *zone) { 275 COMMON_MALLOC_FORCE_UNLOCK(); 276} 277 278void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { 279 COMMON_MALLOC_FILL_STATS(zone, stats); 280} 281 282boolean_t mi_zone_locked(malloc_zone_t *zone) { 283 // UNIMPLEMENTED(); 284 return false; 285} 286 287} // unnamed namespace 288 289namespace COMMON_MALLOC_NAMESPACE { 290 291void 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