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