[Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303)
[llvm-project.git] / compiler-rt / lib / sanitizer_common / sanitizer_malloc_mac.inc
blob6343eb284afbf186f7078f01ffade6f58df77b8a
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.
12 //===----------------------------------------------------------------------===//
14 #include "sanitizer_common/sanitizer_platform.h"
15 #if !SANITIZER_APPLE
16 #error "This file should only be compiled on Darwin."
17 #endif
19 #include <AvailabilityMacros.h>
20 #include <CoreFoundation/CFBase.h>
21 #include <dlfcn.h>
22 #include <malloc/malloc.h>
23 #include <sys/mman.h>
25 #include "interception/interception.h"
26 #include "sanitizer_common/sanitizer_allocator_dlsym.h"
27 #include "sanitizer_common/sanitizer_mac.h"
29 // Similar code is used in Google Perftools,
30 // https://github.com/gperftools/gperftools.
32 namespace __sanitizer {
34 extern malloc_zone_t sanitizer_zone;
36 struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
37   // IMPORTANT: Do not change the order, alignment, or types of these fields to
38   // maintain binary compatibility. You should only add fields to this struct.
40   // Used to track changes to the allocator that will affect
41   // zone enumeration.
42   u64 allocator_enumeration_version;
43   uptr allocator_ptr;
44   uptr allocator_size;
47 u64 GetMallocZoneAllocatorEnumerationVersion() {
48   // This represents the current allocator ABI version.
49   // This field should be incremented every time the Allocator
50   // ABI changes in a way that breaks allocator enumeration.
51   return 0;
54 }  // namespace __sanitizer
56 INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
57                              vm_size_t start_size, unsigned zone_flags) {
58   COMMON_MALLOC_ENTER();
59   uptr page_size = GetPageSizeCached();
60   uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
61   COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
62   malloc_zone_t *new_zone = (malloc_zone_t *)p;
63   internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
64   new_zone->zone_name = NULL;  // The name will be changed anyway.
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   // We're explicitly *NOT* registering the zone.
70   return new_zone;
73 INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
74   COMMON_MALLOC_ENTER();
75   // We don't need to do anything here.  We're not registering new zones, so we
76   // don't to unregister.  Just un-mprotect and free() the zone.
77   uptr page_size = GetPageSizeCached();
78   uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
79   mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
80   if (zone->zone_name) {
81     COMMON_MALLOC_FREE((void *)zone->zone_name);
82   }
83   COMMON_MALLOC_FREE(zone);
86 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
87   COMMON_MALLOC_ENTER();
88   return &sanitizer_zone;
91 INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
92   COMMON_MALLOC_ENTER();
93   size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
94   if (size) { // Claimed by sanitizer zone?
95     return &sanitizer_zone;
96   }
97   return REAL(malloc_zone_from_ptr)(ptr);
100 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
101   // FIXME: ASan should support purgeable allocations.
102   // https://github.com/google/sanitizers/issues/139
103   COMMON_MALLOC_ENTER();
104   return &sanitizer_zone;
107 INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
108   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
109   // for now.
110   COMMON_MALLOC_ENTER();
113 INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
114   // FIXME: ASan should support purgeable allocations. Ignoring them is fine
115   // for now.
116   COMMON_MALLOC_ENTER();
117   // Must return 0 if the contents were not purged since the last call to
118   // malloc_make_purgeable().
119   return 0;
122 INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
123   COMMON_MALLOC_ENTER();
124   InternalScopedString new_name;
125   if (name && zone->introspect == sanitizer_zone.introspect) {
126     new_name.AppendF(COMMON_MALLOC_ZONE_NAME "-%s", name);
127     name = new_name.data();
128   }
130   // Call the system malloc's implementation for both external and our zones,
131   // since that appropriately changes VM region protections on the zone.
132   REAL(malloc_set_zone_name)(zone, name);
135 INTERCEPTOR(void *, malloc, size_t size) {
136   COMMON_MALLOC_ENTER();
137   COMMON_MALLOC_MALLOC(size);
138   return p;
141 INTERCEPTOR(void, free, void *ptr) {
142   COMMON_MALLOC_ENTER();
143   if (!ptr) return;
144   COMMON_MALLOC_FREE(ptr);
147 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
148   COMMON_MALLOC_ENTER();
149   COMMON_MALLOC_REALLOC(ptr, size);
150   return p;
153 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
154   COMMON_MALLOC_ENTER();
155   COMMON_MALLOC_CALLOC(nmemb, size);
156   return p;
159 INTERCEPTOR(void *, valloc, size_t size) {
160   COMMON_MALLOC_ENTER();
161   COMMON_MALLOC_VALLOC(size);
162   return p;
165 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
166   COMMON_MALLOC_ENTER();
167   return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
170 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
171   COMMON_MALLOC_ENTER();
172   CHECK(memptr);
173   COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
174   return res;
177 namespace {
179 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
180 // wrappers, as they are basically copied from there.
181 extern "C"
182 SANITIZER_INTERFACE_ATTRIBUTE
183 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
184   COMMON_MALLOC_SIZE(ptr);
185   return size;
188 extern "C"
189 SANITIZER_INTERFACE_ATTRIBUTE
190 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
191   COMMON_MALLOC_ENTER();
192   COMMON_MALLOC_MALLOC(size);
193   return p;
196 struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
197   static bool UseImpl() { return !COMMON_MALLOC_SANITIZER_INITIALIZED; }
200 extern "C"
201 SANITIZER_INTERFACE_ATTRIBUTE
202 void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
203   if (DlsymAlloc::Use())
204     return DlsymAlloc::Callocate(nmemb, size);
205   COMMON_MALLOC_CALLOC(nmemb, size);
206   return p;
209 extern "C"
210 SANITIZER_INTERFACE_ATTRIBUTE
211 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
212   COMMON_MALLOC_ENTER();
213   COMMON_MALLOC_VALLOC(size);
214   return p;
217 // TODO(glider): the allocation callbacks need to be refactored.
218 extern "C"
219 SANITIZER_INTERFACE_ATTRIBUTE
220 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
221   if (!ptr) return;
222   if (DlsymAlloc::PointerIsMine(ptr))
223     return DlsymAlloc::Free(ptr);
224   COMMON_MALLOC_FREE(ptr);
227 #define GET_ZONE_FOR_PTR(ptr) \
228   malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
229   const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
231 extern "C"
232 SANITIZER_INTERFACE_ATTRIBUTE
233 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
234   if (!ptr) {
235     COMMON_MALLOC_MALLOC(new_size);
236     return p;
237   } else {
238     COMMON_MALLOC_SIZE(ptr);
239     if (size) {
240       COMMON_MALLOC_REALLOC(ptr, new_size);
241       return p;
242     } else {
243       // We can't recover from reallocating an unknown address, because
244       // this would require reading at most |new_size| bytes from
245       // potentially unaccessible memory.
246       GET_ZONE_FOR_PTR(ptr);
247       COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
248       return nullptr;
249     }
250   }
253 extern "C"
254 SANITIZER_INTERFACE_ATTRIBUTE
255 void __sanitizer_mz_destroy(malloc_zone_t* zone) {
256   // A no-op -- we will not be destroyed!
257   Report("__sanitizer_mz_destroy() called -- ignoring\n");
260 extern "C"
261 SANITIZER_INTERFACE_ATTRIBUTE
262 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
263   COMMON_MALLOC_ENTER();
264   COMMON_MALLOC_MEMALIGN(align, size);
265   return p;
268 // This public API exists purely for testing purposes.
269 extern "C"
270 SANITIZER_INTERFACE_ATTRIBUTE
271 malloc_zone_t* __sanitizer_mz_default_zone() {
272   return &sanitizer_zone;
275 // This function is currently unused, and we build with -Werror.
276 #if 0
277 void __sanitizer_mz_free_definite_size(
278     malloc_zone_t* zone, void *ptr, size_t size) {
279   // TODO(glider): check that |size| is valid.
280   UNIMPLEMENTED();
282 #endif
284 #ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
285 #error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
286 #endif
287 static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
288                   (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
289               "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
291 #if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
292 // Forward declare and expect the implementation to provided by
293 // includer.
294 kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
295                             vm_address_t zone_address, memory_reader_t reader,
296                             vm_range_recorder_t recorder);
297 #else
298 // Provide stub implementation that fails.
299 kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
300                             vm_address_t zone_address, memory_reader_t reader,
301                             vm_range_recorder_t recorder) {
302   // Not supported.
303   return KERN_FAILURE;
305 #endif
307 #ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
308 #error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
309 #endif
310 static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
311                   (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
312               "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
313 #if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
314 // Forward declare and expect the implementation to provided by
315 // includer.
316 void mi_extra_init(
317     sanitizer_malloc_introspection_t *mi);
318 #else
319 void mi_extra_init(
320     sanitizer_malloc_introspection_t *mi) {
321   // Just zero initialize the fields.
322   mi->allocator_ptr = 0;
323   mi->allocator_size = 0;
325 #endif
327 size_t mi_good_size(malloc_zone_t *zone, size_t size) {
328   // I think it's always safe to return size, but we maybe could do better.
329   return size;
332 boolean_t mi_check(malloc_zone_t *zone) {
333   UNIMPLEMENTED();
336 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
337   UNIMPLEMENTED();
340 void mi_log(malloc_zone_t *zone, void *address) {
341   // I don't think we support anything like this
344 void mi_force_lock(malloc_zone_t *zone) {
345   COMMON_MALLOC_FORCE_LOCK();
348 void mi_force_unlock(malloc_zone_t *zone) {
349   COMMON_MALLOC_FORCE_UNLOCK();
352 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
353   COMMON_MALLOC_FILL_STATS(zone, stats);
356 boolean_t mi_zone_locked(malloc_zone_t *zone) {
357   // UNIMPLEMENTED();
358   return false;
361 }  // unnamed namespace
363 namespace COMMON_MALLOC_NAMESPACE {
365 void InitMallocZoneFields() {
366   static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
367   // Ok to use internal_memset, these places are not performance-critical.
368   internal_memset(&sanitizer_zone_introspection, 0,
369                   sizeof(sanitizer_zone_introspection));
371   sanitizer_zone_introspection.enumerator = &mi_enumerator;
372   sanitizer_zone_introspection.good_size = &mi_good_size;
373   sanitizer_zone_introspection.check = &mi_check;
374   sanitizer_zone_introspection.print = &mi_print;
375   sanitizer_zone_introspection.log = &mi_log;
376   sanitizer_zone_introspection.force_lock = &mi_force_lock;
377   sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
378   sanitizer_zone_introspection.statistics = &mi_statistics;
379   sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
381   // Set current allocator enumeration version.
382   sanitizer_zone_introspection.allocator_enumeration_version =
383       GetMallocZoneAllocatorEnumerationVersion();
385   // Perform any sanitizer specific initialization.
386   mi_extra_init(&sanitizer_zone_introspection);
388   internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
390   // Use version 6 for OSX >= 10.6.
391   sanitizer_zone.version = 6;
392   sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
393   sanitizer_zone.size = &__sanitizer_mz_size;
394   sanitizer_zone.malloc = &__sanitizer_mz_malloc;
395   sanitizer_zone.calloc = &__sanitizer_mz_calloc;
396   sanitizer_zone.valloc = &__sanitizer_mz_valloc;
397   sanitizer_zone.free = &__sanitizer_mz_free;
398   sanitizer_zone.realloc = &__sanitizer_mz_realloc;
399   sanitizer_zone.destroy = &__sanitizer_mz_destroy;
400   sanitizer_zone.batch_malloc = 0;
401   sanitizer_zone.batch_free = 0;
402   sanitizer_zone.free_definite_size = 0;
403   sanitizer_zone.memalign = &__sanitizer_mz_memalign;
404   sanitizer_zone.introspect = &sanitizer_zone_introspection;
407 void ReplaceSystemMalloc() {
408   InitMallocZoneFields();
410   // Register the zone.
411   malloc_zone_register(&sanitizer_zone);
414 }  // namespace COMMON_MALLOC_NAMESPACE