1 //===-- hwasan_interceptors.cc ----------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of HWAddressSanitizer.
12 // Interceptors for standard library functions.
14 // FIXME: move as many interceptors as possible into
15 // sanitizer_common/sanitizer_common_interceptors.h
16 //===----------------------------------------------------------------------===//
18 #include "interception/interception.h"
20 #include "hwasan_mapping.h"
21 #include "hwasan_thread.h"
22 #include "hwasan_poisoning.h"
23 #include "hwasan_report.h"
24 #include "sanitizer_common/sanitizer_platform_limits_posix.h"
25 #include "sanitizer_common/sanitizer_allocator.h"
26 #include "sanitizer_common/sanitizer_allocator_interface.h"
27 #include "sanitizer_common/sanitizer_allocator_internal.h"
28 #include "sanitizer_common/sanitizer_atomic.h"
29 #include "sanitizer_common/sanitizer_common.h"
30 #include "sanitizer_common/sanitizer_errno.h"
31 #include "sanitizer_common/sanitizer_stackdepot.h"
32 #include "sanitizer_common/sanitizer_libc.h"
33 #include "sanitizer_common/sanitizer_linux.h"
34 #include "sanitizer_common/sanitizer_tls_get_addr.h"
37 // ACHTUNG! No other system header includes in this file.
38 // Ideally, we should get rid of stdarg.h as well.
40 using namespace __hwasan;
42 using __sanitizer::memory_order;
43 using __sanitizer::atomic_load;
44 using __sanitizer::atomic_store;
45 using __sanitizer::atomic_uintptr_t;
47 DECLARE_REAL(SIZE_T, strlen, const char *s)
48 DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
49 DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
50 DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
52 bool IsInInterceptorScope() {
53 HwasanThread *t = GetCurrentThread();
54 return t && t->InInterceptorScope();
57 struct InterceptorScope {
59 HwasanThread *t = GetCurrentThread();
61 t->EnterInterceptorScope();
64 HwasanThread *t = GetCurrentThread();
66 t->LeaveInterceptorScope();
70 static uptr allocated_for_dlsym;
71 static const uptr kDlsymAllocPoolSize = 1024;
72 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
74 static bool IsInDlsymAllocPool(const void *ptr) {
75 uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
76 return off < sizeof(alloc_memory_for_dlsym);
79 static void *AllocateFromLocalPool(uptr size_in_bytes) {
80 uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
81 void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
82 allocated_for_dlsym += size_in_words;
83 CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
87 #define ENSURE_HWASAN_INITED() do { \
88 CHECK(!hwasan_init_is_running); \
89 if (!hwasan_inited) { \
96 #define HWASAN_READ_RANGE(ctx, offset, size) \
97 CHECK_UNPOISONED(offset, size)
98 #define HWASAN_WRITE_RANGE(ctx, offset, size) \
99 CHECK_UNPOISONED(offset, size)
103 // Check that [x, x+n) range is unpoisoned.
104 #define CHECK_UNPOISONED_0(x, n) \
106 sptr __offset = __hwasan_test_shadow(x, n); \
107 if (__hwasan::IsInSymbolizer()) break; \
108 if (__offset >= 0) { \
109 GET_CALLER_PC_BP_SP; \
111 ReportInvalidAccessInsideAddressRange(__func__, x, n, __offset); \
112 __hwasan::PrintWarning(pc, bp); \
113 if (__hwasan::flags()->halt_on_error) { \
114 Printf("Exiting\n"); \
120 // Check that [x, x+n) range is unpoisoned unless we are in a nested
122 #define CHECK_UNPOISONED(x, n) \
124 if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
127 #define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \
128 CHECK_UNPOISONED((x), \
129 common_flags()->strict_string_checks ? (len) + 1 : (n) )
132 INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
133 GET_MALLOC_STACK_TRACE;
135 int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
139 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
140 INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
141 GET_MALLOC_STACK_TRACE;
142 return hwasan_memalign(alignment, size, &stack);
144 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
146 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN
149 INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
150 GET_MALLOC_STACK_TRACE;
151 return hwasan_aligned_alloc(alignment, size, &stack);
154 INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
155 GET_MALLOC_STACK_TRACE;
156 void *ptr = hwasan_memalign(alignment, size, &stack);
158 DTLS_on_libc_memalign(ptr, size);
162 INTERCEPTOR(void *, valloc, SIZE_T size) {
163 GET_MALLOC_STACK_TRACE;
164 return hwasan_valloc(size, &stack);
167 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
168 INTERCEPTOR(void *, pvalloc, SIZE_T size) {
169 GET_MALLOC_STACK_TRACE;
170 return hwasan_pvalloc(size, &stack);
172 #define HWASAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
174 #define HWASAN_MAYBE_INTERCEPT_PVALLOC
177 INTERCEPTOR(void, free, void *ptr) {
178 GET_MALLOC_STACK_TRACE;
179 if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
180 HwasanDeallocate(&stack, ptr);
183 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
184 INTERCEPTOR(void, cfree, void *ptr) {
185 GET_MALLOC_STACK_TRACE;
186 if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
187 HwasanDeallocate(&stack, ptr);
189 #define HWASAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
191 #define HWASAN_MAYBE_INTERCEPT_CFREE
194 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
195 return __sanitizer_get_allocated_size(ptr);
198 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
199 // This function actually returns a struct by value, but we can't unpoison a
200 // temporary! The following is equivalent on all supported platforms but
201 // aarch64 (which uses a different register for sret value). We have a test
203 INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
206 asm volatile("mov %0,x8" : "=r" (r8));
207 sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
209 REAL(memset)(sret, 0, sizeof(*sret));
211 #define HWASAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
213 #define HWASAN_MAYBE_INTERCEPT_MALLINFO
216 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
217 INTERCEPTOR(int, mallopt, int cmd, int value) {
220 #define HWASAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
222 #define HWASAN_MAYBE_INTERCEPT_MALLOPT
225 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
226 INTERCEPTOR(void, malloc_stats, void) {
227 // FIXME: implement, but don't call REAL(malloc_stats)!
229 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats)
231 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS
235 INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
236 GET_MALLOC_STACK_TRACE;
237 if (UNLIKELY(!hwasan_inited))
238 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
239 return AllocateFromLocalPool(nmemb * size);
240 return hwasan_calloc(nmemb, size, &stack);
243 INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
244 GET_MALLOC_STACK_TRACE;
245 if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
246 uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
247 uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
249 if (UNLIKELY(!hwasan_inited)) {
250 new_ptr = AllocateFromLocalPool(copy_size);
253 new_ptr = hwasan_malloc(copy_size, &stack);
255 internal_memcpy(new_ptr, ptr, copy_size);
258 return hwasan_realloc(ptr, size, &stack);
261 INTERCEPTOR(void *, malloc, SIZE_T size) {
262 GET_MALLOC_STACK_TRACE;
263 if (UNLIKELY(!hwasan_init_is_running))
264 ENSURE_HWASAN_INITED();
265 if (UNLIKELY(!hwasan_inited))
266 // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
267 return AllocateFromLocalPool(size);
268 return hwasan_malloc(size, &stack);
271 template <class Mmap>
272 static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T sz, int prot,
273 int flags, int fd, OFF64_T off) {
274 if (addr && !MEM_IS_APP(addr)) {
275 if (flags & map_fixed) {
276 errno = errno_EINVAL;
282 return real_mmap(addr, sz, prot, flags, fd, off);
285 extern "C" int pthread_attr_init(void *attr);
286 extern "C" int pthread_attr_destroy(void *attr);
288 static void *HwasanThreadStartFunc(void *arg) {
289 HwasanThread *t = (HwasanThread *)arg;
291 return t->ThreadStart();
294 INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
296 ENSURE_HWASAN_INITED(); // for GetTlsSize()
297 __sanitizer_pthread_attr_t myattr;
299 pthread_attr_init(&myattr);
303 AdjustStackSize(attr);
305 HwasanThread *t = HwasanThread::Create(callback, param);
307 int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
310 pthread_attr_destroy(&myattr);
314 static void BeforeFork() {
318 static void AfterFork() {
319 StackDepotUnlockAll();
322 INTERCEPTOR(int, fork, void) {
323 ENSURE_HWASAN_INITED();
325 int pid = REAL(fork)();
331 struct HwasanInterceptorContext {
332 bool in_interceptor_scope;
338 // FIXME: ask frontend whether we need to return failure.
342 } // namespace __hwasan
344 // A version of CHECK_UNPOISONED using a saved scope value. Used in common
346 #define CHECK_UNPOISONED_CTX(ctx, x, n) \
348 if (!((HwasanInterceptorContext *)ctx)->in_interceptor_scope) \
349 CHECK_UNPOISONED_0(x, n); \
352 #define HWASAN_INTERCEPT_FUNC(name) \
354 if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
355 VReport(1, "HWAddressSanitizer: failed to intercept '" #name "'\n"); \
358 #define HWASAN_INTERCEPT_FUNC_VER(name, ver) \
360 if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
362 1, "HWAddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
365 #define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
366 #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
367 HWASAN_INTERCEPT_FUNC_VER(name, ver)
368 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
369 CHECK_UNPOISONED_CTX(ctx, ptr, size)
370 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
371 CHECK_UNPOISONED_CTX(ctx, ptr, size)
372 #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
373 HWASAN_WRITE_RANGE(ctx, ptr, size)
374 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
375 if (hwasan_init_is_running) return REAL(func)(__VA_ARGS__); \
376 ENSURE_HWASAN_INITED(); \
377 HwasanInterceptorContext hwasan_ctx = {IsInInterceptorScope()}; \
378 ctx = (void *)&hwasan_ctx; \
380 InterceptorScope interceptor_scope;
381 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
384 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
387 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
390 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
393 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
395 } while (false) // FIXME
396 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
398 } while (false) // FIXME
399 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
400 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
402 #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
403 if (HwasanThread *t = GetCurrentThread()) { \
404 *begin = t->tls_begin(); \
405 *end = t->tls_end(); \
410 #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
412 COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \
413 if (common_flags()->intercept_intrin && \
414 MEM_IS_APP(GetAddressFromPointer(dst))) \
415 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \
416 return REAL(memset)(dst, v, size); \
419 #define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, fd, \
422 return mmap_interceptor(REAL(mmap), addr, length, prot, flags, fd, \
426 #include "sanitizer_common/sanitizer_platform_interceptors.h"
427 #include "sanitizer_common/sanitizer_common_interceptors.inc"
428 #include "sanitizer_common/sanitizer_signal_interceptors.inc"
430 #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
431 #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
436 #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
441 #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
446 #include "sanitizer_common/sanitizer_common_syscalls.inc"
447 #include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
453 void InitializeInterceptors() {
454 static int inited = 0;
456 InitializeCommonInterceptors();
457 InitializeSignalInterceptors();
459 INTERCEPT_FUNCTION(posix_memalign);
460 HWASAN_MAYBE_INTERCEPT_MEMALIGN;
461 INTERCEPT_FUNCTION(__libc_memalign);
462 INTERCEPT_FUNCTION(valloc);
463 HWASAN_MAYBE_INTERCEPT_PVALLOC;
464 INTERCEPT_FUNCTION(malloc);
465 INTERCEPT_FUNCTION(calloc);
466 INTERCEPT_FUNCTION(realloc);
467 INTERCEPT_FUNCTION(free);
468 HWASAN_MAYBE_INTERCEPT_CFREE;
469 INTERCEPT_FUNCTION(malloc_usable_size);
470 HWASAN_MAYBE_INTERCEPT_MALLINFO;
471 HWASAN_MAYBE_INTERCEPT_MALLOPT;
472 HWASAN_MAYBE_INTERCEPT_MALLOC_STATS;
473 INTERCEPT_FUNCTION(pthread_create);
474 INTERCEPT_FUNCTION(fork);
478 } // namespace __hwasan