• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- asan_malloc_linux.cc ----------------------------------------------===//
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 is a part of AddressSanitizer, an address sanity checker.
11 //
12 // Linux-specific malloc interception.
13 // We simply define functions like malloc, free, realloc, etc.
14 // They will replace the corresponding libc functions automagically.
15 //===----------------------------------------------------------------------===//
16 
17 #include "sanitizer_common/sanitizer_platform.h"
18 #if SANITIZER_FREEBSD || SANITIZER_LINUX
19 
20 #include "sanitizer_common/sanitizer_tls_get_addr.h"
21 #include "asan_allocator.h"
22 #include "asan_interceptors.h"
23 #include "asan_internal.h"
24 #include "asan_stack.h"
25 
26 // ---------------------- Replacement functions ---------------- {{{1
27 using namespace __asan;  // NOLINT
28 
29 static uptr allocated_for_dlsym;
30 static const uptr kDlsymAllocPoolSize = 1024;
31 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
32 
IsInDlsymAllocPool(const void * ptr)33 static bool IsInDlsymAllocPool(const void *ptr) {
34   uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
35   return off < sizeof(alloc_memory_for_dlsym);
36 }
37 
AllocateFromLocalPool(uptr size_in_bytes)38 static void *AllocateFromLocalPool(uptr size_in_bytes) {
39   uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
40   void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym];
41   allocated_for_dlsym += size_in_words;
42   CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
43   return mem;
44 }
45 
INTERCEPTOR(void,free,void * ptr)46 INTERCEPTOR(void, free, void *ptr) {
47   GET_STACK_TRACE_FREE;
48   if (UNLIKELY(IsInDlsymAllocPool(ptr)))
49     return;
50   asan_free(ptr, &stack, FROM_MALLOC);
51 }
52 
INTERCEPTOR(void,cfree,void * ptr)53 INTERCEPTOR(void, cfree, void *ptr) {
54   GET_STACK_TRACE_FREE;
55   if (UNLIKELY(IsInDlsymAllocPool(ptr)))
56     return;
57   asan_free(ptr, &stack, FROM_MALLOC);
58 }
59 
INTERCEPTOR(void *,malloc,uptr size)60 INTERCEPTOR(void*, malloc, uptr size) {
61   if (UNLIKELY(!asan_inited))
62     // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
63     return AllocateFromLocalPool(size);
64   GET_STACK_TRACE_MALLOC;
65   return asan_malloc(size, &stack);
66 }
67 
INTERCEPTOR(void *,calloc,uptr nmemb,uptr size)68 INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
69   if (UNLIKELY(!asan_inited))
70     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
71     return AllocateFromLocalPool(nmemb * size);
72   GET_STACK_TRACE_MALLOC;
73   return asan_calloc(nmemb, size, &stack);
74 }
75 
INTERCEPTOR(void *,realloc,void * ptr,uptr size)76 INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
77   GET_STACK_TRACE_MALLOC;
78   if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
79     uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
80     uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
81     void *new_ptr = asan_malloc(size, &stack);
82     internal_memcpy(new_ptr, ptr, copy_size);
83     return new_ptr;
84   }
85   return asan_realloc(ptr, size, &stack);
86 }
87 
INTERCEPTOR(void *,memalign,uptr boundary,uptr size)88 INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
89   GET_STACK_TRACE_MALLOC;
90   return asan_memalign(boundary, size, &stack, FROM_MALLOC);
91 }
92 
INTERCEPTOR(void *,aligned_alloc,uptr boundary,uptr size)93 INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
94   GET_STACK_TRACE_MALLOC;
95   return asan_memalign(boundary, size, &stack, FROM_MALLOC);
96 }
97 
INTERCEPTOR(void *,__libc_memalign,uptr boundary,uptr size)98 INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
99   GET_STACK_TRACE_MALLOC;
100   void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
101   DTLS_on_libc_memalign(res, size);
102   return res;
103 }
104 
INTERCEPTOR(uptr,malloc_usable_size,void * ptr)105 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
106   GET_CURRENT_PC_BP_SP;
107   (void)sp;
108   return asan_malloc_usable_size(ptr, pc, bp);
109 }
110 
111 // We avoid including malloc.h for portability reasons.
112 // man mallinfo says the fields are "long", but the implementation uses int.
113 // It doesn't matter much -- we just need to make sure that the libc's mallinfo
114 // is not called.
115 struct fake_mallinfo {
116   int x[10];
117 };
118 
INTERCEPTOR(struct fake_mallinfo,mallinfo,void)119 INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
120   struct fake_mallinfo res;
121   REAL(memset)(&res, 0, sizeof(res));
122   return res;
123 }
124 
INTERCEPTOR(int,mallopt,int cmd,int value)125 INTERCEPTOR(int, mallopt, int cmd, int value) {
126   return -1;
127 }
128 
INTERCEPTOR(int,posix_memalign,void ** memptr,uptr alignment,uptr size)129 INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
130   GET_STACK_TRACE_MALLOC;
131   // Printf("posix_memalign: %zx %zu\n", alignment, size);
132   return asan_posix_memalign(memptr, alignment, size, &stack);
133 }
134 
INTERCEPTOR(void *,valloc,uptr size)135 INTERCEPTOR(void*, valloc, uptr size) {
136   GET_STACK_TRACE_MALLOC;
137   return asan_valloc(size, &stack);
138 }
139 
INTERCEPTOR(void *,pvalloc,uptr size)140 INTERCEPTOR(void*, pvalloc, uptr size) {
141   GET_STACK_TRACE_MALLOC;
142   return asan_pvalloc(size, &stack);
143 }
144 
INTERCEPTOR(void,malloc_stats,void)145 INTERCEPTOR(void, malloc_stats, void) {
146   __asan_print_accumulated_stats();
147 }
148 
149 #if SANITIZER_ANDROID
150 // Format of __libc_malloc_dispatch has changed in Android L.
151 // While we are moving towards a solution that does not depend on bionic
152 // internals, here is something to support both K* and L releases.
153 struct MallocDebugK {
154   void *(*malloc)(uptr bytes);
155   void (*free)(void *mem);
156   void *(*calloc)(uptr n_elements, uptr elem_size);
157   void *(*realloc)(void *oldMem, uptr bytes);
158   void *(*memalign)(uptr alignment, uptr bytes);
159   uptr (*malloc_usable_size)(void *mem);
160 };
161 
162 struct MallocDebugL {
163   void *(*calloc)(uptr n_elements, uptr elem_size);
164   void (*free)(void *mem);
165   fake_mallinfo (*mallinfo)(void);
166   void *(*malloc)(uptr bytes);
167   uptr (*malloc_usable_size)(void *mem);
168   void *(*memalign)(uptr alignment, uptr bytes);
169   int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
170   void* (*pvalloc)(uptr size);
171   void *(*realloc)(void *oldMem, uptr bytes);
172   void* (*valloc)(uptr size);
173 };
174 
175 ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
176     WRAP(malloc),  WRAP(free),     WRAP(calloc),
177     WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
178 
179 ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
180     WRAP(calloc),         WRAP(free),               WRAP(mallinfo),
181     WRAP(malloc),         WRAP(malloc_usable_size), WRAP(memalign),
182     WRAP(posix_memalign), WRAP(pvalloc),            WRAP(realloc),
183     WRAP(valloc)};
184 
185 namespace __asan {
ReplaceSystemMalloc()186 void ReplaceSystemMalloc() {
187   void **__libc_malloc_dispatch_p =
188       (void **)AsanDlSymNext("__libc_malloc_dispatch");
189   if (__libc_malloc_dispatch_p) {
190     // Decide on K vs L dispatch format by the presence of
191     // __libc_malloc_default_dispatch export in libc.
192     void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
193     if (default_dispatch_p)
194       *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
195     else
196       *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
197   }
198 }
199 }  // namespace __asan
200 
201 #else  // SANITIZER_ANDROID
202 
203 namespace __asan {
ReplaceSystemMalloc()204 void ReplaceSystemMalloc() {
205 }
206 }  // namespace __asan
207 #endif  // SANITIZER_ANDROID
208 
209 #endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
210