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