]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc
Merge ^/head r312894 through r312967.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_malloc_mac.inc
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 static malloc_zone_t sanitizer_zone;
33
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);
48   }
49   // We're explicitly *NOT* registering the zone.
50   return new_zone;
51 }
52
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);
61   }
62   COMMON_MALLOC_FREE(zone);
63 }
64
65 extern unsigned malloc_num_zones;
66 extern malloc_zone_t **malloc_zones;
67
68 // We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
69 // libmalloc tries to set up a different zone as malloc_zones[0], it will call
70 // mprotect(malloc_zones, ..., PROT_READ).  This interceptor will catch that and
71 // make sure we are still the first (default) zone.
72 INTERCEPTOR(int, mprotect, void *addr, size_t len, int prot) {
73   if (addr == malloc_zones && prot == PROT_READ) {
74     if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
75       for (unsigned i = 1; i < malloc_num_zones; i++) {
76         if (malloc_zones[i] == &sanitizer_zone) {
77           // Swap malloc_zones[0] and malloc_zones[i].
78           malloc_zones[i] = malloc_zones[0];
79           malloc_zones[0] = &sanitizer_zone;
80           break;
81         }
82       }
83     }
84   }
85   return REAL(mprotect)(addr, len, prot);
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_MEMALIGN(alignment, size);
171   if (p) {
172     *memptr = p;
173     return 0;
174   }
175   return -1;
176 }
177
178 namespace {
179
180 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
181 // wrappers, as they are basically copied from there.
182 extern "C"
183 SANITIZER_INTERFACE_ATTRIBUTE
184 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
185   COMMON_MALLOC_SIZE(ptr);
186   return size;
187 }
188
189 extern "C"
190 SANITIZER_INTERFACE_ATTRIBUTE
191 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
192   COMMON_MALLOC_ENTER();
193   COMMON_MALLOC_MALLOC(size);
194   return p;
195 }
196
197 extern "C"
198 SANITIZER_INTERFACE_ATTRIBUTE
199 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
200   if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
201     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
202     const size_t kCallocPoolSize = 1024;
203     static uptr calloc_memory_for_dlsym[kCallocPoolSize];
204     static size_t allocated;
205     size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
206     void *mem = (void*)&calloc_memory_for_dlsym[allocated];
207     allocated += size_in_words;
208     CHECK(allocated < kCallocPoolSize);
209     return mem;
210   }
211   COMMON_MALLOC_CALLOC(nmemb, size);
212   return p;
213 }
214
215 extern "C"
216 SANITIZER_INTERFACE_ATTRIBUTE
217 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
218   COMMON_MALLOC_ENTER();
219   COMMON_MALLOC_VALLOC(size);
220   return p;
221 }
222
223 // TODO(glider): the allocation callbacks need to be refactored.
224 extern "C"
225 SANITIZER_INTERFACE_ATTRIBUTE
226 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
227   if (!ptr) return;
228   COMMON_MALLOC_FREE(ptr);
229 }
230
231 #define GET_ZONE_FOR_PTR(ptr) \
232   malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
233   const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
234
235 extern "C"
236 SANITIZER_INTERFACE_ATTRIBUTE
237 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
238   if (!ptr) {
239     COMMON_MALLOC_MALLOC(new_size);
240     return p;
241   } else {
242     COMMON_MALLOC_SIZE(ptr);
243     if (size) {
244       COMMON_MALLOC_REALLOC(ptr, new_size);
245       return p;
246     } else {
247       // We can't recover from reallocating an unknown address, because
248       // this would require reading at most |new_size| bytes from
249       // potentially unaccessible memory.
250       GET_ZONE_FOR_PTR(ptr);
251       COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
252       return nullptr;
253     }
254   }
255 }
256
257 extern "C"
258 SANITIZER_INTERFACE_ATTRIBUTE
259 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
260   // A no-op -- we will not be destroyed!
261   Report("__sanitizer_mz_destroy() called -- ignoring\n");
262 }
263
264 extern "C"
265 SANITIZER_INTERFACE_ATTRIBUTE
266 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
267   COMMON_MALLOC_ENTER();
268   COMMON_MALLOC_MEMALIGN(align, size);
269   return p;
270 }
271
272 // This function is currently unused, and we build with -Werror.
273 #if 0
274 void __sanitizer_mz_free_definite_size(
275     malloc_zone_t* zone, void *ptr, size_t size) {
276   // TODO(glider): check that |size| is valid.
277   UNIMPLEMENTED();
278 }
279 #endif
280
281 kern_return_t mi_enumerator(task_t task, void *,
282                             unsigned type_mask, vm_address_t zone_address,
283                             memory_reader_t reader,
284                             vm_range_recorder_t recorder) {
285   // Should enumerate all the pointers we have.  Seems like a lot of work.
286   return KERN_FAILURE;
287 }
288
289 size_t mi_good_size(malloc_zone_t *zone, size_t size) {
290   // I think it's always safe to return size, but we maybe could do better.
291   return size;
292 }
293
294 boolean_t mi_check(malloc_zone_t *zone) {
295   UNIMPLEMENTED();
296 }
297
298 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
299   UNIMPLEMENTED();
300 }
301
302 void mi_log(malloc_zone_t *zone, void *address) {
303   // I don't think we support anything like this
304 }
305
306 void mi_force_lock(malloc_zone_t *zone) {
307   COMMON_MALLOC_FORCE_LOCK();
308 }
309
310 void mi_force_unlock(malloc_zone_t *zone) {
311   COMMON_MALLOC_FORCE_UNLOCK();
312 }
313
314 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
315   COMMON_MALLOC_FILL_STATS(zone, stats);
316 }
317
318 boolean_t mi_zone_locked(malloc_zone_t *zone) {
319   // UNIMPLEMENTED();
320   return false;
321 }
322
323 }  // unnamed namespace
324
325 namespace COMMON_MALLOC_NAMESPACE {
326
327 void ReplaceSystemMalloc() {
328   static malloc_introspection_t sanitizer_zone_introspection;
329   // Ok to use internal_memset, these places are not performance-critical.
330   internal_memset(&sanitizer_zone_introspection, 0,
331                   sizeof(sanitizer_zone_introspection));
332
333   sanitizer_zone_introspection.enumerator = &mi_enumerator;
334   sanitizer_zone_introspection.good_size = &mi_good_size;
335   sanitizer_zone_introspection.check = &mi_check;
336   sanitizer_zone_introspection.print = &mi_print;
337   sanitizer_zone_introspection.log = &mi_log;
338   sanitizer_zone_introspection.force_lock = &mi_force_lock;
339   sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
340   sanitizer_zone_introspection.statistics = &mi_statistics;
341   sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
342
343   internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
344
345   // Use version 6 for OSX >= 10.6.
346   sanitizer_zone.version = 6;
347   sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
348   sanitizer_zone.size = &__sanitizer_mz_size;
349   sanitizer_zone.malloc = &__sanitizer_mz_malloc;
350   sanitizer_zone.calloc = &__sanitizer_mz_calloc;
351   sanitizer_zone.valloc = &__sanitizer_mz_valloc;
352   sanitizer_zone.free = &__sanitizer_mz_free;
353   sanitizer_zone.realloc = &__sanitizer_mz_realloc;
354   sanitizer_zone.destroy = &__sanitizer_mz_destroy;
355   sanitizer_zone.batch_malloc = 0;
356   sanitizer_zone.batch_free = 0;
357   sanitizer_zone.free_definite_size = 0;
358   sanitizer_zone.memalign = &__sanitizer_mz_memalign;
359   sanitizer_zone.introspect = &sanitizer_zone_introspection;
360
361   // Register the zone.
362   malloc_zone_register(&sanitizer_zone);
363 }
364
365 }  // namespace COMMON_MALLOC_NAMESPACE