PR libstdc++/86292 fix exception safety of std::vector<InputIterator> constructor
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_malloc_mac.inc
blob2ca4e061464408ce68a5ae02f6539140324c65aa
1 //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file contains Mac-specific malloc interceptors and a custom zone
9 // implementation, which together replace the system allocator.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_platform.h"
14 #if !SANITIZER_MAC
15 #error "This file should only be compiled on Darwin."
16 #endif
18 #include <AvailabilityMacros.h>
19 #include <CoreFoundation/CFBase.h>
20 #include <dlfcn.h>
21 #include <malloc/malloc.h>
22 #include <sys/mman.h>
24 #include "interception/interception.h"
25 #include "sanitizer_common/sanitizer_mac.h"
27 // Similar code is used in Google Perftools,
28 // https://github.com/gperftools/gperftools.
30 static malloc_zone_t sanitizer_zone;
32 INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
33                              vm_size_t start_size, unsigned zone_flags) {
34   COMMON_MALLOC_ENTER();
35   uptr page_size = GetPageSizeCached();
36   uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
37   COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
38   malloc_zone_t *new_zone = (malloc_zone_t *)p;
39   internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
40   new_zone->zone_name = NULL;  // The name will be changed anyway.
41   if (GetMacosVersion() >= MACOS_VERSION_LION) {
42     // Prevent the client app from overwriting the zone contents.
43     // Library functions that need to modify the zone will set PROT_WRITE on it.
44     // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
45     mprotect(new_zone, allocated_size, PROT_READ);
46   }
47   // We're explicitly *NOT* registering the zone.
48   return new_zone;
51 INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
52   COMMON_MALLOC_ENTER();
53   // We don't need to do anything here.  We're not registering new zones, so we
54   // don't to unregister.  Just un-mprotect and free() the zone.
55   if (GetMacosVersion() >= MACOS_VERSION_LION) {
56     uptr page_size = GetPageSizeCached();
57     uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
58     mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
59   }
60   if (zone->zone_name) {
61     COMMON_MALLOC_FREE((void *)zone->zone_name);
62   }
63   COMMON_MALLOC_FREE(zone);
66 extern unsigned malloc_num_zones;
67 extern malloc_zone_t **malloc_zones;
69 // We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
70 // libmalloc tries to set up a different zone as malloc_zones[0], it will call
71 // mprotect(malloc_zones, ..., PROT_READ).  This interceptor will catch that and
72 // make sure we are still the first (default) zone.
73 INTERCEPTOR(int, mprotect, void *addr, size_t len, int prot) {
74   if (addr == malloc_zones && prot == PROT_READ) {
75     if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
76       for (unsigned i = 1; i < malloc_num_zones; i++) {
77         if (malloc_zones[i] == &sanitizer_zone) {
78           // Swap malloc_zones[0] and malloc_zones[i].
79           malloc_zones[i] = malloc_zones[0];
80           malloc_zones[0] = &sanitizer_zone;
81           break;
82         }
83       }
84     }
85   }
86   return REAL(mprotect)(addr, len, prot);
89 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
90   COMMON_MALLOC_ENTER();
91   return &sanitizer_zone;
94 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
95   // FIXME: ASan should support purgeable allocations.
96   // https://github.com/google/sanitizers/issues/139
97   COMMON_MALLOC_ENTER();
98   return &sanitizer_zone;
101 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
102   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
103   // for now.
104   COMMON_MALLOC_ENTER();
107 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
108   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
109   // for now.
110   COMMON_MALLOC_ENTER();
111   // Must return 0 if the contents were not purged since the last call to
112   // malloc_make_purgeable().
113   return 0;
116 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
117   COMMON_MALLOC_ENTER();
118   // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
119   // bytes.
120   size_t buflen =
121       sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
122   InternalScopedString new_name(buflen);
123   if (name && zone->introspect == sanitizer_zone.introspect) {
124     new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
125     name = new_name.data();
126   }
128   // Call the system malloc's implementation for both external and our zones,
129   // since that appropriately changes VM region protections on the zone.
130   REAL(malloc_set_zone_name)(zone, name);
133 INTERCEPTOR(void *, malloc, size_t size) {
134   COMMON_MALLOC_ENTER();
135   COMMON_MALLOC_MALLOC(size);
136   return p;
139 INTERCEPTOR(void, free, void *ptr) {
140   COMMON_MALLOC_ENTER();
141   if (!ptr) return;
142   COMMON_MALLOC_FREE(ptr);
145 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
146   COMMON_MALLOC_ENTER();
147   COMMON_MALLOC_REALLOC(ptr, size);
148   return p;
151 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
152   COMMON_MALLOC_ENTER();
153   COMMON_MALLOC_CALLOC(nmemb, size);
154   return p;
157 INTERCEPTOR(void *, valloc, size_t size) {
158   COMMON_MALLOC_ENTER();
159   COMMON_MALLOC_VALLOC(size);
160   return p;
163 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
164   COMMON_MALLOC_ENTER();
165   return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
168 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
169   COMMON_MALLOC_ENTER();
170   CHECK(memptr);
171   COMMON_MALLOC_MEMALIGN(alignment, size);
172   if (p) {
173     *memptr = p;
174     return 0;
175   }
176   return -1;
179 namespace {
181 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
182 // wrappers, as they are basically copied from there.
183 extern "C"
184 SANITIZER_INTERFACE_ATTRIBUTE
185 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
186   COMMON_MALLOC_SIZE(ptr);
187   return size;
190 extern "C"
191 SANITIZER_INTERFACE_ATTRIBUTE
192 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
193   COMMON_MALLOC_ENTER();
194   COMMON_MALLOC_MALLOC(size);
195   return p;
198 extern "C"
199 SANITIZER_INTERFACE_ATTRIBUTE
200 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
201   if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
202     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
203     const size_t kCallocPoolSize = 1024;
204     static uptr calloc_memory_for_dlsym[kCallocPoolSize];
205     static size_t allocated;
206     size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
207     void *mem = (void*)&calloc_memory_for_dlsym[allocated];
208     allocated += size_in_words;
209     CHECK(allocated < kCallocPoolSize);
210     return mem;
211   }
212   COMMON_MALLOC_CALLOC(nmemb, size);
213   return p;
216 extern "C"
217 SANITIZER_INTERFACE_ATTRIBUTE
218 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
219   COMMON_MALLOC_ENTER();
220   COMMON_MALLOC_VALLOC(size);
221   return p;
224 // TODO(glider): the allocation callbacks need to be refactored.
225 extern "C"
226 SANITIZER_INTERFACE_ATTRIBUTE
227 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
228   if (!ptr) return;
229   COMMON_MALLOC_FREE(ptr);
232 #define GET_ZONE_FOR_PTR(ptr) \
233   malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
234   const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
236 extern "C"
237 SANITIZER_INTERFACE_ATTRIBUTE
238 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
239   if (!ptr) {
240     COMMON_MALLOC_MALLOC(new_size);
241     return p;
242   } else {
243     COMMON_MALLOC_SIZE(ptr);
244     if (size) {
245       COMMON_MALLOC_REALLOC(ptr, new_size);
246       return p;
247     } else {
248       // We can't recover from reallocating an unknown address, because
249       // this would require reading at most |new_size| bytes from
250       // potentially unaccessible memory.
251       GET_ZONE_FOR_PTR(ptr);
252       COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
253       return nullptr;
254     }
255   }
258 extern "C"
259 SANITIZER_INTERFACE_ATTRIBUTE
260 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
261   // A no-op -- we will not be destroyed!
262   Report("__sanitizer_mz_destroy() called -- ignoring\n");
265 extern "C"
266 SANITIZER_INTERFACE_ATTRIBUTE
267 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
268   COMMON_MALLOC_ENTER();
269   COMMON_MALLOC_MEMALIGN(align, size);
270   return p;
273 // This function is currently unused, and we build with -Werror.
274 #if 0
275 void __sanitizer_mz_free_definite_size(
276     malloc_zone_t* zone, void *ptr, size_t size) {
277   // TODO(glider): check that |size| is valid.
278   UNIMPLEMENTED();
280 #endif
282 kern_return_t mi_enumerator(task_t task, void *,
283                             unsigned type_mask, vm_address_t zone_address,
284                             memory_reader_t reader,
285                             vm_range_recorder_t recorder) {
286   // Should enumerate all the pointers we have.  Seems like a lot of work.
287   return KERN_FAILURE;
290 size_t mi_good_size(malloc_zone_t *zone, size_t size) {
291   // I think it's always safe to return size, but we maybe could do better.
292   return size;
295 boolean_t mi_check(malloc_zone_t *zone) {
296   UNIMPLEMENTED();
299 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
300   UNIMPLEMENTED();
303 void mi_log(malloc_zone_t *zone, void *address) {
304   // I don't think we support anything like this
307 void mi_force_lock(malloc_zone_t *zone) {
308   COMMON_MALLOC_FORCE_LOCK();
311 void mi_force_unlock(malloc_zone_t *zone) {
312   COMMON_MALLOC_FORCE_UNLOCK();
315 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
316   COMMON_MALLOC_FILL_STATS(zone, stats);
319 boolean_t mi_zone_locked(malloc_zone_t *zone) {
320   // UNIMPLEMENTED();
321   return false;
324 }  // unnamed namespace
326 namespace COMMON_MALLOC_NAMESPACE {
328 void ReplaceSystemMalloc() {
329   static malloc_introspection_t sanitizer_zone_introspection;
330   // Ok to use internal_memset, these places are not performance-critical.
331   internal_memset(&sanitizer_zone_introspection, 0,
332                   sizeof(sanitizer_zone_introspection));
334   sanitizer_zone_introspection.enumerator = &mi_enumerator;
335   sanitizer_zone_introspection.good_size = &mi_good_size;
336   sanitizer_zone_introspection.check = &mi_check;
337   sanitizer_zone_introspection.print = &mi_print;
338   sanitizer_zone_introspection.log = &mi_log;
339   sanitizer_zone_introspection.force_lock = &mi_force_lock;
340   sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
341   sanitizer_zone_introspection.statistics = &mi_statistics;
342   sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
344   internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
346   // Use version 6 for OSX >= 10.6.
347   sanitizer_zone.version = 6;
348   sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
349   sanitizer_zone.size = &__sanitizer_mz_size;
350   sanitizer_zone.malloc = &__sanitizer_mz_malloc;
351   sanitizer_zone.calloc = &__sanitizer_mz_calloc;
352   sanitizer_zone.valloc = &__sanitizer_mz_valloc;
353   sanitizer_zone.free = &__sanitizer_mz_free;
354   sanitizer_zone.realloc = &__sanitizer_mz_realloc;
355   sanitizer_zone.destroy = &__sanitizer_mz_destroy;
356   sanitizer_zone.batch_malloc = 0;
357   sanitizer_zone.batch_free = 0;
358   sanitizer_zone.free_definite_size = 0;
359   sanitizer_zone.memalign = &__sanitizer_mz_memalign;
360   sanitizer_zone.introspect = &sanitizer_zone_introspection;
362   // Register the zone.
363   malloc_zone_register(&sanitizer_zone);
366 }  // namespace COMMON_MALLOC_NAMESPACE