1353944Sdim//===-- asan_malloc_linux.cpp ---------------------------------------------===// 2353944Sdim// 3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353944Sdim// See https://llvm.org/LICENSE.txt for license information. 5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353944Sdim// 7353944Sdim//===----------------------------------------------------------------------===// 8353944Sdim// 9353944Sdim// This file is a part of AddressSanitizer, an address sanity checker. 10353944Sdim// 11353944Sdim// Linux-specific malloc interception. 12353944Sdim// We simply define functions like malloc, free, realloc, etc. 13353944Sdim// They will replace the corresponding libc functions automagically. 14353944Sdim//===----------------------------------------------------------------------===// 15353944Sdim 16353944Sdim#include "sanitizer_common/sanitizer_platform.h" 17353944Sdim#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \ 18353944Sdim SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS 19353944Sdim 20353944Sdim#include "sanitizer_common/sanitizer_allocator_checks.h" 21353944Sdim#include "sanitizer_common/sanitizer_errno.h" 22353944Sdim#include "sanitizer_common/sanitizer_tls_get_addr.h" 23353944Sdim#include "asan_allocator.h" 24353944Sdim#include "asan_interceptors.h" 25353944Sdim#include "asan_internal.h" 26353944Sdim#include "asan_malloc_local.h" 27353944Sdim#include "asan_stack.h" 28353944Sdim 29353944Sdim// ---------------------- Replacement functions ---------------- {{{1 30353944Sdimusing namespace __asan; 31353944Sdim 32353944Sdimstatic uptr allocated_for_dlsym; 33353944Sdimstatic uptr last_dlsym_alloc_size_in_words; 34353944Sdimstatic const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024; 35353944Sdimstatic uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; 36353944Sdim 37353944Sdimstatic INLINE bool IsInDlsymAllocPool(const void *ptr) { 38353944Sdim uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; 39353944Sdim return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]); 40353944Sdim} 41353944Sdim 42353944Sdimstatic void *AllocateFromLocalPool(uptr size_in_bytes) { 43353944Sdim uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize; 44353944Sdim void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym]; 45353944Sdim last_dlsym_alloc_size_in_words = size_in_words; 46353944Sdim allocated_for_dlsym += size_in_words; 47353944Sdim CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize); 48353944Sdim return mem; 49353944Sdim} 50353944Sdim 51353944Sdimstatic void DeallocateFromLocalPool(const void *ptr) { 52353944Sdim // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store 53353944Sdim // error messages and instead uses malloc followed by free. To avoid pool 54353944Sdim // exhaustion due to long object filenames, handle that special case here. 55353944Sdim uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words; 56353944Sdim void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset]; 57353944Sdim if (prev_mem == ptr) { 58353944Sdim REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize); 59353944Sdim allocated_for_dlsym = prev_offset; 60353944Sdim last_dlsym_alloc_size_in_words = 0; 61353944Sdim } 62353944Sdim} 63353944Sdim 64353944Sdimstatic int PosixMemalignFromLocalPool(void **memptr, uptr alignment, 65353944Sdim uptr size_in_bytes) { 66353944Sdim if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) 67353944Sdim return errno_EINVAL; 68353944Sdim 69353944Sdim CHECK(alignment >= kWordSize); 70353944Sdim 71353944Sdim uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym]; 72353944Sdim uptr aligned_addr = RoundUpTo(addr, alignment); 73353944Sdim uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize); 74353944Sdim 75353944Sdim uptr *end_mem = (uptr*)(aligned_addr + aligned_size); 76353944Sdim uptr allocated = end_mem - alloc_memory_for_dlsym; 77353944Sdim if (allocated >= kDlsymAllocPoolSize) 78353944Sdim return errno_ENOMEM; 79353944Sdim 80353944Sdim allocated_for_dlsym = allocated; 81353944Sdim *memptr = (void*)aligned_addr; 82353944Sdim return 0; 83353944Sdim} 84353944Sdim 85353944Sdim#if SANITIZER_RTEMS 86353944Sdimvoid* MemalignFromLocalPool(uptr alignment, uptr size) { 87353944Sdim void *ptr = nullptr; 88353944Sdim alignment = Max(alignment, kWordSize); 89353944Sdim PosixMemalignFromLocalPool(&ptr, alignment, size); 90353944Sdim return ptr; 91353944Sdim} 92353944Sdim 93353944Sdimbool IsFromLocalPool(const void *ptr) { 94353944Sdim return IsInDlsymAllocPool(ptr); 95353944Sdim} 96353944Sdim#endif 97353944Sdim 98353944Sdimstatic INLINE bool MaybeInDlsym() { 99353944Sdim // Fuchsia doesn't use dlsym-based interceptors. 100353944Sdim return !SANITIZER_FUCHSIA && asan_init_is_running; 101353944Sdim} 102353944Sdim 103353944Sdimstatic INLINE bool UseLocalPool() { 104353944Sdim return EarlyMalloc() || MaybeInDlsym(); 105353944Sdim} 106353944Sdim 107353944Sdimstatic void *ReallocFromLocalPool(void *ptr, uptr size) { 108353944Sdim const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; 109353944Sdim const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); 110353944Sdim void *new_ptr; 111353944Sdim if (UNLIKELY(UseLocalPool())) { 112353944Sdim new_ptr = AllocateFromLocalPool(size); 113353944Sdim } else { 114353944Sdim ENSURE_ASAN_INITED(); 115353944Sdim GET_STACK_TRACE_MALLOC; 116353944Sdim new_ptr = asan_malloc(size, &stack); 117353944Sdim } 118353944Sdim internal_memcpy(new_ptr, ptr, copy_size); 119353944Sdim return new_ptr; 120353944Sdim} 121353944Sdim 122353944SdimINTERCEPTOR(void, free, void *ptr) { 123353944Sdim GET_STACK_TRACE_FREE; 124353944Sdim if (UNLIKELY(IsInDlsymAllocPool(ptr))) { 125353944Sdim DeallocateFromLocalPool(ptr); 126353944Sdim return; 127353944Sdim } 128353944Sdim asan_free(ptr, &stack, FROM_MALLOC); 129353944Sdim} 130353944Sdim 131353944Sdim#if SANITIZER_INTERCEPT_CFREE 132353944SdimINTERCEPTOR(void, cfree, void *ptr) { 133353944Sdim GET_STACK_TRACE_FREE; 134353944Sdim if (UNLIKELY(IsInDlsymAllocPool(ptr))) 135353944Sdim return; 136353944Sdim asan_free(ptr, &stack, FROM_MALLOC); 137353944Sdim} 138353944Sdim#endif // SANITIZER_INTERCEPT_CFREE 139353944Sdim 140353944SdimINTERCEPTOR(void*, malloc, uptr size) { 141353944Sdim if (UNLIKELY(UseLocalPool())) 142353944Sdim // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. 143353944Sdim return AllocateFromLocalPool(size); 144353944Sdim ENSURE_ASAN_INITED(); 145353944Sdim GET_STACK_TRACE_MALLOC; 146353944Sdim return asan_malloc(size, &stack); 147353944Sdim} 148353944Sdim 149353944SdimINTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { 150353944Sdim if (UNLIKELY(UseLocalPool())) 151353944Sdim // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. 152353944Sdim return AllocateFromLocalPool(nmemb * size); 153353944Sdim ENSURE_ASAN_INITED(); 154353944Sdim GET_STACK_TRACE_MALLOC; 155353944Sdim return asan_calloc(nmemb, size, &stack); 156353944Sdim} 157353944Sdim 158353944SdimINTERCEPTOR(void*, realloc, void *ptr, uptr size) { 159353944Sdim if (UNLIKELY(IsInDlsymAllocPool(ptr))) 160353944Sdim return ReallocFromLocalPool(ptr, size); 161353944Sdim if (UNLIKELY(UseLocalPool())) 162353944Sdim return AllocateFromLocalPool(size); 163353944Sdim ENSURE_ASAN_INITED(); 164353944Sdim GET_STACK_TRACE_MALLOC; 165353944Sdim return asan_realloc(ptr, size, &stack); 166353944Sdim} 167353944Sdim 168353944Sdim#if SANITIZER_INTERCEPT_REALLOCARRAY 169353944SdimINTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) { 170353944Sdim ENSURE_ASAN_INITED(); 171353944Sdim GET_STACK_TRACE_MALLOC; 172353944Sdim return asan_reallocarray(ptr, nmemb, size, &stack); 173353944Sdim} 174353944Sdim#endif // SANITIZER_INTERCEPT_REALLOCARRAY 175353944Sdim 176353944Sdim#if SANITIZER_INTERCEPT_MEMALIGN 177353944SdimINTERCEPTOR(void*, memalign, uptr boundary, uptr size) { 178353944Sdim GET_STACK_TRACE_MALLOC; 179353944Sdim return asan_memalign(boundary, size, &stack, FROM_MALLOC); 180353944Sdim} 181353944Sdim 182353944SdimINTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { 183353944Sdim GET_STACK_TRACE_MALLOC; 184353944Sdim void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); 185353944Sdim DTLS_on_libc_memalign(res, size); 186353944Sdim return res; 187353944Sdim} 188353944Sdim#endif // SANITIZER_INTERCEPT_MEMALIGN 189353944Sdim 190353944Sdim#if SANITIZER_INTERCEPT_ALIGNED_ALLOC 191353944SdimINTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) { 192353944Sdim GET_STACK_TRACE_MALLOC; 193353944Sdim return asan_aligned_alloc(boundary, size, &stack); 194353944Sdim} 195353944Sdim#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC 196353944Sdim 197353944SdimINTERCEPTOR(uptr, malloc_usable_size, void *ptr) { 198353944Sdim GET_CURRENT_PC_BP_SP; 199353944Sdim (void)sp; 200353944Sdim return asan_malloc_usable_size(ptr, pc, bp); 201353944Sdim} 202353944Sdim 203353944Sdim#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO 204353944Sdim// We avoid including malloc.h for portability reasons. 205353944Sdim// man mallinfo says the fields are "long", but the implementation uses int. 206353944Sdim// It doesn't matter much -- we just need to make sure that the libc's mallinfo 207353944Sdim// is not called. 208353944Sdimstruct fake_mallinfo { 209353944Sdim int x[10]; 210353944Sdim}; 211353944Sdim 212353944SdimINTERCEPTOR(struct fake_mallinfo, mallinfo, void) { 213353944Sdim struct fake_mallinfo res; 214353944Sdim REAL(memset)(&res, 0, sizeof(res)); 215353944Sdim return res; 216353944Sdim} 217353944Sdim 218353944SdimINTERCEPTOR(int, mallopt, int cmd, int value) { 219353944Sdim return 0; 220353944Sdim} 221353944Sdim#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO 222353944Sdim 223353944SdimINTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { 224353944Sdim if (UNLIKELY(UseLocalPool())) 225353944Sdim return PosixMemalignFromLocalPool(memptr, alignment, size); 226353944Sdim GET_STACK_TRACE_MALLOC; 227353944Sdim return asan_posix_memalign(memptr, alignment, size, &stack); 228353944Sdim} 229353944Sdim 230353944SdimINTERCEPTOR(void*, valloc, uptr size) { 231353944Sdim GET_STACK_TRACE_MALLOC; 232353944Sdim return asan_valloc(size, &stack); 233353944Sdim} 234353944Sdim 235353944Sdim#if SANITIZER_INTERCEPT_PVALLOC 236353944SdimINTERCEPTOR(void*, pvalloc, uptr size) { 237353944Sdim GET_STACK_TRACE_MALLOC; 238353944Sdim return asan_pvalloc(size, &stack); 239353944Sdim} 240353944Sdim#endif // SANITIZER_INTERCEPT_PVALLOC 241353944Sdim 242353944SdimINTERCEPTOR(void, malloc_stats, void) { 243353944Sdim __asan_print_accumulated_stats(); 244353944Sdim} 245353944Sdim 246353944Sdim#if SANITIZER_ANDROID 247353944Sdim// Format of __libc_malloc_dispatch has changed in Android L. 248353944Sdim// While we are moving towards a solution that does not depend on bionic 249353944Sdim// internals, here is something to support both K* and L releases. 250353944Sdimstruct MallocDebugK { 251353944Sdim void *(*malloc)(uptr bytes); 252353944Sdim void (*free)(void *mem); 253353944Sdim void *(*calloc)(uptr n_elements, uptr elem_size); 254353944Sdim void *(*realloc)(void *oldMem, uptr bytes); 255353944Sdim void *(*memalign)(uptr alignment, uptr bytes); 256353944Sdim uptr (*malloc_usable_size)(void *mem); 257353944Sdim}; 258353944Sdim 259353944Sdimstruct MallocDebugL { 260353944Sdim void *(*calloc)(uptr n_elements, uptr elem_size); 261353944Sdim void (*free)(void *mem); 262353944Sdim fake_mallinfo (*mallinfo)(void); 263353944Sdim void *(*malloc)(uptr bytes); 264353944Sdim uptr (*malloc_usable_size)(void *mem); 265353944Sdim void *(*memalign)(uptr alignment, uptr bytes); 266353944Sdim int (*posix_memalign)(void **memptr, uptr alignment, uptr size); 267353944Sdim void* (*pvalloc)(uptr size); 268353944Sdim void *(*realloc)(void *oldMem, uptr bytes); 269353944Sdim void* (*valloc)(uptr size); 270353944Sdim}; 271353944Sdim 272353944SdimALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = { 273353944Sdim WRAP(malloc), WRAP(free), WRAP(calloc), 274353944Sdim WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)}; 275353944Sdim 276353944SdimALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = { 277353944Sdim WRAP(calloc), WRAP(free), WRAP(mallinfo), 278353944Sdim WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign), 279353944Sdim WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc), 280353944Sdim WRAP(valloc)}; 281353944Sdim 282353944Sdimnamespace __asan { 283353944Sdimvoid ReplaceSystemMalloc() { 284353944Sdim void **__libc_malloc_dispatch_p = 285353944Sdim (void **)AsanDlSymNext("__libc_malloc_dispatch"); 286353944Sdim if (__libc_malloc_dispatch_p) { 287353944Sdim // Decide on K vs L dispatch format by the presence of 288353944Sdim // __libc_malloc_default_dispatch export in libc. 289353944Sdim void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch"); 290353944Sdim if (default_dispatch_p) 291353944Sdim *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k; 292353944Sdim else 293353944Sdim *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l; 294353944Sdim } 295353944Sdim} 296353944Sdim} // namespace __asan 297353944Sdim 298353944Sdim#else // SANITIZER_ANDROID 299353944Sdim 300353944Sdimnamespace __asan { 301353944Sdimvoid ReplaceSystemMalloc() { 302353944Sdim} 303353944Sdim} // namespace __asan 304353944Sdim#endif // SANITIZER_ANDROID 305353944Sdim 306353944Sdim#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || 307353944Sdim // SANITIZER_NETBSD || SANITIZER_SOLARIS 308