]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/hwasan/hwasan_interceptors.cc
Merge compiler-rt trunk r338150, and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / hwasan / hwasan_interceptors.cc
1 //===-- hwasan_interceptors.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 HWAddressSanitizer.
11 //
12 // Interceptors for standard library functions.
13 //
14 // FIXME: move as many interceptors as possible into
15 // sanitizer_common/sanitizer_common_interceptors.h
16 //===----------------------------------------------------------------------===//
17
18 #include "interception/interception.h"
19 #include "hwasan.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"
35
36 #include <stdarg.h>
37 // ACHTUNG! No other system header includes in this file.
38 // Ideally, we should get rid of stdarg.h as well.
39
40 using namespace __hwasan;
41
42 using __sanitizer::memory_order;
43 using __sanitizer::atomic_load;
44 using __sanitizer::atomic_store;
45 using __sanitizer::atomic_uintptr_t;
46
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)
51
52 bool IsInInterceptorScope() {
53   HwasanThread *t = GetCurrentThread();
54   return t && t->InInterceptorScope();
55 }
56
57 struct InterceptorScope {
58   InterceptorScope() {
59     HwasanThread *t = GetCurrentThread();
60     if (t)
61       t->EnterInterceptorScope();
62   }
63   ~InterceptorScope() {
64     HwasanThread *t = GetCurrentThread();
65     if (t)
66       t->LeaveInterceptorScope();
67   }
68 };
69
70 static uptr allocated_for_dlsym;
71 static const uptr kDlsymAllocPoolSize = 1024;
72 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
73
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);
77 }
78
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);
84   return mem;
85 }
86
87 #define ENSURE_HWASAN_INITED() do { \
88   CHECK(!hwasan_init_is_running); \
89   if (!hwasan_inited) { \
90     __hwasan_init(); \
91   } \
92 } while (0)
93
94
95
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)
100
101
102
103 // Check that [x, x+n) range is unpoisoned.
104 #define CHECK_UNPOISONED_0(x, n)                                       \
105   do {                                                                 \
106     sptr __offset = __hwasan_test_shadow(x, n);                         \
107     if (__hwasan::IsInSymbolizer()) break;                              \
108     if (__offset >= 0) {                                               \
109       GET_CALLER_PC_BP_SP;                                             \
110       (void)sp;                                                        \
111       ReportInvalidAccessInsideAddressRange(__func__, x, n, __offset); \
112       __hwasan::PrintWarning(pc, bp);                                   \
113       if (__hwasan::flags()->halt_on_error) {                           \
114         Printf("Exiting\n");                                           \
115         Die();                                                         \
116       }                                                                \
117     }                                                                  \
118   } while (0)
119
120 // Check that [x, x+n) range is unpoisoned unless we are in a nested
121 // interceptor.
122 #define CHECK_UNPOISONED(x, n)                             \
123   do {                                                     \
124     if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
125   } while (0)
126
127 #define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n)               \
128   CHECK_UNPOISONED((x),                                         \
129     common_flags()->strict_string_checks ? (len) + 1 : (n) )
130
131
132 INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
133   GET_MALLOC_STACK_TRACE;
134   CHECK_NE(memptr, 0);
135   int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
136   return res;
137 }
138
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);
143 }
144 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
145 #else
146 #define HWASAN_MAYBE_INTERCEPT_MEMALIGN
147 #endif
148
149 INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
150   GET_MALLOC_STACK_TRACE;
151   return hwasan_aligned_alloc(alignment, size, &stack);
152 }
153
154 INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
155   GET_MALLOC_STACK_TRACE;
156   void *ptr = hwasan_memalign(alignment, size, &stack);
157   if (ptr)
158     DTLS_on_libc_memalign(ptr, size);
159   return ptr;
160 }
161
162 INTERCEPTOR(void *, valloc, SIZE_T size) {
163   GET_MALLOC_STACK_TRACE;
164   return hwasan_valloc(size, &stack);
165 }
166
167 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
168 INTERCEPTOR(void *, pvalloc, SIZE_T size) {
169   GET_MALLOC_STACK_TRACE;
170   return hwasan_pvalloc(size, &stack);
171 }
172 #define HWASAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
173 #else
174 #define HWASAN_MAYBE_INTERCEPT_PVALLOC
175 #endif
176
177 INTERCEPTOR(void, free, void *ptr) {
178   GET_MALLOC_STACK_TRACE;
179   if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
180   HwasanDeallocate(&stack, ptr);
181 }
182
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);
188 }
189 #define HWASAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
190 #else
191 #define HWASAN_MAYBE_INTERCEPT_CFREE
192 #endif
193
194 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
195   return __sanitizer_get_allocated_size(ptr);
196 }
197
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
202 // to confirm that.
203 INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
204 #ifdef __aarch64__
205   uptr r8;
206   asm volatile("mov %0,x8" : "=r" (r8));
207   sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
208 #endif
209   REAL(memset)(sret, 0, sizeof(*sret));
210 }
211 #define HWASAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
212 #else
213 #define HWASAN_MAYBE_INTERCEPT_MALLINFO
214 #endif
215
216 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
217 INTERCEPTOR(int, mallopt, int cmd, int value) {
218   return -1;
219 }
220 #define HWASAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
221 #else
222 #define HWASAN_MAYBE_INTERCEPT_MALLOPT
223 #endif
224
225 #if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
226 INTERCEPTOR(void, malloc_stats, void) {
227   // FIXME: implement, but don't call REAL(malloc_stats)!
228 }
229 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats)
230 #else
231 #define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS
232 #endif
233
234
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);
241 }
242
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);
248     void *new_ptr;
249     if (UNLIKELY(!hwasan_inited)) {
250       new_ptr = AllocateFromLocalPool(copy_size);
251     } else {
252       copy_size = size;
253       new_ptr = hwasan_malloc(copy_size, &stack);
254     }
255     internal_memcpy(new_ptr, ptr, copy_size);
256     return new_ptr;
257   }
258   return hwasan_realloc(ptr, size, &stack);
259 }
260
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);
269 }
270
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;
277       return (void *)-1;
278     } else {
279       addr = nullptr;
280     }
281   }
282   return real_mmap(addr, sz, prot, flags, fd, off);
283 }
284
285 extern "C" int pthread_attr_init(void *attr);
286 extern "C" int pthread_attr_destroy(void *attr);
287
288 static void *HwasanThreadStartFunc(void *arg) {
289   HwasanThread *t = (HwasanThread *)arg;
290   SetCurrentThread(t);
291   return t->ThreadStart();
292 }
293
294 INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
295             void * param) {
296   ENSURE_HWASAN_INITED(); // for GetTlsSize()
297   __sanitizer_pthread_attr_t myattr;
298   if (!attr) {
299     pthread_attr_init(&myattr);
300     attr = &myattr;
301   }
302
303   AdjustStackSize(attr);
304
305   HwasanThread *t = HwasanThread::Create(callback, param);
306
307   int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
308
309   if (attr == &myattr)
310     pthread_attr_destroy(&myattr);
311   return res;
312 }
313
314 static void BeforeFork() {
315   StackDepotLockAll();
316 }
317
318 static void AfterFork() {
319   StackDepotUnlockAll();
320 }
321
322 INTERCEPTOR(int, fork, void) {
323   ENSURE_HWASAN_INITED();
324   BeforeFork();
325   int pid = REAL(fork)();
326   AfterFork();
327   return pid;
328 }
329
330
331 struct HwasanInterceptorContext {
332   bool in_interceptor_scope;
333 };
334
335 namespace __hwasan {
336
337 int OnExit() {
338   // FIXME: ask frontend whether we need to return failure.
339   return 0;
340 }
341
342 } // namespace __hwasan
343
344 // A version of CHECK_UNPOISONED using a saved scope value. Used in common
345 // interceptors.
346 #define CHECK_UNPOISONED_CTX(ctx, x, n)                         \
347   do {                                                          \
348     if (!((HwasanInterceptorContext *)ctx)->in_interceptor_scope) \
349       CHECK_UNPOISONED_0(x, n);                                 \
350   } while (0)
351
352 #define HWASAN_INTERCEPT_FUNC(name)                                       \
353   do {                                                                  \
354     if ((!INTERCEPT_FUNCTION(name) || !REAL(name)))                     \
355       VReport(1, "HWAddressSanitizer: failed to intercept '" #name "'\n"); \
356   } while (0)
357
358 #define HWASAN_INTERCEPT_FUNC_VER(name, ver)                                    \
359   do {                                                                        \
360     if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name)))                  \
361       VReport(                                                                \
362           1, "HWAddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
363   } while (0)
364
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;                                        \
379   (void)ctx;                                                      \
380   InterceptorScope interceptor_scope;
381 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
382   do {                                            \
383   } while (false)
384 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
385   do {                                         \
386   } while (false)
387 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
388   do {                                         \
389   } while (false)
390 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
391   do {                                                      \
392   } while (false)
393 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
394   do {                                                \
395   } while (false)  // FIXME
396 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
397   do {                                                         \
398   } while (false)  // FIXME
399 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
400 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
401
402 #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end)                           \
403   if (HwasanThread *t = GetCurrentThread()) {                                    \
404     *begin = t->tls_begin();                                                   \
405     *end = t->tls_end();                                                       \
406   } else {                                                                     \
407     *begin = *end = 0;                                                         \
408   }
409
410 #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
411   {                                                       \
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);                    \
417   }
418
419 #define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, fd, \
420                                      offset)                                   \
421   do {                                                                         \
422     return mmap_interceptor(REAL(mmap), addr, length, prot, flags, fd,         \
423                             offset);                                           \
424   } while (false)
425
426 #include "sanitizer_common/sanitizer_platform_interceptors.h"
427 #include "sanitizer_common/sanitizer_common_interceptors.inc"
428 #include "sanitizer_common/sanitizer_signal_interceptors.inc"
429
430 #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
431 #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
432   do {                                       \
433     (void)(p);                               \
434     (void)(s);                               \
435   } while (false)
436 #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
437   do {                                       \
438     (void)(p);                               \
439     (void)(s);                               \
440   } while (false)
441 #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
442   do {                                        \
443     (void)(p);                                \
444     (void)(s);                                \
445   } while (false)
446 #include "sanitizer_common/sanitizer_common_syscalls.inc"
447 #include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
448
449
450
451 namespace __hwasan {
452
453 void InitializeInterceptors() {
454   static int inited = 0;
455   CHECK_EQ(inited, 0);
456   InitializeCommonInterceptors();
457   InitializeSignalInterceptors();
458
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);
475
476   inited = 1;
477 }
478 } // namespace __hwasan