1//=-- lsan_interceptors.cc ------------------------------------------------===// 2// 3// This file is distributed under the University of Illinois Open Source 4// License. See LICENSE.TXT for details. 5// 6//===----------------------------------------------------------------------===// 7// 8// This file is a part of LeakSanitizer. 9// Interceptors for standalone LSan. 10// 11//===----------------------------------------------------------------------===// 12 13#include "sanitizer_common/sanitizer_allocator.h" 14#include "sanitizer_common/sanitizer_atomic.h" 15#include "sanitizer_common/sanitizer_common.h" 16#include "sanitizer_common/sanitizer_flags.h" 17#include "sanitizer_common/sanitizer_interception.h" 18#include "sanitizer_common/sanitizer_internal_defs.h" 19#include "sanitizer_common/sanitizer_linux.h" 20#include "sanitizer_common/sanitizer_platform_limits_posix.h" 21#include "lsan.h" 22#include "lsan_allocator.h" 23#include "lsan_thread.h" 24 25using namespace __lsan; 26 27extern "C" { 28int pthread_attr_init(void *attr); 29int pthread_attr_destroy(void *attr); 30int pthread_attr_getdetachstate(void *attr, int *v); 31int pthread_key_create(unsigned *key, void (*destructor)(void* v)); 32int pthread_setspecific(unsigned key, const void *v); 33} 34 35#define ENSURE_LSAN_INITED do { \ 36 CHECK(!lsan_init_is_running); \ 37 if (!lsan_inited) \ 38 __lsan_init(); \ 39} while (0) 40 41///// Malloc/free interceptors. ///// 42 43const bool kAlwaysClearMemory = true; 44 45namespace std { 46 struct nothrow_t; 47} 48 49INTERCEPTOR(void*, malloc, uptr size) { 50 ENSURE_LSAN_INITED; 51 GET_STACK_TRACE_MALLOC; 52 return Allocate(stack, size, 1, kAlwaysClearMemory); 53} 54 55INTERCEPTOR(void, free, void *p) { 56 ENSURE_LSAN_INITED; 57 Deallocate(p); 58} 59 60INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { 61 if (lsan_init_is_running) { 62 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. 63 const uptr kCallocPoolSize = 1024; 64 static uptr calloc_memory_for_dlsym[kCallocPoolSize]; 65 static uptr allocated; 66 uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; 67 void *mem = (void*)&calloc_memory_for_dlsym[allocated]; 68 allocated += size_in_words; 69 CHECK(allocated < kCallocPoolSize); 70 return mem; 71 } 72 if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; 73 ENSURE_LSAN_INITED; 74 GET_STACK_TRACE_MALLOC; 75 size *= nmemb; 76 return Allocate(stack, size, 1, true); 77} 78 79INTERCEPTOR(void*, realloc, void *q, uptr size) { 80 ENSURE_LSAN_INITED; 81 GET_STACK_TRACE_MALLOC; 82 return Reallocate(stack, q, size, 1); 83} 84 85INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { 86 ENSURE_LSAN_INITED; 87 GET_STACK_TRACE_MALLOC; 88 return Allocate(stack, size, alignment, kAlwaysClearMemory); 89} 90 91INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) { 92 ENSURE_LSAN_INITED; 93 GET_STACK_TRACE_MALLOC; 94 return Allocate(stack, size, alignment, kAlwaysClearMemory); 95} 96 97INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { 98 ENSURE_LSAN_INITED; 99 GET_STACK_TRACE_MALLOC; 100 *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); 101 // FIXME: Return ENOMEM if user requested more than max alloc size. 102 return 0; 103} 104 105INTERCEPTOR(void*, valloc, uptr size) { 106 ENSURE_LSAN_INITED; 107 GET_STACK_TRACE_MALLOC; 108 if (size == 0) 109 size = GetPageSizeCached(); 110 return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); 111} 112 113INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { 114 ENSURE_LSAN_INITED; 115 return GetMallocUsableSize(ptr); 116} 117 118struct fake_mallinfo { 119 int x[10]; 120}; 121 122INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { 123 struct fake_mallinfo res; 124 internal_memset(&res, 0, sizeof(res)); 125 return res; 126} 127 128INTERCEPTOR(int, mallopt, int cmd, int value) { 129 return -1; 130} 131 132INTERCEPTOR(void*, pvalloc, uptr size) { 133 ENSURE_LSAN_INITED; 134 GET_STACK_TRACE_MALLOC; 135 uptr PageSize = GetPageSizeCached(); 136 size = RoundUpTo(size, PageSize); 137 if (size == 0) { 138 // pvalloc(0) should allocate one page. 139 size = PageSize; 140 } 141 return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); 142} 143 144INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free)); 145 146#define OPERATOR_NEW_BODY \ 147 ENSURE_LSAN_INITED; \ 148 GET_STACK_TRACE_MALLOC; \ 149 return Allocate(stack, size, 1, kAlwaysClearMemory); 150 151INTERCEPTOR_ATTRIBUTE 152void *operator new(uptr size) { OPERATOR_NEW_BODY; } 153INTERCEPTOR_ATTRIBUTE 154void *operator new[](uptr size) { OPERATOR_NEW_BODY; } 155INTERCEPTOR_ATTRIBUTE 156void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } 157INTERCEPTOR_ATTRIBUTE 158void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } 159 160#define OPERATOR_DELETE_BODY \ 161 ENSURE_LSAN_INITED; \ 162 Deallocate(ptr); 163 164INTERCEPTOR_ATTRIBUTE 165void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } 166INTERCEPTOR_ATTRIBUTE 167void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } 168INTERCEPTOR_ATTRIBUTE 169void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } 170INTERCEPTOR_ATTRIBUTE 171void operator delete[](void *ptr, std::nothrow_t const &) { 172 OPERATOR_DELETE_BODY; 173} 174 175// We need this to intercept the __libc_memalign calls that are used to 176// allocate dynamic TLS space in ld-linux.so. 177INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) 178 ALIAS(WRAPPER_NAME(memalign)); 179 180///// Thread initialization and finalization. ///// 181 182static unsigned g_thread_finalize_key; 183 184static void thread_finalize(void *v) { 185 uptr iter = (uptr)v; 186 if (iter > 1) { 187 if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { 188 Report("LeakSanitizer: failed to set thread key.\n"); 189 Die(); 190 } 191 return; 192 } 193 ThreadFinish(); 194} 195 196struct ThreadParam { 197 void *(*callback)(void *arg); 198 void *param; 199 atomic_uintptr_t tid; 200}; 201 202extern "C" void *__lsan_thread_start_func(void *arg) { 203 ThreadParam *p = (ThreadParam*)arg; 204 void* (*callback)(void *arg) = p->callback; 205 void *param = p->param; 206 // Wait until the last iteration to maximize the chance that we are the last 207 // destructor to run. 208 if (pthread_setspecific(g_thread_finalize_key, 209 (void*)kPthreadDestructorIterations)) { 210 Report("LeakSanitizer: failed to set thread key.\n"); 211 Die(); 212 } 213 int tid = 0; 214 while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) 215 internal_sched_yield(); 216 atomic_store(&p->tid, 0, memory_order_release); 217 SetCurrentThread(tid); 218 ThreadStart(tid, GetTid()); 219 return callback(param); 220} 221 222INTERCEPTOR(int, pthread_create, void *th, void *attr, 223 void *(*callback)(void *), void *param) { 224 ENSURE_LSAN_INITED; 225 EnsureMainThreadIDIsCorrect(); 226 __sanitizer_pthread_attr_t myattr; 227 if (attr == 0) { 228 pthread_attr_init(&myattr); 229 attr = &myattr; 230 } 231 AdjustStackSize(attr); 232 int detached = 0; 233 pthread_attr_getdetachstate(attr, &detached); 234 ThreadParam p; 235 p.callback = callback; 236 p.param = param; 237 atomic_store(&p.tid, 0, memory_order_relaxed); 238 int res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p); 239 if (res == 0) { 240 int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, detached); 241 CHECK_NE(tid, 0); 242 atomic_store(&p.tid, tid, memory_order_release); 243 while (atomic_load(&p.tid, memory_order_acquire) != 0) 244 internal_sched_yield(); 245 } 246 if (attr == &myattr) 247 pthread_attr_destroy(&myattr); 248 return res; 249} 250 251INTERCEPTOR(int, pthread_join, void *th, void **ret) { 252 ENSURE_LSAN_INITED; 253 int tid = ThreadTid((uptr)th); 254 int res = REAL(pthread_join)(th, ret); 255 if (res == 0) 256 ThreadJoin(tid); 257 return res; 258} 259 260namespace __lsan { 261 262void InitializeInterceptors() { 263 INTERCEPT_FUNCTION(malloc); 264 INTERCEPT_FUNCTION(free); 265 INTERCEPT_FUNCTION(cfree); 266 INTERCEPT_FUNCTION(calloc); 267 INTERCEPT_FUNCTION(realloc); 268 INTERCEPT_FUNCTION(memalign); 269 INTERCEPT_FUNCTION(posix_memalign); 270 INTERCEPT_FUNCTION(__libc_memalign); 271 INTERCEPT_FUNCTION(valloc); 272 INTERCEPT_FUNCTION(pvalloc); 273 INTERCEPT_FUNCTION(malloc_usable_size); 274 INTERCEPT_FUNCTION(mallinfo); 275 INTERCEPT_FUNCTION(mallopt); 276 INTERCEPT_FUNCTION(pthread_create); 277 INTERCEPT_FUNCTION(pthread_join); 278 279 if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { 280 Report("LeakSanitizer: failed to create thread key.\n"); 281 Die(); 282 } 283} 284 285} // namespace __lsan 286