Home | History | Annotate | Line # | Download | only in asan
      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
    301