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 32 namespace __sanitizer { 33 34 extern malloc_zone_t sanitizer_zone; 35 36 struct sanitizer_malloc_introspection_t : public malloc_introspection_t { 37 // IMPORTANT: Do not change the order, alignment, or types of these fields to 38 // maintain binary compatibility. You should only add fields to this struct. 39 40 // Used to track changes to the allocator that will affect 41 // zone enumeration. 42 u64 allocator_enumeration_version; 43 }; 44 45 u64 GetMallocZoneAllocatorEnumerationVersion() { 46 // This represents the current allocator ABI version. 47 // This field should be incremented every time the Allocator 48 // ABI changes in a way that breaks allocator enumeration. 49 return 0; 50 } 51 52 } // namespace __sanitizer 53 54 INTERCEPTOR(malloc_zone_t *, malloc_create_zone, 55 vm_size_t start_size, unsigned zone_flags) { 56 COMMON_MALLOC_ENTER(); 57 uptr page_size = GetPageSizeCached(); 58 uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size); 59 COMMON_MALLOC_MEMALIGN(page_size, allocated_size); 60 malloc_zone_t *new_zone = (malloc_zone_t *)p; 61 internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone)); 62 new_zone->zone_name = NULL; // The name will be changed anyway. 63 if (GetMacosVersion() >= MACOS_VERSION_LION) { 64 // Prevent the client app from overwriting the zone contents. 65 // Library functions that need to modify the zone will set PROT_WRITE on it. 66 // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. 67 mprotect(new_zone, allocated_size, PROT_READ); 68 } 69 // We're explicitly *NOT* registering the zone. 70 return new_zone; 71 } 72 73 INTERCEPTOR(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 we 76 // don't to unregister. Just un-mprotect and free() the zone. 77 if (GetMacosVersion() >= MACOS_VERSION_LION) { 78 uptr page_size = GetPageSizeCached(); 79 uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size); 80 mprotect(zone, allocated_size, PROT_READ | PROT_WRITE); 81 } 82 if (zone->zone_name) { 83 COMMON_MALLOC_FREE((void *)zone->zone_name); 84 } 85 COMMON_MALLOC_FREE(zone); 86 } 87 88 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { 89 COMMON_MALLOC_ENTER(); 90 return &sanitizer_zone; 91 } 92 93 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { 94 // FIXME: ASan should support purgeable allocations. 95 // https://github.com/google/sanitizers/issues/139 96 COMMON_MALLOC_ENTER(); 97 return &sanitizer_zone; 98 } 99 100 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { 101 // FIXME: ASan should support purgeable allocations. Ignoring them is fine 102 // for now. 103 COMMON_MALLOC_ENTER(); 104 } 105 106 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { 107 // FIXME: ASan should support purgeable allocations. Ignoring them is fine 108 // for now. 109 COMMON_MALLOC_ENTER(); 110 // Must return 0 if the contents were not purged since the last call to 111 // malloc_make_purgeable(). 112 return 0; 113 } 114 115 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { 116 COMMON_MALLOC_ENTER(); 117 // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)| 118 // bytes. 119 size_t buflen = 120 sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0); 121 InternalScopedString new_name(buflen); 122 if (name && zone->introspect == sanitizer_zone.introspect) { 123 new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name); 124 name = new_name.data(); 125 } 126 127 // Call the system malloc's implementation for both external and our zones, 128 // since that appropriately changes VM region protections on the zone. 129 REAL(malloc_set_zone_name)(zone, name); 130 } 131 132 INTERCEPTOR(void *, malloc, size_t size) { 133 COMMON_MALLOC_ENTER(); 134 COMMON_MALLOC_MALLOC(size); 135 return p; 136 } 137 138 INTERCEPTOR(void, free, void *ptr) { 139 COMMON_MALLOC_ENTER(); 140 if (!ptr) return; 141 COMMON_MALLOC_FREE(ptr); 142 } 143 144 INTERCEPTOR(void *, realloc, void *ptr, size_t size) { 145 COMMON_MALLOC_ENTER(); 146 COMMON_MALLOC_REALLOC(ptr, size); 147 return p; 148 } 149 150 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { 151 COMMON_MALLOC_ENTER(); 152 COMMON_MALLOC_CALLOC(nmemb, size); 153 return p; 154 } 155 156 INTERCEPTOR(void *, valloc, size_t size) { 157 COMMON_MALLOC_ENTER(); 158 COMMON_MALLOC_VALLOC(size); 159 return p; 160 } 161 162 INTERCEPTOR(size_t, malloc_good_size, size_t size) { 163 COMMON_MALLOC_ENTER(); 164 return sanitizer_zone.introspect->good_size(&sanitizer_zone, size); 165 } 166 167 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { 168 COMMON_MALLOC_ENTER(); 169 CHECK(memptr); 170 COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size); 171 return res; 172 } 173 174 namespace { 175 176 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux 177 // wrappers, as they are basically copied from there. 178 extern "C" 179 SANITIZER_INTERFACE_ATTRIBUTE 180 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) { 181 COMMON_MALLOC_SIZE(ptr); 182 return size; 183 } 184 185 extern "C" 186 SANITIZER_INTERFACE_ATTRIBUTE 187 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) { 188 COMMON_MALLOC_ENTER(); 189 COMMON_MALLOC_MALLOC(size); 190 return p; 191 } 192 193 extern "C" 194 SANITIZER_INTERFACE_ATTRIBUTE 195 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { 196 if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) { 197 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. 198 const size_t kCallocPoolSize = 1024; 199 static uptr calloc_memory_for_dlsym[kCallocPoolSize]; 200 static size_t allocated; 201 size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; 202 void *mem = (void*)&calloc_memory_for_dlsym[allocated]; 203 allocated += size_in_words; 204 CHECK(allocated < kCallocPoolSize); 205 return mem; 206 } 207 COMMON_MALLOC_CALLOC(nmemb, size); 208 return p; 209 } 210 211 extern "C" 212 SANITIZER_INTERFACE_ATTRIBUTE 213 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) { 214 COMMON_MALLOC_ENTER(); 215 COMMON_MALLOC_VALLOC(size); 216 return p; 217 } 218 219 // TODO(glider): the allocation callbacks need to be refactored. 220 extern "C" 221 SANITIZER_INTERFACE_ATTRIBUTE 222 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) { 223 if (!ptr) return; 224 COMMON_MALLOC_FREE(ptr); 225 } 226 227 #define GET_ZONE_FOR_PTR(ptr) \ 228 malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ 229 const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name 230 231 extern "C" 232 SANITIZER_INTERFACE_ATTRIBUTE 233 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) { 234 if (!ptr) { 235 COMMON_MALLOC_MALLOC(new_size); 236 return p; 237 } else { 238 COMMON_MALLOC_SIZE(ptr); 239 if (size) { 240 COMMON_MALLOC_REALLOC(ptr, new_size); 241 return p; 242 } else { 243 // We can't recover from reallocating an unknown address, because 244 // this would require reading at most |new_size| bytes from 245 // potentially unaccessible memory. 246 GET_ZONE_FOR_PTR(ptr); 247 COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name); 248 return nullptr; 249 } 250 } 251 } 252 253 extern "C" 254 SANITIZER_INTERFACE_ATTRIBUTE 255 void __sanitizer_mz_destroy(malloc_zone_t* zone) { 256 // A no-op -- we will not be destroyed! 257 Report("__sanitizer_mz_destroy() called -- ignoring\n"); 258 } 259 260 extern "C" 261 SANITIZER_INTERFACE_ATTRIBUTE 262 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { 263 COMMON_MALLOC_ENTER(); 264 COMMON_MALLOC_MEMALIGN(align, size); 265 return p; 266 } 267 268 // This public API exists purely for testing purposes. 269 extern "C" 270 SANITIZER_INTERFACE_ATTRIBUTE 271 malloc_zone_t* __sanitizer_mz_default_zone() { 272 return &sanitizer_zone; 273 } 274 275 // This function is currently unused, and we build with -Werror. 276 #if 0 277 void __sanitizer_mz_free_definite_size( 278 malloc_zone_t* zone, void *ptr, size_t size) { 279 // TODO(glider): check that |size| is valid. 280 UNIMPLEMENTED(); 281 } 282 #endif 283 284 kern_return_t mi_enumerator(task_t task, void *, 285 unsigned type_mask, vm_address_t zone_address, 286 memory_reader_t reader, 287 vm_range_recorder_t recorder) { 288 // Should enumerate all the pointers we have. Seems like a lot of work. 289 return KERN_FAILURE; 290 } 291 292 size_t mi_good_size(malloc_zone_t *zone, size_t size) { 293 // I think it's always safe to return size, but we maybe could do better. 294 return size; 295 } 296 297 boolean_t mi_check(malloc_zone_t *zone) { 298 UNIMPLEMENTED(); 299 } 300 301 void mi_print(malloc_zone_t *zone, boolean_t verbose) { 302 UNIMPLEMENTED(); 303 } 304 305 void mi_log(malloc_zone_t *zone, void *address) { 306 // I don't think we support anything like this 307 } 308 309 void mi_force_lock(malloc_zone_t *zone) { 310 COMMON_MALLOC_FORCE_LOCK(); 311 } 312 313 void mi_force_unlock(malloc_zone_t *zone) { 314 COMMON_MALLOC_FORCE_UNLOCK(); 315 } 316 317 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { 318 COMMON_MALLOC_FILL_STATS(zone, stats); 319 } 320 321 boolean_t mi_zone_locked(malloc_zone_t *zone) { 322 // UNIMPLEMENTED(); 323 return false; 324 } 325 326 } // unnamed namespace 327 328 namespace COMMON_MALLOC_NAMESPACE { 329 330 void InitMallocZoneFields() { 331 static sanitizer_malloc_introspection_t sanitizer_zone_introspection; 332 // Ok to use internal_memset, these places are not performance-critical. 333 internal_memset(&sanitizer_zone_introspection, 0, 334 sizeof(sanitizer_zone_introspection)); 335 336 sanitizer_zone_introspection.enumerator = &mi_enumerator; 337 sanitizer_zone_introspection.good_size = &mi_good_size; 338 sanitizer_zone_introspection.check = &mi_check; 339 sanitizer_zone_introspection.print = &mi_print; 340 sanitizer_zone_introspection.log = &mi_log; 341 sanitizer_zone_introspection.force_lock = &mi_force_lock; 342 sanitizer_zone_introspection.force_unlock = &mi_force_unlock; 343 sanitizer_zone_introspection.statistics = &mi_statistics; 344 sanitizer_zone_introspection.zone_locked = &mi_zone_locked; 345 346 // Set current allocator enumeration version. 347 sanitizer_zone_introspection.allocator_enumeration_version = 348 GetMallocZoneAllocatorEnumerationVersion(); 349 350 internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t)); 351 352 // Use version 6 for OSX >= 10.6. 353 sanitizer_zone.version = 6; 354 sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME; 355 sanitizer_zone.size = &__sanitizer_mz_size; 356 sanitizer_zone.malloc = &__sanitizer_mz_malloc; 357 sanitizer_zone.calloc = &__sanitizer_mz_calloc; 358 sanitizer_zone.valloc = &__sanitizer_mz_valloc; 359 sanitizer_zone.free = &__sanitizer_mz_free; 360 sanitizer_zone.realloc = &__sanitizer_mz_realloc; 361 sanitizer_zone.destroy = &__sanitizer_mz_destroy; 362 sanitizer_zone.batch_malloc = 0; 363 sanitizer_zone.batch_free = 0; 364 sanitizer_zone.free_definite_size = 0; 365 sanitizer_zone.memalign = &__sanitizer_mz_memalign; 366 sanitizer_zone.introspect = &sanitizer_zone_introspection; 367 } 368 369 void ReplaceSystemMalloc() { 370 InitMallocZoneFields(); 371 372 // Register the zone. 373 malloc_zone_register(&sanitizer_zone); 374 } 375 376 } // namespace COMMON_MALLOC_NAMESPACE 377