1238901Sandrew//===-- asan_malloc_linux.cc ----------------------------------------------===//
2229109Sed//
3229109Sed//                     The LLVM Compiler Infrastructure
4229109Sed//
5229109Sed// This file is distributed under the University of Illinois Open Source
6229109Sed// License. See LICENSE.TXT for details.
7229109Sed//
8229109Sed//===----------------------------------------------------------------------===//
9229109Sed//
10229109Sed// This file is a part of AddressSanitizer, an address sanity checker.
11229109Sed//
12229109Sed// Linux-specific malloc interception.
13229109Sed// We simply define functions like malloc, free, realloc, etc.
14229109Sed// They will replace the corresponding libc functions automagically.
15229109Sed//===----------------------------------------------------------------------===//
16229109Sed
17251034Sed#include "sanitizer_common/sanitizer_platform.h"
18276789Sdim#if SANITIZER_FREEBSD || SANITIZER_LINUX
19251034Sed
20276789Sdim#include "sanitizer_common/sanitizer_tls_get_addr.h"
21229109Sed#include "asan_allocator.h"
22229109Sed#include "asan_interceptors.h"
23229109Sed#include "asan_internal.h"
24229109Sed#include "asan_stack.h"
25229109Sed
26229109Sed// ---------------------- Replacement functions ---------------- {{{1
27229109Sedusing namespace __asan;  // NOLINT
28229109Sed
29296417Sdimstatic const uptr kCallocPoolSize = 1024;
30296417Sdimstatic uptr calloc_memory_for_dlsym[kCallocPoolSize];
31296417Sdim
32296417Sdimstatic bool IsInCallocPool(const void *ptr) {
33296417Sdim  sptr off = (sptr)ptr - (sptr)calloc_memory_for_dlsym;
34296417Sdim  return 0 <= off && off < (sptr)kCallocPoolSize;
35296417Sdim}
36296417Sdim
37238901SandrewINTERCEPTOR(void, free, void *ptr) {
38245614Sandrew  GET_STACK_TRACE_FREE;
39296417Sdim  if (UNLIKELY(IsInCallocPool(ptr)))
40296417Sdim    return;
41245614Sandrew  asan_free(ptr, &stack, FROM_MALLOC);
42229109Sed}
43229109Sed
44238901SandrewINTERCEPTOR(void, cfree, void *ptr) {
45245614Sandrew  GET_STACK_TRACE_FREE;
46296417Sdim  if (UNLIKELY(IsInCallocPool(ptr)))
47296417Sdim    return;
48245614Sandrew  asan_free(ptr, &stack, FROM_MALLOC);
49229109Sed}
50229109Sed
51238901SandrewINTERCEPTOR(void*, malloc, uptr size) {
52245614Sandrew  GET_STACK_TRACE_MALLOC;
53229109Sed  return asan_malloc(size, &stack);
54229109Sed}
55229109Sed
56238901SandrewINTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
57276789Sdim  if (UNLIKELY(!asan_inited)) {
58238901Sandrew    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
59238901Sandrew    static uptr allocated;
60238901Sandrew    uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
61229109Sed    void *mem = (void*)&calloc_memory_for_dlsym[allocated];
62229109Sed    allocated += size_in_words;
63229109Sed    CHECK(allocated < kCallocPoolSize);
64229109Sed    return mem;
65229109Sed  }
66245614Sandrew  GET_STACK_TRACE_MALLOC;
67229109Sed  return asan_calloc(nmemb, size, &stack);
68229109Sed}
69229109Sed
70238901SandrewINTERCEPTOR(void*, realloc, void *ptr, uptr size) {
71245614Sandrew  GET_STACK_TRACE_MALLOC;
72296417Sdim  if (UNLIKELY(IsInCallocPool(ptr))) {
73296417Sdim    uptr offset = (uptr)ptr - (uptr)calloc_memory_for_dlsym;
74296417Sdim    uptr copy_size = Min(size, kCallocPoolSize - offset);
75296417Sdim    void *new_ptr = asan_malloc(size, &stack);
76296417Sdim    internal_memcpy(new_ptr, ptr, copy_size);
77296417Sdim    return new_ptr;
78296417Sdim  }
79229109Sed  return asan_realloc(ptr, size, &stack);
80229109Sed}
81229109Sed
82238901SandrewINTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
83245614Sandrew  GET_STACK_TRACE_MALLOC;
84245614Sandrew  return asan_memalign(boundary, size, &stack, FROM_MALLOC);
85229109Sed}
86229109Sed
87276789SdimINTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
88276789Sdim  GET_STACK_TRACE_MALLOC;
89276789Sdim  return asan_memalign(boundary, size, &stack, FROM_MALLOC);
90276789Sdim}
91229109Sed
92276789SdimINTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
93276789Sdim  GET_STACK_TRACE_MALLOC;
94276789Sdim  void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
95276789Sdim  DTLS_on_libc_memalign(res, size * boundary);
96276789Sdim  return res;
97276789Sdim}
98276789Sdim
99238901SandrewINTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
100274201Sdim  GET_CURRENT_PC_BP_SP;
101274201Sdim  (void)sp;
102274201Sdim  return asan_malloc_usable_size(ptr, pc, bp);
103238901Sandrew}
104238901Sandrew
105238901Sandrew// We avoid including malloc.h for portability reasons.
106238901Sandrew// man mallinfo says the fields are "long", but the implementation uses int.
107238901Sandrew// It doesn't matter much -- we just need to make sure that the libc's mallinfo
108238901Sandrew// is not called.
109238901Sandrewstruct fake_mallinfo {
110238901Sandrew  int x[10];
111238901Sandrew};
112238901Sandrew
113238901SandrewINTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
114238901Sandrew  struct fake_mallinfo res;
115238901Sandrew  REAL(memset)(&res, 0, sizeof(res));
116229109Sed  return res;
117229109Sed}
118229109Sed
119238901SandrewINTERCEPTOR(int, mallopt, int cmd, int value) {
120229109Sed  return -1;
121229109Sed}
122229109Sed
123238901SandrewINTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
124245614Sandrew  GET_STACK_TRACE_MALLOC;
125238901Sandrew  // Printf("posix_memalign: %zx %zu\n", alignment, size);
126229109Sed  return asan_posix_memalign(memptr, alignment, size, &stack);
127229109Sed}
128229109Sed
129238901SandrewINTERCEPTOR(void*, valloc, uptr size) {
130245614Sandrew  GET_STACK_TRACE_MALLOC;
131229109Sed  return asan_valloc(size, &stack);
132229109Sed}
133229109Sed
134238901SandrewINTERCEPTOR(void*, pvalloc, uptr size) {
135245614Sandrew  GET_STACK_TRACE_MALLOC;
136229109Sed  return asan_pvalloc(size, &stack);
137229109Sed}
138229109Sed
139245614SandrewINTERCEPTOR(void, malloc_stats, void) {
140245614Sandrew  __asan_print_accumulated_stats();
141245614Sandrew}
142245614Sandrew
143276789Sdim#if SANITIZER_ANDROID
144276789Sdim// Format of __libc_malloc_dispatch has changed in Android L.
145276789Sdim// While we are moving towards a solution that does not depend on bionic
146276789Sdim// internals, here is something to support both K* and L releases.
147276789Sdimstruct MallocDebugK {
148276789Sdim  void *(*malloc)(uptr bytes);
149276789Sdim  void (*free)(void *mem);
150276789Sdim  void *(*calloc)(uptr n_elements, uptr elem_size);
151276789Sdim  void *(*realloc)(void *oldMem, uptr bytes);
152276789Sdim  void *(*memalign)(uptr alignment, uptr bytes);
153276789Sdim  uptr (*malloc_usable_size)(void *mem);
154276789Sdim};
155276789Sdim
156276789Sdimstruct MallocDebugL {
157276789Sdim  void *(*calloc)(uptr n_elements, uptr elem_size);
158276789Sdim  void (*free)(void *mem);
159276789Sdim  fake_mallinfo (*mallinfo)(void);
160276789Sdim  void *(*malloc)(uptr bytes);
161276789Sdim  uptr (*malloc_usable_size)(void *mem);
162276789Sdim  void *(*memalign)(uptr alignment, uptr bytes);
163276789Sdim  int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
164276789Sdim  void* (*pvalloc)(uptr size);
165276789Sdim  void *(*realloc)(void *oldMem, uptr bytes);
166276789Sdim  void* (*valloc)(uptr size);
167276789Sdim};
168276789Sdim
169276789SdimALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
170276789Sdim    WRAP(malloc),  WRAP(free),     WRAP(calloc),
171276789Sdim    WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
172276789Sdim
173276789SdimALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
174276789Sdim    WRAP(calloc),         WRAP(free),               WRAP(mallinfo),
175276789Sdim    WRAP(malloc),         WRAP(malloc_usable_size), WRAP(memalign),
176276789Sdim    WRAP(posix_memalign), WRAP(pvalloc),            WRAP(realloc),
177276789Sdim    WRAP(valloc)};
178276789Sdim
179276789Sdimnamespace __asan {
180276789Sdimvoid ReplaceSystemMalloc() {
181276789Sdim  void **__libc_malloc_dispatch_p =
182276789Sdim      (void **)AsanDlSymNext("__libc_malloc_dispatch");
183276789Sdim  if (__libc_malloc_dispatch_p) {
184276789Sdim    // Decide on K vs L dispatch format by the presence of
185276789Sdim    // __libc_malloc_default_dispatch export in libc.
186276789Sdim    void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
187276789Sdim    if (default_dispatch_p)
188276789Sdim      *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
189276789Sdim    else
190276789Sdim      *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
191276789Sdim  }
192276789Sdim}
193276789Sdim}  // namespace __asan
194276789Sdim
195276789Sdim#else  // SANITIZER_ANDROID
196276789Sdim
197276789Sdimnamespace __asan {
198276789Sdimvoid ReplaceSystemMalloc() {
199276789Sdim}
200276789Sdim}  // namespace __asan
201276789Sdim#endif  // SANITIZER_ANDROID
202276789Sdim
203276789Sdim#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
204