1//===-- wrappers_c.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#ifndef SCUDO_PREFIX
10#error "Define SCUDO_PREFIX prior to including this file!"
11#endif
12
13// malloc-type functions have to be aligned to std::max_align_t. This is
14// distinct from (1U << SCUDO_MIN_ALIGNMENT_LOG), since C++ new-type functions
15// do not have to abide by the same requirement.
16#ifndef SCUDO_MALLOC_ALIGNMENT
17#define SCUDO_MALLOC_ALIGNMENT FIRST_32_SECOND_64(8U, 16U)
18#endif
19
20extern "C" {
21
22INTERFACE WEAK void *SCUDO_PREFIX(calloc)(size_t nmemb, size_t size) {
23  scudo::uptr Product;
24  if (UNLIKELY(scudo::checkForCallocOverflow(size, nmemb, &Product))) {
25    if (SCUDO_ALLOCATOR.canReturnNull()) {
26      errno = ENOMEM;
27      return nullptr;
28    }
29    scudo::reportCallocOverflow(nmemb, size);
30  }
31  return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
32      Product, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT, true));
33}
34
35INTERFACE WEAK void SCUDO_PREFIX(free)(void *ptr) {
36  SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc);
37}
38
39INTERFACE WEAK struct SCUDO_MALLINFO SCUDO_PREFIX(mallinfo)(void) {
40  struct SCUDO_MALLINFO Info = {};
41  scudo::StatCounters Stats;
42  SCUDO_ALLOCATOR.getStats(Stats);
43  // Space allocated in mmapped regions (bytes)
44  Info.hblkhd = static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatMapped]);
45  // Maximum total allocated space (bytes)
46  Info.usmblks = Info.hblkhd;
47  // Space in freed fastbin blocks (bytes)
48  Info.fsmblks = static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatFree]);
49  // Total allocated space (bytes)
50  Info.uordblks =
51      static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatAllocated]);
52  // Total free space (bytes)
53  Info.fordblks = Info.fsmblks;
54  return Info;
55}
56
57INTERFACE WEAK void *SCUDO_PREFIX(malloc)(size_t size) {
58  return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
59      size, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT));
60}
61
62#if SCUDO_ANDROID
63INTERFACE WEAK size_t SCUDO_PREFIX(malloc_usable_size)(const void *ptr) {
64#else
65INTERFACE WEAK size_t SCUDO_PREFIX(malloc_usable_size)(void *ptr) {
66#endif
67  return SCUDO_ALLOCATOR.getUsableSize(ptr);
68}
69
70INTERFACE WEAK void *SCUDO_PREFIX(memalign)(size_t alignment, size_t size) {
71  // Android rounds up the alignment to a power of two if it isn't one.
72  if (SCUDO_ANDROID) {
73    if (UNLIKELY(!alignment)) {
74      alignment = 1U;
75    } else {
76      if (UNLIKELY(!scudo::isPowerOfTwo(alignment)))
77        alignment = scudo::roundUpToPowerOfTwo(alignment);
78    }
79  } else {
80    if (UNLIKELY(!scudo::isPowerOfTwo(alignment))) {
81      if (SCUDO_ALLOCATOR.canReturnNull()) {
82        errno = EINVAL;
83        return nullptr;
84      }
85      scudo::reportAlignmentNotPowerOfTwo(alignment);
86    }
87  }
88  return SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign,
89                                  alignment);
90}
91
92INTERFACE WEAK int SCUDO_PREFIX(posix_memalign)(void **memptr, size_t alignment,
93                                                size_t size) {
94  if (UNLIKELY(scudo::checkPosixMemalignAlignment(alignment))) {
95    if (!SCUDO_ALLOCATOR.canReturnNull())
96      scudo::reportInvalidPosixMemalignAlignment(alignment);
97    return EINVAL;
98  }
99  void *Ptr =
100      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment);
101  if (UNLIKELY(!Ptr))
102    return ENOMEM;
103  *memptr = Ptr;
104  return 0;
105}
106
107INTERFACE WEAK void *SCUDO_PREFIX(pvalloc)(size_t size) {
108  const scudo::uptr PageSize = scudo::getPageSizeCached();
109  if (UNLIKELY(scudo::checkForPvallocOverflow(size, PageSize))) {
110    if (SCUDO_ALLOCATOR.canReturnNull()) {
111      errno = ENOMEM;
112      return nullptr;
113    }
114    scudo::reportPvallocOverflow(size);
115  }
116  // pvalloc(0) should allocate one page.
117  return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
118      size ? scudo::roundUpTo(size, PageSize) : PageSize,
119      scudo::Chunk::Origin::Memalign, PageSize));
120}
121
122INTERFACE WEAK void *SCUDO_PREFIX(realloc)(void *ptr, size_t size) {
123  if (!ptr)
124    return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
125        size, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT));
126  if (size == 0) {
127    SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc);
128    return nullptr;
129  }
130  return scudo::setErrnoOnNull(
131      SCUDO_ALLOCATOR.reallocate(ptr, size, SCUDO_MALLOC_ALIGNMENT));
132}
133
134INTERFACE WEAK void *SCUDO_PREFIX(valloc)(size_t size) {
135  return scudo::setErrnoOnNull(SCUDO_ALLOCATOR.allocate(
136      size, scudo::Chunk::Origin::Memalign, scudo::getPageSizeCached()));
137}
138
139INTERFACE WEAK int SCUDO_PREFIX(malloc_iterate)(
140    uintptr_t base, size_t size,
141    void (*callback)(uintptr_t base, size_t size, void *arg), void *arg) {
142  SCUDO_ALLOCATOR.iterateOverChunks(base, size, callback, arg);
143  return 0;
144}
145
146INTERFACE WEAK void SCUDO_PREFIX(malloc_enable)() { SCUDO_ALLOCATOR.enable(); }
147
148INTERFACE WEAK void SCUDO_PREFIX(malloc_disable)() {
149  SCUDO_ALLOCATOR.disable();
150}
151
152void SCUDO_PREFIX(malloc_postinit)() {
153  SCUDO_ALLOCATOR.initGwpAsan();
154  pthread_atfork(SCUDO_PREFIX(malloc_disable), SCUDO_PREFIX(malloc_enable),
155                 SCUDO_PREFIX(malloc_enable));
156}
157
158INTERFACE WEAK int SCUDO_PREFIX(mallopt)(int param, int value) {
159  if (param == M_DECAY_TIME) {
160    if (SCUDO_ANDROID) {
161      if (value == 0) {
162        // Will set the release values to their minimum values.
163        value = INT32_MIN;
164      } else {
165        // Will set the release values to their maximum values.
166        value = INT32_MAX;
167      }
168    }
169
170    SCUDO_ALLOCATOR.setOption(scudo::Option::ReleaseInterval,
171                              static_cast<scudo::sptr>(value));
172    return 1;
173  } else if (param == M_PURGE) {
174    SCUDO_ALLOCATOR.releaseToOS();
175    return 1;
176  } else {
177    scudo::Option option;
178    switch (param) {
179    case M_MEMTAG_TUNING:
180      option = scudo::Option::MemtagTuning;
181      break;
182    case M_THREAD_DISABLE_MEM_INIT:
183      option = scudo::Option::ThreadDisableMemInit;
184      break;
185    case M_CACHE_COUNT_MAX:
186      option = scudo::Option::MaxCacheEntriesCount;
187      break;
188    case M_CACHE_SIZE_MAX:
189      option = scudo::Option::MaxCacheEntrySize;
190      break;
191    case M_TSDS_COUNT_MAX:
192      option = scudo::Option::MaxTSDsCount;
193      break;
194    default:
195      return 0;
196    }
197    return SCUDO_ALLOCATOR.setOption(option, static_cast<scudo::sptr>(value));
198  }
199}
200
201INTERFACE WEAK void *SCUDO_PREFIX(aligned_alloc)(size_t alignment,
202                                                 size_t size) {
203  if (UNLIKELY(scudo::checkAlignedAllocAlignmentAndSize(alignment, size))) {
204    if (SCUDO_ALLOCATOR.canReturnNull()) {
205      errno = EINVAL;
206      return nullptr;
207    }
208    scudo::reportInvalidAlignedAllocAlignment(alignment, size);
209  }
210  return scudo::setErrnoOnNull(
211      SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment));
212}
213
214INTERFACE WEAK int SCUDO_PREFIX(malloc_info)(UNUSED int options, FILE *stream) {
215  const scudo::uptr max_size =
216      decltype(SCUDO_ALLOCATOR)::PrimaryT::SizeClassMap::MaxSize;
217  auto *sizes = static_cast<scudo::uptr *>(
218      SCUDO_PREFIX(calloc)(max_size, sizeof(scudo::uptr)));
219  auto callback = [](uintptr_t, size_t size, void *arg) {
220    auto *sizes = reinterpret_cast<scudo::uptr *>(arg);
221    if (size < max_size)
222      sizes[size]++;
223  };
224  SCUDO_ALLOCATOR.iterateOverChunks(0, -1ul, callback, sizes);
225
226  fputs("<malloc version=\"scudo-1\">\n", stream);
227  for (scudo::uptr i = 0; i != max_size; ++i)
228    if (sizes[i])
229      fprintf(stream, "<alloc size=\"%lu\" count=\"%lu\"/>\n", i, sizes[i]);
230  fputs("</malloc>\n", stream);
231  SCUDO_PREFIX(free)(sizes);
232  return 0;
233}
234
235// Disable memory tagging for the heap. The caller must disable memory tag
236// checks globally (e.g. by clearing TCF0 on aarch64) before calling this
237// function, and may not re-enable them after calling the function.
238INTERFACE WEAK void SCUDO_PREFIX(malloc_disable_memory_tagging)() {
239  SCUDO_ALLOCATOR.disableMemoryTagging();
240}
241
242// Sets whether scudo records stack traces and other metadata for allocations
243// and deallocations. This function only has an effect if the allocator and
244// hardware support memory tagging.
245INTERFACE WEAK void
246SCUDO_PREFIX(malloc_set_track_allocation_stacks)(int track) {
247  SCUDO_ALLOCATOR.setTrackAllocationStacks(track);
248}
249
250// Sets whether scudo zero-initializes all allocated memory.
251INTERFACE WEAK void SCUDO_PREFIX(malloc_set_zero_contents)(int zero_contents) {
252  SCUDO_ALLOCATOR.setFillContents(zero_contents ? scudo::ZeroFill
253                                                : scudo::NoFill);
254}
255
256// Sets whether scudo pattern-initializes all allocated memory.
257INTERFACE WEAK void
258SCUDO_PREFIX(malloc_set_pattern_fill_contents)(int pattern_fill_contents) {
259  SCUDO_ALLOCATOR.setFillContents(
260      pattern_fill_contents ? scudo::PatternOrZeroFill : scudo::NoFill);
261}
262
263} // extern "C"
264