]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/asan/asan_malloc_linux.cc
Import tzdata 2019a
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / asan / asan_malloc_linux.cc
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_FUCHSIA || SANITIZER_LINUX || \
19     SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS
20
21 #include "sanitizer_common/sanitizer_allocator_checks.h"
22 #include "sanitizer_common/sanitizer_errno.h"
23 #include "sanitizer_common/sanitizer_tls_get_addr.h"
24 #include "asan_allocator.h"
25 #include "asan_interceptors.h"
26 #include "asan_internal.h"
27 #include "asan_malloc_local.h"
28 #include "asan_stack.h"
29
30 // ---------------------- Replacement functions ---------------- {{{1
31 using namespace __asan;  // NOLINT
32
33 static uptr allocated_for_dlsym;
34 static uptr last_dlsym_alloc_size_in_words;
35 static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024;
36 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
37
38 static INLINE bool IsInDlsymAllocPool(const void *ptr) {
39   uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
40   return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]);
41 }
42
43 static void *AllocateFromLocalPool(uptr size_in_bytes) {
44   uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
45   void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym];
46   last_dlsym_alloc_size_in_words = size_in_words;
47   allocated_for_dlsym += size_in_words;
48   CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
49   return mem;
50 }
51
52 static void DeallocateFromLocalPool(const void *ptr) {
53   // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store
54   // error messages and instead uses malloc followed by free. To avoid pool
55   // exhaustion due to long object filenames, handle that special case here.
56   uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words;
57   void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset];
58   if (prev_mem == ptr) {
59     REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize);
60     allocated_for_dlsym = prev_offset;
61     last_dlsym_alloc_size_in_words = 0;
62   }
63 }
64
65 static int PosixMemalignFromLocalPool(void **memptr, uptr alignment,
66                                       uptr size_in_bytes) {
67   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment)))
68     return errno_EINVAL;
69
70   CHECK(alignment >= kWordSize);
71
72   uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym];
73   uptr aligned_addr = RoundUpTo(addr, alignment);
74   uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize);
75
76   uptr *end_mem = (uptr*)(aligned_addr + aligned_size);
77   uptr allocated = end_mem - alloc_memory_for_dlsym;
78   if (allocated >= kDlsymAllocPoolSize)
79     return errno_ENOMEM;
80
81   allocated_for_dlsym = allocated;
82   *memptr = (void*)aligned_addr;
83   return 0;
84 }
85
86 #if SANITIZER_RTEMS
87 void* MemalignFromLocalPool(uptr alignment, uptr size) {
88   void *ptr = nullptr;
89   alignment = Max(alignment, kWordSize);
90   PosixMemalignFromLocalPool(&ptr, alignment, size);
91   return ptr;
92 }
93
94 bool IsFromLocalPool(const void *ptr) {
95   return IsInDlsymAllocPool(ptr);
96 }
97 #endif
98
99 static INLINE bool MaybeInDlsym() {
100   // Fuchsia doesn't use dlsym-based interceptors.
101   return !SANITIZER_FUCHSIA && asan_init_is_running;
102 }
103
104 static INLINE bool UseLocalPool() {
105   return EarlyMalloc() || MaybeInDlsym();
106 }
107
108 static void *ReallocFromLocalPool(void *ptr, uptr size) {
109   const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
110   const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
111   void *new_ptr;
112   if (UNLIKELY(UseLocalPool())) {
113     new_ptr = AllocateFromLocalPool(size);
114   } else {
115     ENSURE_ASAN_INITED();
116     GET_STACK_TRACE_MALLOC;
117     new_ptr = asan_malloc(size, &stack);
118   }
119   internal_memcpy(new_ptr, ptr, copy_size);
120   return new_ptr;
121 }
122
123 INTERCEPTOR(void, free, void *ptr) {
124   GET_STACK_TRACE_FREE;
125   if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
126     DeallocateFromLocalPool(ptr);
127     return;
128   }
129   asan_free(ptr, &stack, FROM_MALLOC);
130 }
131
132 #if SANITIZER_INTERCEPT_CFREE
133 INTERCEPTOR(void, cfree, void *ptr) {
134   GET_STACK_TRACE_FREE;
135   if (UNLIKELY(IsInDlsymAllocPool(ptr)))
136     return;
137   asan_free(ptr, &stack, FROM_MALLOC);
138 }
139 #endif // SANITIZER_INTERCEPT_CFREE
140
141 INTERCEPTOR(void*, malloc, uptr size) {
142   if (UNLIKELY(UseLocalPool()))
143     // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
144     return AllocateFromLocalPool(size);
145   ENSURE_ASAN_INITED();
146   GET_STACK_TRACE_MALLOC;
147   return asan_malloc(size, &stack);
148 }
149
150 INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
151   if (UNLIKELY(UseLocalPool()))
152     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
153     return AllocateFromLocalPool(nmemb * size);
154   ENSURE_ASAN_INITED();
155   GET_STACK_TRACE_MALLOC;
156   return asan_calloc(nmemb, size, &stack);
157 }
158
159 INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
160   if (UNLIKELY(IsInDlsymAllocPool(ptr)))
161     return ReallocFromLocalPool(ptr, size);
162   if (UNLIKELY(UseLocalPool()))
163     return AllocateFromLocalPool(size);
164   ENSURE_ASAN_INITED();
165   GET_STACK_TRACE_MALLOC;
166   return asan_realloc(ptr, size, &stack);
167 }
168
169 #if SANITIZER_INTERCEPT_MEMALIGN
170 INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
171   GET_STACK_TRACE_MALLOC;
172   return asan_memalign(boundary, size, &stack, FROM_MALLOC);
173 }
174
175 INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
176   GET_STACK_TRACE_MALLOC;
177   void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
178   DTLS_on_libc_memalign(res, size);
179   return res;
180 }
181 #endif // SANITIZER_INTERCEPT_MEMALIGN
182
183 #if SANITIZER_INTERCEPT_ALIGNED_ALLOC
184 INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
185   GET_STACK_TRACE_MALLOC;
186   return asan_aligned_alloc(boundary, size, &stack);
187 }
188 #endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC
189
190 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
191   GET_CURRENT_PC_BP_SP;
192   (void)sp;
193   return asan_malloc_usable_size(ptr, pc, bp);
194 }
195
196 #if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
197 // We avoid including malloc.h for portability reasons.
198 // man mallinfo says the fields are "long", but the implementation uses int.
199 // It doesn't matter much -- we just need to make sure that the libc's mallinfo
200 // is not called.
201 struct fake_mallinfo {
202   int x[10];
203 };
204
205 INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
206   struct fake_mallinfo res;
207   REAL(memset)(&res, 0, sizeof(res));
208   return res;
209 }
210
211 INTERCEPTOR(int, mallopt, int cmd, int value) {
212   return 0;
213 }
214 #endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
215
216 INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
217   if (UNLIKELY(UseLocalPool()))
218     return PosixMemalignFromLocalPool(memptr, alignment, size);
219   GET_STACK_TRACE_MALLOC;
220   return asan_posix_memalign(memptr, alignment, size, &stack);
221 }
222
223 INTERCEPTOR(void*, valloc, uptr size) {
224   GET_STACK_TRACE_MALLOC;
225   return asan_valloc(size, &stack);
226 }
227
228 #if SANITIZER_INTERCEPT_PVALLOC
229 INTERCEPTOR(void*, pvalloc, uptr size) {
230   GET_STACK_TRACE_MALLOC;
231   return asan_pvalloc(size, &stack);
232 }
233 #endif // SANITIZER_INTERCEPT_PVALLOC
234
235 INTERCEPTOR(void, malloc_stats, void) {
236   __asan_print_accumulated_stats();
237 }
238
239 #if SANITIZER_ANDROID
240 // Format of __libc_malloc_dispatch has changed in Android L.
241 // While we are moving towards a solution that does not depend on bionic
242 // internals, here is something to support both K* and L releases.
243 struct MallocDebugK {
244   void *(*malloc)(uptr bytes);
245   void (*free)(void *mem);
246   void *(*calloc)(uptr n_elements, uptr elem_size);
247   void *(*realloc)(void *oldMem, uptr bytes);
248   void *(*memalign)(uptr alignment, uptr bytes);
249   uptr (*malloc_usable_size)(void *mem);
250 };
251
252 struct MallocDebugL {
253   void *(*calloc)(uptr n_elements, uptr elem_size);
254   void (*free)(void *mem);
255   fake_mallinfo (*mallinfo)(void);
256   void *(*malloc)(uptr bytes);
257   uptr (*malloc_usable_size)(void *mem);
258   void *(*memalign)(uptr alignment, uptr bytes);
259   int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
260   void* (*pvalloc)(uptr size);
261   void *(*realloc)(void *oldMem, uptr bytes);
262   void* (*valloc)(uptr size);
263 };
264
265 ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
266     WRAP(malloc),  WRAP(free),     WRAP(calloc),
267     WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
268
269 ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
270     WRAP(calloc),         WRAP(free),               WRAP(mallinfo),
271     WRAP(malloc),         WRAP(malloc_usable_size), WRAP(memalign),
272     WRAP(posix_memalign), WRAP(pvalloc),            WRAP(realloc),
273     WRAP(valloc)};
274
275 namespace __asan {
276 void ReplaceSystemMalloc() {
277   void **__libc_malloc_dispatch_p =
278       (void **)AsanDlSymNext("__libc_malloc_dispatch");
279   if (__libc_malloc_dispatch_p) {
280     // Decide on K vs L dispatch format by the presence of
281     // __libc_malloc_default_dispatch export in libc.
282     void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
283     if (default_dispatch_p)
284       *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
285     else
286       *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
287   }
288 }
289 }  // namespace __asan
290
291 #else  // SANITIZER_ANDROID
292
293 namespace __asan {
294 void ReplaceSystemMalloc() {
295 }
296 }  // namespace __asan
297 #endif  // SANITIZER_ANDROID
298
299 #endif  // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX ||
300         // SANITIZER_NETBSD || SANITIZER_SOLARIS