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