1 //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file contains Mac-specific malloc interceptors and a custom zone
11 // implementation, which together replace the system allocator.
13 //===----------------------------------------------------------------------===//
15 #include "sanitizer_common/sanitizer_platform.h"
17 #error "This file should only be compiled on Darwin."
20 #include <AvailabilityMacros.h>
21 #include <CoreFoundation/CFBase.h>
23 #include <malloc/malloc.h>
26 #include "interception/interception.h"
27 #include "sanitizer_common/sanitizer_mac.h"
29 // Similar code is used in Google Perftools,
30 // https://github.com/gperftools/gperftools.
32 static malloc_zone_t sanitizer_zone;
34 INTERCEPTOR(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);
49 // We're explicitly *NOT* registering the zone.
53 INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
54 COMMON_MALLOC_ENTER();
55 // We don't need to do anything here. We're not registering new zones, so we
56 // don't to unregister. Just un-mprotect and free() the zone.
57 if (GetMacosVersion() >= MACOS_VERSION_LION) {
58 uptr page_size = GetPageSizeCached();
59 uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
60 mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
62 if (zone->zone_name) {
63 COMMON_MALLOC_FREE((void *)zone->zone_name);
65 COMMON_MALLOC_FREE(zone);
68 extern unsigned malloc_num_zones;
69 extern malloc_zone_t **malloc_zones;
71 // We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
72 // libmalloc tries to set up a different zone as malloc_zones[0], it will call
73 // mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and
74 // make sure we are still the first (default) zone.
75 INTERCEPTOR(int, mprotect, void *addr, size_t len, int prot) {
76 if (addr == malloc_zones && prot == PROT_READ) {
77 if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
78 for (unsigned i = 1; i < malloc_num_zones; i++) {
79 if (malloc_zones[i] == &sanitizer_zone) {
80 // Swap malloc_zones[0] and malloc_zones[i].
81 malloc_zones[i] = malloc_zones[0];
82 malloc_zones[0] = &sanitizer_zone;
88 return REAL(mprotect)(addr, len, prot);
91 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
92 COMMON_MALLOC_ENTER();
93 return &sanitizer_zone;
96 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
97 // FIXME: ASan should support purgeable allocations.
98 // https://github.com/google/sanitizers/issues/139
99 COMMON_MALLOC_ENTER();
100 return &sanitizer_zone;
103 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
104 // FIXME: ASan should support purgeable allocations. Ignoring them is fine
106 COMMON_MALLOC_ENTER();
109 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
110 // FIXME: ASan should support purgeable allocations. Ignoring them is fine
112 COMMON_MALLOC_ENTER();
113 // Must return 0 if the contents were not purged since the last call to
114 // malloc_make_purgeable().
118 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
119 COMMON_MALLOC_ENTER();
120 // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
123 sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
124 InternalScopedString new_name(buflen);
125 if (name && zone->introspect == sanitizer_zone.introspect) {
126 new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
127 name = new_name.data();
130 // Call the system malloc's implementation for both external and our zones,
131 // since that appropriately changes VM region protections on the zone.
132 REAL(malloc_set_zone_name)(zone, name);
135 INTERCEPTOR(void *, malloc, size_t size) {
136 COMMON_MALLOC_ENTER();
137 COMMON_MALLOC_MALLOC(size);
141 INTERCEPTOR(void, free, void *ptr) {
142 COMMON_MALLOC_ENTER();
144 COMMON_MALLOC_FREE(ptr);
147 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
148 COMMON_MALLOC_ENTER();
149 COMMON_MALLOC_REALLOC(ptr, size);
153 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
154 COMMON_MALLOC_ENTER();
155 COMMON_MALLOC_CALLOC(nmemb, size);
159 INTERCEPTOR(void *, valloc, size_t size) {
160 COMMON_MALLOC_ENTER();
161 COMMON_MALLOC_VALLOC(size);
165 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
166 COMMON_MALLOC_ENTER();
167 return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
170 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
171 COMMON_MALLOC_ENTER();
173 COMMON_MALLOC_MEMALIGN(alignment, size);
183 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
184 // wrappers, as they are basically copied from there.
186 SANITIZER_INTERFACE_ATTRIBUTE
187 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
188 COMMON_MALLOC_SIZE(ptr);
193 SANITIZER_INTERFACE_ATTRIBUTE
194 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
195 COMMON_MALLOC_ENTER();
196 COMMON_MALLOC_MALLOC(size);
201 SANITIZER_INTERFACE_ATTRIBUTE
202 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
203 if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
204 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
205 const size_t kCallocPoolSize = 1024;
206 static uptr calloc_memory_for_dlsym[kCallocPoolSize];
207 static size_t allocated;
208 size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
209 void *mem = (void*)&calloc_memory_for_dlsym[allocated];
210 allocated += size_in_words;
211 CHECK(allocated < kCallocPoolSize);
214 COMMON_MALLOC_CALLOC(nmemb, size);
219 SANITIZER_INTERFACE_ATTRIBUTE
220 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
221 COMMON_MALLOC_ENTER();
222 COMMON_MALLOC_VALLOC(size);
226 // TODO(glider): the allocation callbacks need to be refactored.
228 SANITIZER_INTERFACE_ATTRIBUTE
229 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
231 COMMON_MALLOC_FREE(ptr);
234 #define GET_ZONE_FOR_PTR(ptr) \
235 malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
236 const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
239 SANITIZER_INTERFACE_ATTRIBUTE
240 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
242 COMMON_MALLOC_MALLOC(new_size);
245 COMMON_MALLOC_SIZE(ptr);
247 COMMON_MALLOC_REALLOC(ptr, new_size);
250 // We can't recover from reallocating an unknown address, because
251 // this would require reading at most |new_size| bytes from
252 // potentially unaccessible memory.
253 GET_ZONE_FOR_PTR(ptr);
254 COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
261 SANITIZER_INTERFACE_ATTRIBUTE
262 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
263 // A no-op -- we will not be destroyed!
264 Report("__sanitizer_mz_destroy() called -- ignoring\n");
268 SANITIZER_INTERFACE_ATTRIBUTE
269 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
270 COMMON_MALLOC_ENTER();
271 COMMON_MALLOC_MEMALIGN(align, size);
275 // This function is currently unused, and we build with -Werror.
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.
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.
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.
297 boolean_t mi_check(malloc_zone_t *zone) {
301 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
305 void mi_log(malloc_zone_t *zone, void *address) {
306 // I don't think we support anything like this
309 void mi_force_lock(malloc_zone_t *zone) {
310 COMMON_MALLOC_FORCE_LOCK();
313 void mi_force_unlock(malloc_zone_t *zone) {
314 COMMON_MALLOC_FORCE_UNLOCK();
317 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
318 COMMON_MALLOC_FILL_STATS(zone, stats);
321 boolean_t mi_zone_locked(malloc_zone_t *zone) {
326 } // unnamed namespace
328 namespace COMMON_MALLOC_NAMESPACE {
330 void ReplaceSystemMalloc() {
331 static 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));
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;
346 internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
348 // Use version 6 for OSX >= 10.6.
349 sanitizer_zone.version = 6;
350 sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
351 sanitizer_zone.size = &__sanitizer_mz_size;
352 sanitizer_zone.malloc = &__sanitizer_mz_malloc;
353 sanitizer_zone.calloc = &__sanitizer_mz_calloc;
354 sanitizer_zone.valloc = &__sanitizer_mz_valloc;
355 sanitizer_zone.free = &__sanitizer_mz_free;
356 sanitizer_zone.realloc = &__sanitizer_mz_realloc;
357 sanitizer_zone.destroy = &__sanitizer_mz_destroy;
358 sanitizer_zone.batch_malloc = 0;
359 sanitizer_zone.batch_free = 0;
360 sanitizer_zone.free_definite_size = 0;
361 sanitizer_zone.memalign = &__sanitizer_mz_memalign;
362 sanitizer_zone.introspect = &sanitizer_zone_introspection;
364 // Register the zone.
365 malloc_zone_register(&sanitizer_zone);
368 } // namespace COMMON_MALLOC_NAMESPACE