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