asan_malloc_mac.cc revision e205a9daec9ec4afed956cf5455889725b9192fb
1//===-- asan_rtl.cc -------------------------------------------------------===// 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 is a part of AddressSanitizer, an address sanity checker. 11// 12// Mac-specific malloc interception. 13//===----------------------------------------------------------------------===// 14 15#ifdef __APPLE__ 16 17#include <AvailabilityMacros.h> 18#include <CoreFoundation/CFBase.h> 19#include <malloc/malloc.h> 20#include <setjmp.h> 21 22#include "asan_allocator.h" 23#include "asan_interceptors.h" 24#include "asan_internal.h" 25#include "asan_stack.h" 26 27// Similar code is used in Google Perftools, 28// http://code.google.com/p/google-perftools. 29 30// ---------------------- Replacement functions ---------------- {{{1 31using namespace __asan; // NOLINT 32 33// TODO(glider): do we need both zones? 34static malloc_zone_t *system_malloc_zone = 0; 35static malloc_zone_t *system_purgeable_zone = 0; 36CFAllocatorRef cf_asan = 0; 37 38// The free() implementation provided by OS X calls malloc_zone_from_ptr() 39// to find the owner of |ptr|. If the result is 0, an invalid free() is 40// reported. Our implementation falls back to asan_free() in this case 41// in order to print an ASan-style report. 42// 43// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is 44// placed at the beginning of the allocated chunk and the pointer returned by 45// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then 46// passed directly to free(), which will lead to errors. 47// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)| 48// contains a pointer to our CFAllocator (assuming no other allocator is used). 49// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more 50// info. 51INTERCEPTOR(void, free, void *ptr) { 52 malloc_zone_t *zone = malloc_zone_from_ptr(ptr); 53 if (zone) { 54#if defined(MAC_OS_X_VERSION_10_6) && \ 55 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 56 if ((zone->version >= 6) && (zone->free_definite_size)) { 57 zone->free_definite_size(zone, ptr, malloc_size(ptr)); 58 } else { 59 malloc_zone_free(zone, ptr); 60 } 61#else 62 malloc_zone_free(zone, ptr); 63#endif 64 } else { 65 if (FLAG_replace_cfallocator) { 66 // Make sure we're not hitting the previous page. This may be incorrect 67 // if ASan's malloc returns an address ending with 0xFF8, which will be 68 // then padded to a page boundary with a CFAllocatorRef. 69 uptr arith_ptr = (uptr)ptr; 70 if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) { 71 CFAllocatorRef *saved = 72 (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef)); 73 if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved; 74 } 75 } 76 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 77 asan_free(ptr, &stack); 78 } 79} 80 81namespace { 82// TODO(glider): the mz_* functions should be united with the Linux wrappers, 83// as they are basically copied from there. 84size_t mz_size(malloc_zone_t* zone, const void* ptr) { 85 // Fast path: check whether this pointer belongs to the original malloc zone. 86 // We cannot just call malloc_zone_from_ptr(), because it in turn 87 // calls our mz_size(). 88 if (system_malloc_zone) { 89 if ((system_malloc_zone->size)(system_malloc_zone, ptr)) return 0; 90 } 91 return asan_mz_size(ptr); 92} 93 94void *mz_malloc(malloc_zone_t *zone, size_t size) { 95 if (!asan_inited) { 96 CHECK(system_malloc_zone); 97 return malloc_zone_malloc(system_malloc_zone, size); 98 } 99 GET_STACK_TRACE_HERE_FOR_MALLOC; 100 return asan_malloc(size, &stack); 101} 102 103void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) { 104 if (!asan_inited) { 105 CHECK(system_malloc_zone); 106 return malloc_zone_malloc(system_malloc_zone, size); 107 } 108 GET_STACK_TRACE_HERE_FOR_MALLOC; 109 return asan_malloc(size, &stack); 110} 111 112void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { 113 if (!asan_inited) { 114 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. 115 const size_t kCallocPoolSize = 1024; 116 static uptr calloc_memory_for_dlsym[kCallocPoolSize]; 117 static size_t allocated; 118 size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; 119 void *mem = (void*)&calloc_memory_for_dlsym[allocated]; 120 allocated += size_in_words; 121 CHECK(allocated < kCallocPoolSize); 122 return mem; 123 } 124 GET_STACK_TRACE_HERE_FOR_MALLOC; 125 return asan_calloc(nmemb, size, &stack); 126} 127 128void *mz_valloc(malloc_zone_t *zone, size_t size) { 129 if (!asan_inited) { 130 CHECK(system_malloc_zone); 131 return malloc_zone_valloc(system_malloc_zone, size); 132 } 133 GET_STACK_TRACE_HERE_FOR_MALLOC; 134 return asan_memalign(kPageSize, size, &stack); 135} 136 137void print_zone_for_ptr(void *ptr) { 138 malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); 139 if (orig_zone) { 140 if (orig_zone->zone_name) { 141 AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n", 142 ptr, orig_zone, orig_zone->zone_name); 143 } else { 144 AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", 145 ptr, orig_zone); 146 } 147 } else { 148 AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr); 149 } 150} 151 152// TODO(glider): the allocation callbacks need to be refactored. 153void mz_free(malloc_zone_t *zone, void *ptr) { 154 if (!ptr) return; 155 malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); 156 // For some reason Chromium calls mz_free() for pointers that belong to 157 // DefaultPurgeableMallocZone instead of asan_zone. We might want to 158 // fix this someday. 159 if (orig_zone == system_purgeable_zone) { 160 system_purgeable_zone->free(system_purgeable_zone, ptr); 161 return; 162 } 163 if (asan_mz_size(ptr)) { 164 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 165 asan_free(ptr, &stack); 166 } else { 167 // Let us just leak this memory for now. 168 AsanPrintf("mz_free(%p) -- attempting to free unallocated memory.\n" 169 "AddressSanitizer is ignoring this error on Mac OS now.\n", 170 ptr); 171 print_zone_for_ptr(ptr); 172 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 173 stack.PrintStack(); 174 return; 175 } 176} 177 178void cf_free(void *ptr, void *info) { 179 if (!ptr) return; 180 malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); 181 // For some reason Chromium calls mz_free() for pointers that belong to 182 // DefaultPurgeableMallocZone instead of asan_zone. We might want to 183 // fix this someday. 184 if (orig_zone == system_purgeable_zone) { 185 system_purgeable_zone->free(system_purgeable_zone, ptr); 186 return; 187 } 188 if (asan_mz_size(ptr)) { 189 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 190 asan_free(ptr, &stack); 191 } else { 192 // Let us just leak this memory for now. 193 AsanPrintf("cf_free(%p) -- attempting to free unallocated memory.\n" 194 "AddressSanitizer is ignoring this error on Mac OS now.\n", 195 ptr); 196 print_zone_for_ptr(ptr); 197 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 198 stack.PrintStack(); 199 return; 200 } 201} 202 203void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { 204 if (!ptr) { 205 GET_STACK_TRACE_HERE_FOR_MALLOC; 206 return asan_malloc(size, &stack); 207 } else { 208 if (asan_mz_size(ptr)) { 209 GET_STACK_TRACE_HERE_FOR_MALLOC; 210 return asan_realloc(ptr, size, &stack); 211 } else { 212 // We can't recover from reallocating an unknown address, because 213 // this would require reading at most |size| bytes from 214 // potentially unaccessible memory. 215 AsanPrintf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" 216 "This is an unrecoverable problem, exiting now.\n", 217 ptr); 218 print_zone_for_ptr(ptr); 219 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 220 stack.PrintStack(); 221 ShowStatsAndAbort(); 222 return 0; // unreachable 223 } 224 } 225} 226 227void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) { 228 if (!ptr) { 229 GET_STACK_TRACE_HERE_FOR_MALLOC; 230 return asan_malloc(size, &stack); 231 } else { 232 if (asan_mz_size(ptr)) { 233 GET_STACK_TRACE_HERE_FOR_MALLOC; 234 return asan_realloc(ptr, size, &stack); 235 } else { 236 // We can't recover from reallocating an unknown address, because 237 // this would require reading at most |size| bytes from 238 // potentially unaccessible memory. 239 AsanPrintf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" 240 "This is an unrecoverable problem, exiting now.\n", 241 ptr); 242 print_zone_for_ptr(ptr); 243 GET_STACK_TRACE_HERE_FOR_FREE(ptr); 244 stack.PrintStack(); 245 ShowStatsAndAbort(); 246 return 0; // unreachable 247 } 248 } 249} 250 251void mz_destroy(malloc_zone_t* zone) { 252 // A no-op -- we will not be destroyed! 253 AsanPrintf("mz_destroy() called -- ignoring\n"); 254} 255 // from AvailabilityMacros.h 256#if defined(MAC_OS_X_VERSION_10_6) && \ 257 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 258void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { 259 if (!asan_inited) { 260 CHECK(system_malloc_zone); 261 return malloc_zone_memalign(system_malloc_zone, align, size); 262 } 263 GET_STACK_TRACE_HERE_FOR_MALLOC; 264 return asan_memalign(align, size, &stack); 265} 266 267// This function is currently unused, and we build with -Werror. 268#if 0 269void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { 270 // TODO(glider): check that |size| is valid. 271 UNIMPLEMENTED(); 272} 273#endif 274#endif 275 276// malloc_introspection callbacks. I'm not clear on what all of these do. 277kern_return_t mi_enumerator(task_t task, void *, 278 unsigned type_mask, vm_address_t zone_address, 279 memory_reader_t reader, 280 vm_range_recorder_t recorder) { 281 // Should enumerate all the pointers we have. Seems like a lot of work. 282 return KERN_FAILURE; 283} 284 285size_t mi_good_size(malloc_zone_t *zone, size_t size) { 286 // I think it's always safe to return size, but we maybe could do better. 287 return size; 288} 289 290boolean_t mi_check(malloc_zone_t *zone) { 291 UNIMPLEMENTED(); 292 return true; 293} 294 295void mi_print(malloc_zone_t *zone, boolean_t verbose) { 296 UNIMPLEMENTED(); 297 return; 298} 299 300void mi_log(malloc_zone_t *zone, void *address) { 301 // I don't think we support anything like this 302} 303 304void mi_force_lock(malloc_zone_t *zone) { 305 asan_mz_force_lock(); 306} 307 308void mi_force_unlock(malloc_zone_t *zone) { 309 asan_mz_force_unlock(); 310} 311 312// This function is currently unused, and we build with -Werror. 313#if 0 314void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { 315 // TODO(csilvers): figure out how to fill these out 316 // TODO(glider): port this from tcmalloc when ready. 317 stats->blocks_in_use = 0; 318 stats->size_in_use = 0; 319 stats->max_size_in_use = 0; 320 stats->size_allocated = 0; 321} 322#endif 323 324#if defined(MAC_OS_X_VERSION_10_6) && \ 325 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 326boolean_t mi_zone_locked(malloc_zone_t *zone) { 327 // UNIMPLEMENTED(); 328 return false; 329} 330#endif 331 332} // unnamed namespace 333 334extern bool kCFUseCollectableAllocator; // is GC on? 335 336namespace __asan { 337void ReplaceSystemMalloc() { 338 static malloc_introspection_t asan_introspection; 339 // Ok to use internal_memset, these places are not performance-critical. 340 internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); 341 342 asan_introspection.enumerator = &mi_enumerator; 343 asan_introspection.good_size = &mi_good_size; 344 asan_introspection.check = &mi_check; 345 asan_introspection.print = &mi_print; 346 asan_introspection.log = &mi_log; 347 asan_introspection.force_lock = &mi_force_lock; 348 asan_introspection.force_unlock = &mi_force_unlock; 349 350 static malloc_zone_t asan_zone; 351 internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); 352 353 // Start with a version 4 zone which is used for OS X 10.4 and 10.5. 354 asan_zone.version = 4; 355 asan_zone.zone_name = "asan"; 356 asan_zone.size = &mz_size; 357 asan_zone.malloc = &mz_malloc; 358 asan_zone.calloc = &mz_calloc; 359 asan_zone.valloc = &mz_valloc; 360 asan_zone.free = &mz_free; 361 asan_zone.realloc = &mz_realloc; 362 asan_zone.destroy = &mz_destroy; 363 asan_zone.batch_malloc = 0; 364 asan_zone.batch_free = 0; 365 asan_zone.introspect = &asan_introspection; 366 367 // from AvailabilityMacros.h 368#if defined(MAC_OS_X_VERSION_10_6) && \ 369 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 370 // Switch to version 6 on OSX 10.6 to support memalign. 371 asan_zone.version = 6; 372 asan_zone.free_definite_size = 0; 373 asan_zone.memalign = &mz_memalign; 374 asan_introspection.zone_locked = &mi_zone_locked; 375 376 // Request the default purgable zone to force its creation. The 377 // current default zone is registered with the purgable zone for 378 // doing tiny and small allocs. Sadly, it assumes that the default 379 // zone is the szone implementation from OS X and will crash if it 380 // isn't. By creating the zone now, this will be true and changing 381 // the default zone won't cause a problem. (OS X 10.6 and higher.) 382 system_purgeable_zone = malloc_default_purgeable_zone(); 383#endif 384 385 // Register the ASan zone. At this point, it will not be the 386 // default zone. 387 malloc_zone_register(&asan_zone); 388 389 // Unregister and reregister the default zone. Unregistering swaps 390 // the specified zone with the last one registered which for the 391 // default zone makes the more recently registered zone the default 392 // zone. The default zone is then re-registered to ensure that 393 // allocations made from it earlier will be handled correctly. 394 // Things are not guaranteed to work that way, but it's how they work now. 395 system_malloc_zone = malloc_default_zone(); 396 malloc_zone_unregister(system_malloc_zone); 397 malloc_zone_register(system_malloc_zone); 398 // Make sure the default allocator was replaced. 399 CHECK(malloc_default_zone() == &asan_zone); 400 401 if (FLAG_replace_cfallocator) { 402 static CFAllocatorContext asan_context = 403 { /*version*/ 0, /*info*/ &asan_zone, 404 /*retain*/ 0, /*release*/ 0, 405 /*copyDescription*/0, 406 /*allocate*/ &cf_malloc, 407 /*reallocate*/ &cf_realloc, 408 /*deallocate*/ &cf_free, 409 /*preferredSize*/ 0 }; 410 cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); 411 CFAllocatorSetDefault(cf_asan); 412 } 413} 414} // namespace __asan 415 416#endif // __APPLE__ 417