]> git.ipfire.org Git - thirdparty/gcc.git/blob - libsanitizer/sanitizer_common/sanitizer_malloc_mac.inc
Libsanitizer: merge from master.
[thirdparty/gcc.git] / libsanitizer / sanitizer_common / sanitizer_malloc_mac.inc
1 //===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains Mac-specific malloc interceptors and a custom zone
10 // implementation, which together replace the system allocator.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "sanitizer_common/sanitizer_platform.h"
15 #if !SANITIZER_MAC
16 #error "This file should only be compiled on Darwin."
17 #endif
18
19 #include <AvailabilityMacros.h>
20 #include <CoreFoundation/CFBase.h>
21 #include <dlfcn.h>
22 #include <malloc/malloc.h>
23 #include <sys/mman.h>
24
25 #include "interception/interception.h"
26 #include "sanitizer_common/sanitizer_mac.h"
27
28 // Similar code is used in Google Perftools,
29 // https://github.com/gperftools/gperftools.
30
31 namespace __sanitizer {
32
33 extern malloc_zone_t sanitizer_zone;
34
35 struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
36 // IMPORTANT: Do not change the order, alignment, or types of these fields to
37 // maintain binary compatibility. You should only add fields to this struct.
38
39 // Used to track changes to the allocator that will affect
40 // zone enumeration.
41 u64 allocator_enumeration_version;
42 uptr allocator_ptr;
43 uptr allocator_size;
44 };
45
46 u64 GetMallocZoneAllocatorEnumerationVersion() {
47 // This represents the current allocator ABI version.
48 // This field should be incremented every time the Allocator
49 // ABI changes in a way that breaks allocator enumeration.
50 return 0;
51 }
52
53 } // namespace __sanitizer
54
55 INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
56 vm_size_t start_size, unsigned zone_flags) {
57 COMMON_MALLOC_ENTER();
58 uptr page_size = GetPageSizeCached();
59 uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
60 COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
61 malloc_zone_t *new_zone = (malloc_zone_t *)p;
62 internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
63 new_zone->zone_name = NULL; // The name will be changed anyway.
64 // Prevent the client app from overwriting the zone contents.
65 // Library functions that need to modify the zone will set PROT_WRITE on it.
66 // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
67 mprotect(new_zone, allocated_size, PROT_READ);
68 // We're explicitly *NOT* registering the zone.
69 return new_zone;
70 }
71
72 INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
73 COMMON_MALLOC_ENTER();
74 // We don't need to do anything here. We're not registering new zones, so we
75 // don't to unregister. Just un-mprotect and free() the zone.
76 uptr page_size = GetPageSizeCached();
77 uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
78 mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
79 if (zone->zone_name) {
80 COMMON_MALLOC_FREE((void *)zone->zone_name);
81 }
82 COMMON_MALLOC_FREE(zone);
83 }
84
85 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
86 COMMON_MALLOC_ENTER();
87 return &sanitizer_zone;
88 }
89
90 INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
91 COMMON_MALLOC_ENTER();
92 size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
93 if (size) { // Claimed by sanitizer zone?
94 return &sanitizer_zone;
95 }
96 return REAL(malloc_zone_from_ptr)(ptr);
97 }
98
99 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
100 // FIXME: ASan should support purgeable allocations.
101 // https://github.com/google/sanitizers/issues/139
102 COMMON_MALLOC_ENTER();
103 return &sanitizer_zone;
104 }
105
106 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
107 // FIXME: ASan should support purgeable allocations. Ignoring them is fine
108 // for now.
109 COMMON_MALLOC_ENTER();
110 }
111
112 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
113 // FIXME: ASan should support purgeable allocations. Ignoring them is fine
114 // for now.
115 COMMON_MALLOC_ENTER();
116 // Must return 0 if the contents were not purged since the last call to
117 // malloc_make_purgeable().
118 return 0;
119 }
120
121 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
122 COMMON_MALLOC_ENTER();
123 // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
124 // bytes.
125 size_t buflen =
126 sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
127 InternalScopedString new_name(buflen);
128 if (name && zone->introspect == sanitizer_zone.introspect) {
129 new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
130 name = new_name.data();
131 }
132
133 // Call the system malloc's implementation for both external and our zones,
134 // since that appropriately changes VM region protections on the zone.
135 REAL(malloc_set_zone_name)(zone, name);
136 }
137
138 INTERCEPTOR(void *, malloc, size_t size) {
139 COMMON_MALLOC_ENTER();
140 COMMON_MALLOC_MALLOC(size);
141 return p;
142 }
143
144 INTERCEPTOR(void, free, void *ptr) {
145 COMMON_MALLOC_ENTER();
146 if (!ptr) return;
147 COMMON_MALLOC_FREE(ptr);
148 }
149
150 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
151 COMMON_MALLOC_ENTER();
152 COMMON_MALLOC_REALLOC(ptr, size);
153 return p;
154 }
155
156 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
157 COMMON_MALLOC_ENTER();
158 COMMON_MALLOC_CALLOC(nmemb, size);
159 return p;
160 }
161
162 INTERCEPTOR(void *, valloc, size_t size) {
163 COMMON_MALLOC_ENTER();
164 COMMON_MALLOC_VALLOC(size);
165 return p;
166 }
167
168 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
169 COMMON_MALLOC_ENTER();
170 return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
171 }
172
173 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
174 COMMON_MALLOC_ENTER();
175 CHECK(memptr);
176 COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
177 return res;
178 }
179
180 namespace {
181
182 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
183 // wrappers, as they are basically copied from there.
184 extern "C"
185 SANITIZER_INTERFACE_ATTRIBUTE
186 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
187 COMMON_MALLOC_SIZE(ptr);
188 return size;
189 }
190
191 extern "C"
192 SANITIZER_INTERFACE_ATTRIBUTE
193 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
194 COMMON_MALLOC_ENTER();
195 COMMON_MALLOC_MALLOC(size);
196 return p;
197 }
198
199 extern "C"
200 SANITIZER_INTERFACE_ATTRIBUTE
201 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
202 if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
203 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
204 const size_t kCallocPoolSize = 1024;
205 static uptr calloc_memory_for_dlsym[kCallocPoolSize];
206 static size_t allocated;
207 size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
208 void *mem = (void*)&calloc_memory_for_dlsym[allocated];
209 allocated += size_in_words;
210 CHECK(allocated < kCallocPoolSize);
211 return mem;
212 }
213 COMMON_MALLOC_CALLOC(nmemb, size);
214 return p;
215 }
216
217 extern "C"
218 SANITIZER_INTERFACE_ATTRIBUTE
219 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
220 COMMON_MALLOC_ENTER();
221 COMMON_MALLOC_VALLOC(size);
222 return p;
223 }
224
225 // TODO(glider): the allocation callbacks need to be refactored.
226 extern "C"
227 SANITIZER_INTERFACE_ATTRIBUTE
228 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
229 if (!ptr) return;
230 COMMON_MALLOC_FREE(ptr);
231 }
232
233 #define GET_ZONE_FOR_PTR(ptr) \
234 malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
235 const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
236
237 extern "C"
238 SANITIZER_INTERFACE_ATTRIBUTE
239 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
240 if (!ptr) {
241 COMMON_MALLOC_MALLOC(new_size);
242 return p;
243 } else {
244 COMMON_MALLOC_SIZE(ptr);
245 if (size) {
246 COMMON_MALLOC_REALLOC(ptr, new_size);
247 return p;
248 } else {
249 // We can't recover from reallocating an unknown address, because
250 // this would require reading at most |new_size| bytes from
251 // potentially unaccessible memory.
252 GET_ZONE_FOR_PTR(ptr);
253 COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
254 return nullptr;
255 }
256 }
257 }
258
259 extern "C"
260 SANITIZER_INTERFACE_ATTRIBUTE
261 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
262 // A no-op -- we will not be destroyed!
263 Report("__sanitizer_mz_destroy() called -- ignoring\n");
264 }
265
266 extern "C"
267 SANITIZER_INTERFACE_ATTRIBUTE
268 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
269 COMMON_MALLOC_ENTER();
270 COMMON_MALLOC_MEMALIGN(align, size);
271 return p;
272 }
273
274 // This public API exists purely for testing purposes.
275 extern "C"
276 SANITIZER_INTERFACE_ATTRIBUTE
277 malloc_zone_t* __sanitizer_mz_default_zone() {
278 return &sanitizer_zone;
279 }
280
281 // This function is currently unused, and we build with -Werror.
282 #if 0
283 void __sanitizer_mz_free_definite_size(
284 malloc_zone_t* zone, void *ptr, size_t size) {
285 // TODO(glider): check that |size| is valid.
286 UNIMPLEMENTED();
287 }
288 #endif
289
290 #ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
291 #error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
292 #endif
293 static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
294 (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
295 "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
296
297 #if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
298 // Forward declare and expect the implementation to provided by
299 // includer.
300 kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
301 vm_address_t zone_address, memory_reader_t reader,
302 vm_range_recorder_t recorder);
303 #else
304 // Provide stub implementation that fails.
305 kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
306 vm_address_t zone_address, memory_reader_t reader,
307 vm_range_recorder_t recorder) {
308 // Not supported.
309 return KERN_FAILURE;
310 }
311 #endif
312
313 #ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
314 #error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
315 #endif
316 static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
317 (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
318 "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
319 #if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
320 // Forward declare and expect the implementation to provided by
321 // includer.
322 void mi_extra_init(
323 sanitizer_malloc_introspection_t *mi);
324 #else
325 void mi_extra_init(
326 sanitizer_malloc_introspection_t *mi) {
327 // Just zero initialize the fields.
328 mi->allocator_ptr = 0;
329 mi->allocator_size = 0;
330 }
331 #endif
332
333 size_t mi_good_size(malloc_zone_t *zone, size_t size) {
334 // I think it's always safe to return size, but we maybe could do better.
335 return size;
336 }
337
338 boolean_t mi_check(malloc_zone_t *zone) {
339 UNIMPLEMENTED();
340 }
341
342 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
343 UNIMPLEMENTED();
344 }
345
346 void mi_log(malloc_zone_t *zone, void *address) {
347 // I don't think we support anything like this
348 }
349
350 void mi_force_lock(malloc_zone_t *zone) {
351 COMMON_MALLOC_FORCE_LOCK();
352 }
353
354 void mi_force_unlock(malloc_zone_t *zone) {
355 COMMON_MALLOC_FORCE_UNLOCK();
356 }
357
358 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
359 COMMON_MALLOC_FILL_STATS(zone, stats);
360 }
361
362 boolean_t mi_zone_locked(malloc_zone_t *zone) {
363 // UNIMPLEMENTED();
364 return false;
365 }
366
367 } // unnamed namespace
368
369 namespace COMMON_MALLOC_NAMESPACE {
370
371 void InitMallocZoneFields() {
372 static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
373 // Ok to use internal_memset, these places are not performance-critical.
374 internal_memset(&sanitizer_zone_introspection, 0,
375 sizeof(sanitizer_zone_introspection));
376
377 sanitizer_zone_introspection.enumerator = &mi_enumerator;
378 sanitizer_zone_introspection.good_size = &mi_good_size;
379 sanitizer_zone_introspection.check = &mi_check;
380 sanitizer_zone_introspection.print = &mi_print;
381 sanitizer_zone_introspection.log = &mi_log;
382 sanitizer_zone_introspection.force_lock = &mi_force_lock;
383 sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
384 sanitizer_zone_introspection.statistics = &mi_statistics;
385 sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
386
387 // Set current allocator enumeration version.
388 sanitizer_zone_introspection.allocator_enumeration_version =
389 GetMallocZoneAllocatorEnumerationVersion();
390
391 // Perform any sanitizer specific initialization.
392 mi_extra_init(&sanitizer_zone_introspection);
393
394 internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
395
396 // Use version 6 for OSX >= 10.6.
397 sanitizer_zone.version = 6;
398 sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
399 sanitizer_zone.size = &__sanitizer_mz_size;
400 sanitizer_zone.malloc = &__sanitizer_mz_malloc;
401 sanitizer_zone.calloc = &__sanitizer_mz_calloc;
402 sanitizer_zone.valloc = &__sanitizer_mz_valloc;
403 sanitizer_zone.free = &__sanitizer_mz_free;
404 sanitizer_zone.realloc = &__sanitizer_mz_realloc;
405 sanitizer_zone.destroy = &__sanitizer_mz_destroy;
406 sanitizer_zone.batch_malloc = 0;
407 sanitizer_zone.batch_free = 0;
408 sanitizer_zone.free_definite_size = 0;
409 sanitizer_zone.memalign = &__sanitizer_mz_memalign;
410 sanitizer_zone.introspect = &sanitizer_zone_introspection;
411 }
412
413 void ReplaceSystemMalloc() {
414 InitMallocZoneFields();
415
416 // Register the zone.
417 malloc_zone_register(&sanitizer_zone);
418 }
419
420 } // namespace COMMON_MALLOC_NAMESPACE