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