1353944Sdim//===-- asan_malloc_linux.cpp ---------------------------------------------===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// This file is a part of AddressSanitizer, an address sanity checker.
10353944Sdim//
11353944Sdim// Linux-specific malloc interception.
12353944Sdim// We simply define functions like malloc, free, realloc, etc.
13353944Sdim// They will replace the corresponding libc functions automagically.
14353944Sdim//===----------------------------------------------------------------------===//
15353944Sdim
16353944Sdim#include "sanitizer_common/sanitizer_platform.h"
17353944Sdim#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \
18353944Sdim    SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS
19353944Sdim
20353944Sdim#include "sanitizer_common/sanitizer_allocator_checks.h"
21353944Sdim#include "sanitizer_common/sanitizer_errno.h"
22353944Sdim#include "sanitizer_common/sanitizer_tls_get_addr.h"
23353944Sdim#include "asan_allocator.h"
24353944Sdim#include "asan_interceptors.h"
25353944Sdim#include "asan_internal.h"
26353944Sdim#include "asan_malloc_local.h"
27353944Sdim#include "asan_stack.h"
28353944Sdim
29353944Sdim// ---------------------- Replacement functions ---------------- {{{1
30353944Sdimusing namespace __asan;
31353944Sdim
32353944Sdimstatic uptr allocated_for_dlsym;
33353944Sdimstatic uptr last_dlsym_alloc_size_in_words;
34353944Sdimstatic const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024;
35353944Sdimstatic uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
36353944Sdim
37353944Sdimstatic INLINE bool IsInDlsymAllocPool(const void *ptr) {
38353944Sdim  uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
39353944Sdim  return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]);
40353944Sdim}
41353944Sdim
42353944Sdimstatic void *AllocateFromLocalPool(uptr size_in_bytes) {
43353944Sdim  uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
44353944Sdim  void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym];
45353944Sdim  last_dlsym_alloc_size_in_words = size_in_words;
46353944Sdim  allocated_for_dlsym += size_in_words;
47353944Sdim  CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
48353944Sdim  return mem;
49353944Sdim}
50353944Sdim
51353944Sdimstatic void DeallocateFromLocalPool(const void *ptr) {
52353944Sdim  // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store
53353944Sdim  // error messages and instead uses malloc followed by free. To avoid pool
54353944Sdim  // exhaustion due to long object filenames, handle that special case here.
55353944Sdim  uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words;
56353944Sdim  void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset];
57353944Sdim  if (prev_mem == ptr) {
58353944Sdim    REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize);
59353944Sdim    allocated_for_dlsym = prev_offset;
60353944Sdim    last_dlsym_alloc_size_in_words = 0;
61353944Sdim  }
62353944Sdim}
63353944Sdim
64353944Sdimstatic int PosixMemalignFromLocalPool(void **memptr, uptr alignment,
65353944Sdim                                      uptr size_in_bytes) {
66353944Sdim  if (UNLIKELY(!CheckPosixMemalignAlignment(alignment)))
67353944Sdim    return errno_EINVAL;
68353944Sdim
69353944Sdim  CHECK(alignment >= kWordSize);
70353944Sdim
71353944Sdim  uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym];
72353944Sdim  uptr aligned_addr = RoundUpTo(addr, alignment);
73353944Sdim  uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize);
74353944Sdim
75353944Sdim  uptr *end_mem = (uptr*)(aligned_addr + aligned_size);
76353944Sdim  uptr allocated = end_mem - alloc_memory_for_dlsym;
77353944Sdim  if (allocated >= kDlsymAllocPoolSize)
78353944Sdim    return errno_ENOMEM;
79353944Sdim
80353944Sdim  allocated_for_dlsym = allocated;
81353944Sdim  *memptr = (void*)aligned_addr;
82353944Sdim  return 0;
83353944Sdim}
84353944Sdim
85353944Sdim#if SANITIZER_RTEMS
86353944Sdimvoid* MemalignFromLocalPool(uptr alignment, uptr size) {
87353944Sdim  void *ptr = nullptr;
88353944Sdim  alignment = Max(alignment, kWordSize);
89353944Sdim  PosixMemalignFromLocalPool(&ptr, alignment, size);
90353944Sdim  return ptr;
91353944Sdim}
92353944Sdim
93353944Sdimbool IsFromLocalPool(const void *ptr) {
94353944Sdim  return IsInDlsymAllocPool(ptr);
95353944Sdim}
96353944Sdim#endif
97353944Sdim
98353944Sdimstatic INLINE bool MaybeInDlsym() {
99353944Sdim  // Fuchsia doesn't use dlsym-based interceptors.
100353944Sdim  return !SANITIZER_FUCHSIA && asan_init_is_running;
101353944Sdim}
102353944Sdim
103353944Sdimstatic INLINE bool UseLocalPool() {
104353944Sdim  return EarlyMalloc() || MaybeInDlsym();
105353944Sdim}
106353944Sdim
107353944Sdimstatic void *ReallocFromLocalPool(void *ptr, uptr size) {
108353944Sdim  const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
109353944Sdim  const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
110353944Sdim  void *new_ptr;
111353944Sdim  if (UNLIKELY(UseLocalPool())) {
112353944Sdim    new_ptr = AllocateFromLocalPool(size);
113353944Sdim  } else {
114353944Sdim    ENSURE_ASAN_INITED();
115353944Sdim    GET_STACK_TRACE_MALLOC;
116353944Sdim    new_ptr = asan_malloc(size, &stack);
117353944Sdim  }
118353944Sdim  internal_memcpy(new_ptr, ptr, copy_size);
119353944Sdim  return new_ptr;
120353944Sdim}
121353944Sdim
122353944SdimINTERCEPTOR(void, free, void *ptr) {
123353944Sdim  GET_STACK_TRACE_FREE;
124353944Sdim  if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
125353944Sdim    DeallocateFromLocalPool(ptr);
126353944Sdim    return;
127353944Sdim  }
128353944Sdim  asan_free(ptr, &stack, FROM_MALLOC);
129353944Sdim}
130353944Sdim
131353944Sdim#if SANITIZER_INTERCEPT_CFREE
132353944SdimINTERCEPTOR(void, cfree, void *ptr) {
133353944Sdim  GET_STACK_TRACE_FREE;
134353944Sdim  if (UNLIKELY(IsInDlsymAllocPool(ptr)))
135353944Sdim    return;
136353944Sdim  asan_free(ptr, &stack, FROM_MALLOC);
137353944Sdim}
138353944Sdim#endif // SANITIZER_INTERCEPT_CFREE
139353944Sdim
140353944SdimINTERCEPTOR(void*, malloc, uptr size) {
141353944Sdim  if (UNLIKELY(UseLocalPool()))
142353944Sdim    // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
143353944Sdim    return AllocateFromLocalPool(size);
144353944Sdim  ENSURE_ASAN_INITED();
145353944Sdim  GET_STACK_TRACE_MALLOC;
146353944Sdim  return asan_malloc(size, &stack);
147353944Sdim}
148353944Sdim
149353944SdimINTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
150353944Sdim  if (UNLIKELY(UseLocalPool()))
151353944Sdim    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
152353944Sdim    return AllocateFromLocalPool(nmemb * size);
153353944Sdim  ENSURE_ASAN_INITED();
154353944Sdim  GET_STACK_TRACE_MALLOC;
155353944Sdim  return asan_calloc(nmemb, size, &stack);
156353944Sdim}
157353944Sdim
158353944SdimINTERCEPTOR(void*, realloc, void *ptr, uptr size) {
159353944Sdim  if (UNLIKELY(IsInDlsymAllocPool(ptr)))
160353944Sdim    return ReallocFromLocalPool(ptr, size);
161353944Sdim  if (UNLIKELY(UseLocalPool()))
162353944Sdim    return AllocateFromLocalPool(size);
163353944Sdim  ENSURE_ASAN_INITED();
164353944Sdim  GET_STACK_TRACE_MALLOC;
165353944Sdim  return asan_realloc(ptr, size, &stack);
166353944Sdim}
167353944Sdim
168353944Sdim#if SANITIZER_INTERCEPT_REALLOCARRAY
169353944SdimINTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) {
170353944Sdim  ENSURE_ASAN_INITED();
171353944Sdim  GET_STACK_TRACE_MALLOC;
172353944Sdim  return asan_reallocarray(ptr, nmemb, size, &stack);
173353944Sdim}
174353944Sdim#endif  // SANITIZER_INTERCEPT_REALLOCARRAY
175353944Sdim
176353944Sdim#if SANITIZER_INTERCEPT_MEMALIGN
177353944SdimINTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
178353944Sdim  GET_STACK_TRACE_MALLOC;
179353944Sdim  return asan_memalign(boundary, size, &stack, FROM_MALLOC);
180353944Sdim}
181353944Sdim
182353944SdimINTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
183353944Sdim  GET_STACK_TRACE_MALLOC;
184353944Sdim  void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
185353944Sdim  DTLS_on_libc_memalign(res, size);
186353944Sdim  return res;
187353944Sdim}
188353944Sdim#endif // SANITIZER_INTERCEPT_MEMALIGN
189353944Sdim
190353944Sdim#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
191353944SdimINTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
192353944Sdim  GET_STACK_TRACE_MALLOC;
193353944Sdim  return asan_aligned_alloc(boundary, size, &stack);
194353944Sdim}
195353944Sdim#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC
196353944Sdim
197353944SdimINTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
198353944Sdim  GET_CURRENT_PC_BP_SP;
199353944Sdim  (void)sp;
200353944Sdim  return asan_malloc_usable_size(ptr, pc, bp);
201353944Sdim}
202353944Sdim
203353944Sdim#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
204353944Sdim// We avoid including malloc.h for portability reasons.
205353944Sdim// man mallinfo says the fields are "long", but the implementation uses int.
206353944Sdim// It doesn't matter much -- we just need to make sure that the libc's mallinfo
207353944Sdim// is not called.
208353944Sdimstruct fake_mallinfo {
209353944Sdim  int x[10];
210353944Sdim};
211353944Sdim
212353944SdimINTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
213353944Sdim  struct fake_mallinfo res;
214353944Sdim  REAL(memset)(&res, 0, sizeof(res));
215353944Sdim  return res;
216353944Sdim}
217353944Sdim
218353944SdimINTERCEPTOR(int, mallopt, int cmd, int value) {
219353944Sdim  return 0;
220353944Sdim}
221353944Sdim#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
222353944Sdim
223353944SdimINTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
224353944Sdim  if (UNLIKELY(UseLocalPool()))
225353944Sdim    return PosixMemalignFromLocalPool(memptr, alignment, size);
226353944Sdim  GET_STACK_TRACE_MALLOC;
227353944Sdim  return asan_posix_memalign(memptr, alignment, size, &stack);
228353944Sdim}
229353944Sdim
230353944SdimINTERCEPTOR(void*, valloc, uptr size) {
231353944Sdim  GET_STACK_TRACE_MALLOC;
232353944Sdim  return asan_valloc(size, &stack);
233353944Sdim}
234353944Sdim
235353944Sdim#if SANITIZER_INTERCEPT_PVALLOC
236353944SdimINTERCEPTOR(void*, pvalloc, uptr size) {
237353944Sdim  GET_STACK_TRACE_MALLOC;
238353944Sdim  return asan_pvalloc(size, &stack);
239353944Sdim}
240353944Sdim#endif // SANITIZER_INTERCEPT_PVALLOC
241353944Sdim
242353944SdimINTERCEPTOR(void, malloc_stats, void) {
243353944Sdim  __asan_print_accumulated_stats();
244353944Sdim}
245353944Sdim
246353944Sdim#if SANITIZER_ANDROID
247353944Sdim// Format of __libc_malloc_dispatch has changed in Android L.
248353944Sdim// While we are moving towards a solution that does not depend on bionic
249353944Sdim// internals, here is something to support both K* and L releases.
250353944Sdimstruct MallocDebugK {
251353944Sdim  void *(*malloc)(uptr bytes);
252353944Sdim  void (*free)(void *mem);
253353944Sdim  void *(*calloc)(uptr n_elements, uptr elem_size);
254353944Sdim  void *(*realloc)(void *oldMem, uptr bytes);
255353944Sdim  void *(*memalign)(uptr alignment, uptr bytes);
256353944Sdim  uptr (*malloc_usable_size)(void *mem);
257353944Sdim};
258353944Sdim
259353944Sdimstruct MallocDebugL {
260353944Sdim  void *(*calloc)(uptr n_elements, uptr elem_size);
261353944Sdim  void (*free)(void *mem);
262353944Sdim  fake_mallinfo (*mallinfo)(void);
263353944Sdim  void *(*malloc)(uptr bytes);
264353944Sdim  uptr (*malloc_usable_size)(void *mem);
265353944Sdim  void *(*memalign)(uptr alignment, uptr bytes);
266353944Sdim  int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
267353944Sdim  void* (*pvalloc)(uptr size);
268353944Sdim  void *(*realloc)(void *oldMem, uptr bytes);
269353944Sdim  void* (*valloc)(uptr size);
270353944Sdim};
271353944Sdim
272353944SdimALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
273353944Sdim    WRAP(malloc),  WRAP(free),     WRAP(calloc),
274353944Sdim    WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
275353944Sdim
276353944SdimALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
277353944Sdim    WRAP(calloc),         WRAP(free),               WRAP(mallinfo),
278353944Sdim    WRAP(malloc),         WRAP(malloc_usable_size), WRAP(memalign),
279353944Sdim    WRAP(posix_memalign), WRAP(pvalloc),            WRAP(realloc),
280353944Sdim    WRAP(valloc)};
281353944Sdim
282353944Sdimnamespace __asan {
283353944Sdimvoid ReplaceSystemMalloc() {
284353944Sdim  void **__libc_malloc_dispatch_p =
285353944Sdim      (void **)AsanDlSymNext("__libc_malloc_dispatch");
286353944Sdim  if (__libc_malloc_dispatch_p) {
287353944Sdim    // Decide on K vs L dispatch format by the presence of
288353944Sdim    // __libc_malloc_default_dispatch export in libc.
289353944Sdim    void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
290353944Sdim    if (default_dispatch_p)
291353944Sdim      *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
292353944Sdim    else
293353944Sdim      *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
294353944Sdim  }
295353944Sdim}
296353944Sdim}  // namespace __asan
297353944Sdim
298353944Sdim#else  // SANITIZER_ANDROID
299353944Sdim
300353944Sdimnamespace __asan {
301353944Sdimvoid ReplaceSystemMalloc() {
302353944Sdim}
303353944Sdim}  // namespace __asan
304353944Sdim#endif  // SANITIZER_ANDROID
305353944Sdim
306353944Sdim#endif  // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX ||
307353944Sdim        // SANITIZER_NETBSD || SANITIZER_SOLARIS
308