]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / compiler-rt / lib / 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   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   }
70   // We're explicitly *NOT* registering the zone.
71   return new_zone;
72 }
73
74 INTERCEPTOR(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
89 INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
90   COMMON_MALLOC_ENTER();
91   return &sanitizer_zone;
92 }
93
94 INTERCEPTOR(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
103 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
104   // FIXME: ASan should support purgeable allocations.
105   // https://github.com/google/sanitizers/issues/139
106   COMMON_MALLOC_ENTER();
107   return &sanitizer_zone;
108 }
109
110 INTERCEPTOR(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
116 INTERCEPTOR(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
125 INTERCEPTOR(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
142 INTERCEPTOR(void *, malloc, size_t size) {
143   COMMON_MALLOC_ENTER();
144   COMMON_MALLOC_MALLOC(size);
145   return p;
146 }
147
148 INTERCEPTOR(void, free, void *ptr) {
149   COMMON_MALLOC_ENTER();
150   if (!ptr) return;
151   COMMON_MALLOC_FREE(ptr);
152 }
153
154 INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
155   COMMON_MALLOC_ENTER();
156   COMMON_MALLOC_REALLOC(ptr, size);
157   return p;
158 }
159
160 INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
161   COMMON_MALLOC_ENTER();
162   COMMON_MALLOC_CALLOC(nmemb, size);
163   return p;
164 }
165
166 INTERCEPTOR(void *, valloc, size_t size) {
167   COMMON_MALLOC_ENTER();
168   COMMON_MALLOC_VALLOC(size);
169   return p;
170 }
171
172 INTERCEPTOR(size_t, malloc_good_size, size_t size) {
173   COMMON_MALLOC_ENTER();
174   return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
175 }
176
177 INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
178   COMMON_MALLOC_ENTER();
179   CHECK(memptr);
180   COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
181   return res;
182 }
183
184 namespace {
185
186 // TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
187 // wrappers, as they are basically copied from there.
188 extern "C"
189 SANITIZER_INTERFACE_ATTRIBUTE
190 size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
191   COMMON_MALLOC_SIZE(ptr);
192   return size;
193 }
194
195 extern "C"
196 SANITIZER_INTERFACE_ATTRIBUTE
197 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
198   COMMON_MALLOC_ENTER();
199   COMMON_MALLOC_MALLOC(size);
200   return p;
201 }
202
203 extern "C"
204 SANITIZER_INTERFACE_ATTRIBUTE
205 void *__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
221 extern "C"
222 SANITIZER_INTERFACE_ATTRIBUTE
223 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
224   COMMON_MALLOC_ENTER();
225   COMMON_MALLOC_VALLOC(size);
226   return p;
227 }
228
229 // TODO(glider): the allocation callbacks need to be refactored.
230 extern "C"
231 SANITIZER_INTERFACE_ATTRIBUTE
232 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
233   if (!ptr) return;
234   COMMON_MALLOC_FREE(ptr);
235 }
236
237 #define GET_ZONE_FOR_PTR(ptr) \
238   malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
239   const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
240
241 extern "C"
242 SANITIZER_INTERFACE_ATTRIBUTE
243 void *__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
263 extern "C"
264 SANITIZER_INTERFACE_ATTRIBUTE
265 void __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
270 extern "C"
271 SANITIZER_INTERFACE_ATTRIBUTE
272 void *__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
278 // This public API exists purely for testing purposes.
279 extern "C"
280 SANITIZER_INTERFACE_ATTRIBUTE
281 malloc_zone_t* __sanitizer_mz_default_zone() {
282   return &sanitizer_zone;
283 }
284
285 // This function is currently unused, and we build with -Werror.
286 #if 0
287 void __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
294 #ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
295 #error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
296 #endif
297 static_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.
304 kern_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.
309 kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
310                             vm_address_t zone_address, memory_reader_t reader,
311                             vm_range_recorder_t recorder) {
312   // Not supported.
313   return KERN_FAILURE;
314 }
315 #endif
316
317 #ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
318 #error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
319 #endif
320 static_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.
326 void mi_extra_init(
327     sanitizer_malloc_introspection_t *mi);
328 #else
329 void 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
336
337 size_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
342 boolean_t mi_check(malloc_zone_t *zone) {
343   UNIMPLEMENTED();
344 }
345
346 void mi_print(malloc_zone_t *zone, boolean_t verbose) {
347   UNIMPLEMENTED();
348 }
349
350 void mi_log(malloc_zone_t *zone, void *address) {
351   // I don't think we support anything like this
352 }
353
354 void mi_force_lock(malloc_zone_t *zone) {
355   COMMON_MALLOC_FORCE_LOCK();
356 }
357
358 void mi_force_unlock(malloc_zone_t *zone) {
359   COMMON_MALLOC_FORCE_UNLOCK();
360 }
361
362 void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
363   COMMON_MALLOC_FILL_STATS(zone, stats);
364 }
365
366 boolean_t mi_zone_locked(malloc_zone_t *zone) {
367   // UNIMPLEMENTED();
368   return false;
369 }
370
371 }  // unnamed namespace
372
373 namespace COMMON_MALLOC_NAMESPACE {
374
375 void InitMallocZoneFields() {
376   static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
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
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
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;
415 }
416
417 void ReplaceSystemMalloc() {
418   InitMallocZoneFields();
419
420   // Register the zone.
421   malloc_zone_register(&sanitizer_zone);
422 }
423
424 }  // namespace COMMON_MALLOC_NAMESPACE