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_thread.h"
21 #include "hwasan_poisoning.h"
22 #include "sanitizer_common/sanitizer_platform_limits_posix.h"
23 #include "sanitizer_common/sanitizer_allocator.h"
24 #include "sanitizer_common/sanitizer_allocator_interface.h"
25 #include "sanitizer_common/sanitizer_allocator_internal.h"
26 #include "sanitizer_common/sanitizer_atomic.h"
27 #include "sanitizer_common/sanitizer_common.h"
28 #include "sanitizer_common/sanitizer_errno.h"
29 #include "sanitizer_common/sanitizer_stackdepot.h"
30 #include "sanitizer_common/sanitizer_libc.h"
31 #include "sanitizer_common/sanitizer_linux.h"
32 #include "sanitizer_common/sanitizer_tls_get_addr.h"
35 // ACHTUNG! No other system header includes in this file.
36 // Ideally, we should get rid of stdarg.h as well.
38 using namespace __hwasan;
40 using __sanitizer::memory_order;
41 using __sanitizer::atomic_load;
42 using __sanitizer::atomic_store;
43 using __sanitizer::atomic_uintptr_t;
45 DECLARE_REAL(SIZE_T, strlen, const char *s)
46 DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
47 DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
48 DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
50 bool IsInInterceptorScope() {
51 HwasanThread *t = GetCurrentThread();
52 return t && t->InInterceptorScope();
55 struct InterceptorScope {
57 HwasanThread *t = GetCurrentThread();
59 t->EnterInterceptorScope();
62 HwasanThread *t = GetCurrentThread();
64 t->LeaveInterceptorScope();
68 static uptr allocated_for_dlsym;
69 static const uptr kDlsymAllocPoolSize = 1024;
70 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
72 static bool IsInDlsymAllocPool(const void *ptr) {
73 uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
74 return off < sizeof(alloc_memory_for_dlsym);
77 static void *AllocateFromLocalPool(uptr size_in_bytes) {
78 uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
79 void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
80 allocated_for_dlsym += size_in_words;
81 CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
85 #define ENSURE_HWASAN_INITED() do { \
86 CHECK(!hwasan_init_is_running); \
87 if (!hwasan_inited) { \
94 #define HWASAN_READ_RANGE(ctx, offset, size) \
95 CHECK_UNPOISONED(offset, size)
96 #define HWASAN_WRITE_RANGE(ctx, offset, size) \
97 CHECK_UNPOISONED(offset, size)
101 // Check that [x, x+n) range is unpoisoned.
102 #define CHECK_UNPOISONED_0(x, n) \
104 sptr __offset = __hwasan_test_shadow(x, n); \
105 if (__hwasan::IsInSymbolizer()) break; \
106 if (__offset >= 0) { \
107 GET_CALLER_PC_BP_SP; \
109 ReportInvalidAccessInsideAddressRange(__func__, x, n, __offset); \
110 __hwasan::PrintWarning(pc, bp); \
111 if (__hwasan::flags()->halt_on_error) { \
112 Printf("Exiting\n"); \
118 // Check that [x, x+n) range is unpoisoned unless we are in a nested
120 #define CHECK_UNPOISONED(x, n) \
122 if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
125 #define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \
126 CHECK_UNPOISONED((x), \
127 common_flags()->strict_string_checks ? (len) + 1 : (n) )
130 INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
131 GET_MALLOC_STACK_TRACE;
133 int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
137 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
138 INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
139 GET_MALLOC_STACK_TRACE;
140 return hwasan_memalign(alignment, size, &stack);
142 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
144 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN
147 INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
148 GET_MALLOC_STACK_TRACE;
149 return hwasan_aligned_alloc(alignment, size, &stack);
152 INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
153 GET_MALLOC_STACK_TRACE;
154 void *ptr = hwasan_memalign(alignment, size, &stack);
156 DTLS_on_libc_memalign(ptr, size);
160 INTERCEPTOR(void *, valloc, SIZE_T size) {
161 GET_MALLOC_STACK_TRACE;
162 return hwasan_valloc(size, &stack);
165 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
166 INTERCEPTOR(void *, pvalloc, SIZE_T size) {
167 GET_MALLOC_STACK_TRACE;
168 return hwasan_pvalloc(size, &stack);
170 #define HWASAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
172 #define HWASAN_MAYBE_INTERCEPT_PVALLOC
175 INTERCEPTOR(void, free, void *ptr) {
176 GET_MALLOC_STACK_TRACE;
177 if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
178 HwasanDeallocate(&stack, ptr);
181 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
182 INTERCEPTOR(void, cfree, void *ptr) {
183 GET_MALLOC_STACK_TRACE;
184 if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
185 HwasanDeallocate(&stack, ptr);
187 #define HWASAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
189 #define HWASAN_MAYBE_INTERCEPT_CFREE
192 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
193 return __sanitizer_get_allocated_size(ptr);
196 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
197 // This function actually returns a struct by value, but we can't unpoison a
198 // temporary! The following is equivalent on all supported platforms but
199 // aarch64 (which uses a different register for sret value). We have a test
201 INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
204 asm volatile("mov %0,x8" : "=r" (r8));
205 sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
207 REAL(memset)(sret, 0, sizeof(*sret));
209 #define HWASAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
211 #define HWASAN_MAYBE_INTERCEPT_MALLINFO
214 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
215 INTERCEPTOR(int, mallopt, int cmd, int value) {
218 #define HWASAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
220 #define HWASAN_MAYBE_INTERCEPT_MALLOPT
223 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
224 INTERCEPTOR(void, malloc_stats, void) {
225 // FIXME: implement, but don't call REAL(malloc_stats)!
227 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats)
229 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS
233 INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
234 GET_MALLOC_STACK_TRACE;
235 if (UNLIKELY(!hwasan_inited))
236 // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
237 return AllocateFromLocalPool(nmemb * size);
238 return hwasan_calloc(nmemb, size, &stack);
241 INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
242 GET_MALLOC_STACK_TRACE;
243 if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
244 uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
245 uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
247 if (UNLIKELY(!hwasan_inited)) {
248 new_ptr = AllocateFromLocalPool(copy_size);
251 new_ptr = hwasan_malloc(copy_size, &stack);
253 internal_memcpy(new_ptr, ptr, copy_size);
256 return hwasan_realloc(ptr, size, &stack);
259 INTERCEPTOR(void *, malloc, SIZE_T size) {
260 GET_MALLOC_STACK_TRACE;
261 if (UNLIKELY(!hwasan_inited))
262 // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
263 return AllocateFromLocalPool(size);
264 return hwasan_malloc(size, &stack);
268 INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
269 int fd, OFF_T offset) {
270 if (hwasan_init_is_running)
271 return REAL(mmap)(addr, length, prot, flags, fd, offset);
272 ENSURE_HWASAN_INITED();
273 if (addr && !MEM_IS_APP(addr)) {
274 if (flags & map_fixed) {
275 errno = errno_EINVAL;
281 void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
285 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
286 INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
287 int fd, OFF64_T offset) {
288 ENSURE_HWASAN_INITED();
289 if (addr && !MEM_IS_APP(addr)) {
290 if (flags & map_fixed) {
291 errno = errno_EINVAL;
297 void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset);
300 #define HWASAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64)
302 #define HWASAN_MAYBE_INTERCEPT_MMAP64
305 extern "C" int pthread_attr_init(void *attr);
306 extern "C" int pthread_attr_destroy(void *attr);
308 static void *HwasanThreadStartFunc(void *arg) {
309 HwasanThread *t = (HwasanThread *)arg;
311 return t->ThreadStart();
314 INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
316 ENSURE_HWASAN_INITED(); // for GetTlsSize()
317 __sanitizer_pthread_attr_t myattr;
319 pthread_attr_init(&myattr);
323 AdjustStackSize(attr);
325 HwasanThread *t = HwasanThread::Create(callback, param);
327 int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
330 pthread_attr_destroy(&myattr);
334 static void BeforeFork() {
338 static void AfterFork() {
339 StackDepotUnlockAll();
342 INTERCEPTOR(int, fork, void) {
343 ENSURE_HWASAN_INITED();
345 int pid = REAL(fork)();
351 struct HwasanInterceptorContext {
352 bool in_interceptor_scope;
358 // FIXME: ask frontend whether we need to return failure.
362 } // namespace __hwasan
364 // A version of CHECK_UNPOISONED using a saved scope value. Used in common
366 #define CHECK_UNPOISONED_CTX(ctx, x, n) \
368 if (!((HwasanInterceptorContext *)ctx)->in_interceptor_scope) \
369 CHECK_UNPOISONED_0(x, n); \
372 #define HWASAN_INTERCEPT_FUNC(name) \
374 if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
375 VReport(1, "HWAddressSanitizer: failed to intercept '" #name "'\n"); \
378 #define HWASAN_INTERCEPT_FUNC_VER(name, ver) \
380 if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
382 1, "HWAddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
385 #define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
386 #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
387 HWASAN_INTERCEPT_FUNC_VER(name, ver)
388 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
389 CHECK_UNPOISONED_CTX(ctx, ptr, size)
390 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
391 CHECK_UNPOISONED_CTX(ctx, ptr, size)
392 #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
393 HWASAN_WRITE_RANGE(ctx, ptr, size)
394 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
395 if (hwasan_init_is_running) return REAL(func)(__VA_ARGS__); \
396 ENSURE_HWASAN_INITED(); \
397 HwasanInterceptorContext hwasan_ctx = {IsInInterceptorScope()}; \
398 ctx = (void *)&hwasan_ctx; \
400 InterceptorScope interceptor_scope;
401 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
404 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
407 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
410 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
413 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
415 } while (false) // FIXME
416 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
418 } while (false) // FIXME
419 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
420 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
422 #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
423 if (HwasanThread *t = GetCurrentThread()) { \
424 *begin = t->tls_begin(); \
425 *end = t->tls_end(); \
430 #include "sanitizer_common/sanitizer_platform_interceptors.h"
431 #include "sanitizer_common/sanitizer_common_interceptors.inc"
432 #include "sanitizer_common/sanitizer_signal_interceptors.inc"
434 #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
435 #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
440 #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
445 #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
450 #include "sanitizer_common/sanitizer_common_syscalls.inc"
456 void InitializeInterceptors() {
457 static int inited = 0;
459 InitializeCommonInterceptors();
460 InitializeSignalInterceptors();
462 INTERCEPT_FUNCTION(mmap);
463 HWASAN_MAYBE_INTERCEPT_MMAP64;
464 INTERCEPT_FUNCTION(posix_memalign);
465 HWASAN_MAYBE_INTERCEPT_MEMALIGN;
466 INTERCEPT_FUNCTION(__libc_memalign);
467 INTERCEPT_FUNCTION(valloc);
468 HWASAN_MAYBE_INTERCEPT_PVALLOC;
469 INTERCEPT_FUNCTION(malloc);
470 INTERCEPT_FUNCTION(calloc);
471 INTERCEPT_FUNCTION(realloc);
472 INTERCEPT_FUNCTION(free);
473 HWASAN_MAYBE_INTERCEPT_CFREE;
474 INTERCEPT_FUNCTION(malloc_usable_size);
475 HWASAN_MAYBE_INTERCEPT_MALLINFO;
476 HWASAN_MAYBE_INTERCEPT_MALLOPT;
477 HWASAN_MAYBE_INTERCEPT_MALLOC_STATS;
478 INTERCEPT_FUNCTION(pthread_create);
479 INTERCEPT_FUNCTION(fork);
483 } // namespace __hwasan