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