forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			550 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			550 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- esan_interceptors.cpp ---------------------------------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // This file is a part of EfficiencySanitizer, a family of performance tuners.
 | |
| //
 | |
| // Interception routines for the esan run-time.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "esan.h"
 | |
| #include "esan_shadow.h"
 | |
| #include "interception/interception.h"
 | |
| #include "sanitizer_common/sanitizer_common.h"
 | |
| #include "sanitizer_common/sanitizer_libc.h"
 | |
| #include "sanitizer_common/sanitizer_linux.h"
 | |
| #include "sanitizer_common/sanitizer_stacktrace.h"
 | |
| 
 | |
| using namespace __esan; // NOLINT
 | |
| 
 | |
| #define CUR_PC() (StackTrace::GetCurrentPc())
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Interception via sanitizer common interceptors
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| // Get the per-platform defines for what is possible to intercept
 | |
| #include "sanitizer_common/sanitizer_platform_interceptors.h"
 | |
| 
 | |
| DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
 | |
| 
 | |
| // TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming
 | |
| // that interception is a perf hit: should we do the same?
 | |
| 
 | |
| // We have no need to intercept:
 | |
| #undef SANITIZER_INTERCEPT_TLS_GET_ADDR
 | |
| 
 | |
| // TODO(bruening): the common realpath interceptor assumes malloc is
 | |
| // intercepted!  We should try to parametrize that, though we'll
 | |
| // intercept malloc soon ourselves and can then remove this undef.
 | |
| #undef SANITIZER_INTERCEPT_REALPATH
 | |
| 
 | |
| // We provide our own version:
 | |
| #undef SANITIZER_INTERCEPT_SIGPROCMASK
 | |
| 
 | |
| #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized)
 | |
| 
 | |
| #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
 | |
| #define COMMON_INTERCEPT_FUNCTION_VER(name, ver)                          \
 | |
|   INTERCEPT_FUNCTION_VER(name, ver)
 | |
| 
 | |
| // We must initialize during early interceptors, to support tcmalloc.
 | |
| // This means that for some apps we fully initialize prior to
 | |
| // __esan_init() being called.
 | |
| // We currently do not use ctx.
 | |
| #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...)                               \
 | |
|   do {                                                                         \
 | |
|     if (UNLIKELY(COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)) {                 \
 | |
|       if (!UNLIKELY(EsanDuringInit))                                           \
 | |
|         initializeLibrary(__esan_which_tool);                                  \
 | |
|       return REAL(func)(__VA_ARGS__);                                          \
 | |
|     }                                                                          \
 | |
|     ctx = nullptr;                                                             \
 | |
|     (void)ctx;                                                                 \
 | |
|   } while (false)
 | |
| 
 | |
| #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...)                      \
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, func, __VA_ARGS__)
 | |
| 
 | |
| #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size)                         \
 | |
|   processRangeAccess(CUR_PC(), (uptr)ptr, size, true)
 | |
| 
 | |
| #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size)                          \
 | |
|   processRangeAccess(CUR_PC(), (uptr)ptr, size, false)
 | |
| 
 | |
| // This is only called if the app explicitly calls exit(), not on
 | |
| // a normal exit.
 | |
| #define COMMON_INTERCEPTOR_ON_EXIT(ctx) finalizeLibrary()
 | |
| 
 | |
| #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path)                          \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(file);                                                              \
 | |
|     (void)(path);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file)                               \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(file);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)                    \
 | |
|   do {                                                                         \
 | |
|     (void)(filename);                                                          \
 | |
|     (void)(handle);                                                            \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED()                                  \
 | |
|   do {                                                                         \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u)                                     \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(u);                                                                 \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_RELEASE(ctx, u)                                     \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(u);                                                                 \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path)                              \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(path);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd)                                 \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd)                                 \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd)                                  \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd)                    \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(fd);                                                                \
 | |
|     (void)(newfd);                                                             \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name)                          \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(name);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name)                 \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(thread);                                                            \
 | |
|     (void)(name);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
 | |
| #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m)                                  \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(m);                                                                 \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m)                                \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(m);                                                                 \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m)                                \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(m);                                                                 \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg)                            \
 | |
|   do {                                                                         \
 | |
|     (void)(ctx);                                                               \
 | |
|     (void)(msg);                                                               \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_USER_CALLBACK_START()                               \
 | |
|   do {                                                                         \
 | |
|   } while (false)
 | |
| #define COMMON_INTERCEPTOR_USER_CALLBACK_END()                                 \
 | |
|   do {                                                                         \
 | |
|   } while (false)
 | |
| 
 | |
| #include "sanitizer_common/sanitizer_common_interceptors.inc"
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Syscall interception
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| // We want the caller's PC b/c unlike the other function interceptors these
 | |
| // are separate pre and post functions called around the app's syscall().
 | |
| 
 | |
| #define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size)                               \
 | |
|   processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false)
 | |
| 
 | |
| #define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size)                              \
 | |
|   do {                                                                         \
 | |
|     (void)(ptr);                                                               \
 | |
|     (void)(size);                                                              \
 | |
|   } while (false)
 | |
| 
 | |
| #define COMMON_SYSCALL_POST_READ_RANGE(ptr, size)                              \
 | |
|   do {                                                                         \
 | |
|     (void)(ptr);                                                               \
 | |
|     (void)(size);                                                              \
 | |
|   } while (false)
 | |
| 
 | |
| // The actual amount written is in post, not pre.
 | |
| #define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size)                             \
 | |
|   processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true)
 | |
| 
 | |
| #define COMMON_SYSCALL_ACQUIRE(addr)                                           \
 | |
|   do {                                                                         \
 | |
|     (void)(addr);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_RELEASE(addr)                                           \
 | |
|   do {                                                                         \
 | |
|     (void)(addr);                                                              \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_FD_CLOSE(fd)                                            \
 | |
|   do {                                                                         \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_FD_ACQUIRE(fd)                                          \
 | |
|   do {                                                                         \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_FD_RELEASE(fd)                                          \
 | |
|   do {                                                                         \
 | |
|     (void)(fd);                                                                \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_PRE_FORK()                                              \
 | |
|   do {                                                                         \
 | |
|   } while (false)
 | |
| #define COMMON_SYSCALL_POST_FORK(res)                                          \
 | |
|   do {                                                                         \
 | |
|     (void)(res);                                                               \
 | |
|   } while (false)
 | |
| 
 | |
| #include "sanitizer_common/sanitizer_common_syscalls.inc"
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Custom interceptors
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| // TODO(bruening): move more of these to the common interception pool as they
 | |
| // are shared with tsan and asan.
 | |
| // While our other files match LLVM style, here we match sanitizer style as we
 | |
| // expect to move these to the common pool.
 | |
| 
 | |
| INTERCEPTOR(char *, strcpy, char *dst, const char *src) { // NOLINT
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, strcpy, dst, src);
 | |
|   uptr srclen = internal_strlen(src);
 | |
|   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, srclen + 1);
 | |
|   COMMON_INTERCEPTOR_READ_RANGE(ctx, src, srclen + 1);
 | |
|   return REAL(strcpy)(dst, src); // NOLINT
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(char *, strncpy, char *dst, char *src, uptr n) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, strncpy, dst, src, n);
 | |
|   uptr srclen = internal_strnlen(src, n);
 | |
|   uptr copied_size = srclen + 1 > n ? n : srclen + 1;
 | |
|   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, copied_size);
 | |
|   COMMON_INTERCEPTOR_READ_RANGE(ctx, src, copied_size);
 | |
|   return REAL(strncpy)(dst, src, n);
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(int, open, const char *name, int flags, int mode) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, open, name, flags, mode);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
 | |
|   return REAL(open)(name, flags, mode);
 | |
| }
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, open64, name, flags, mode);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
 | |
|   return REAL(open64)(name, flags, mode);
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
 | |
| #else
 | |
| #define ESAN_MAYBE_INTERCEPT_OPEN64
 | |
| #endif
 | |
| 
 | |
| INTERCEPTOR(int, creat, const char *name, int mode) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, creat, name, mode);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
 | |
|   return REAL(creat)(name, mode);
 | |
| }
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| INTERCEPTOR(int, creat64, const char *name, int mode) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, creat64, name, mode);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
 | |
|   return REAL(creat64)(name, mode);
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64)
 | |
| #else
 | |
| #define ESAN_MAYBE_INTERCEPT_CREAT64
 | |
| #endif
 | |
| 
 | |
| INTERCEPTOR(int, unlink, char *path) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, unlink, path);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
 | |
|   return REAL(unlink)(path);
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(int, puts, const char *s) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
 | |
|   COMMON_INTERCEPTOR_READ_RANGE(ctx, s, internal_strlen(s));
 | |
|   return REAL(puts)(s);
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(int, rmdir, char *path) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path);
 | |
|   COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
 | |
|   return REAL(rmdir)(path);
 | |
| }
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Shadow-related interceptors
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| // These are candidates for sharing with all sanitizers if shadow memory
 | |
| // support is also standardized.
 | |
| 
 | |
| INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
 | |
|                  int fd, OFF_T off) {
 | |
|   if (UNLIKELY(REAL(mmap) == nullptr)) {
 | |
|     // With esan init during interceptor init and a static libc preventing
 | |
|     // our early-calloc from triggering, we can end up here before our
 | |
|     // REAL pointer is set up.
 | |
|     return (void *)internal_mmap(addr, sz, prot, flags, fd, off);
 | |
|   }
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, sz, prot, flags, fd, off);
 | |
|   if (!fixMmapAddr(&addr, sz, flags))
 | |
|     return (void *)-1;
 | |
|   void *result = REAL(mmap)(addr, sz, prot, flags, fd, off);
 | |
|   return (void *)checkMmapResult((uptr)result, sz);
 | |
| }
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
 | |
|                  int fd, OFF64_T off) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, mmap64, addr, sz, prot, flags, fd, off);
 | |
|   if (!fixMmapAddr(&addr, sz, flags))
 | |
|     return (void *)-1;
 | |
|   void *result = REAL(mmap64)(addr, sz, prot, flags, fd, off);
 | |
|   return (void *)checkMmapResult((uptr)result, sz);
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64)
 | |
| #else
 | |
| #define ESAN_MAYBE_INTERCEPT_MMAP64
 | |
| #endif
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Signal-related interceptors
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| typedef void (*signal_handler_t)(int);
 | |
| INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler);
 | |
|   signal_handler_t result;
 | |
|   if (!processSignal(signum, handler, &result))
 | |
|     return result;
 | |
|   else
 | |
|     return REAL(signal)(signum, handler);
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal)
 | |
| #else
 | |
| #error Platform not supported
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGNAL
 | |
| #endif
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
 | |
|              struct sigaction *oldact)
 | |
| INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
 | |
|             struct sigaction *oldact) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact);
 | |
|   if (!processSigaction(signum, act, oldact))
 | |
|     return 0;
 | |
|   else
 | |
|     return REAL(sigaction)(signum, act, oldact);
 | |
| }
 | |
| 
 | |
| // This is required to properly use internal_sigaction.
 | |
| namespace __sanitizer {
 | |
| int real_sigaction(int signum, const void *act, void *oldact) {
 | |
|   if (REAL(sigaction) == nullptr) {
 | |
|     // With an instrumented allocator, this is called during interceptor init
 | |
|     // and we need a raw syscall solution.
 | |
|     return internal_sigaction_syscall(signum, act, oldact);
 | |
|   }
 | |
|   return REAL(sigaction)(signum, (const struct sigaction *)act,
 | |
|                          (struct sigaction *)oldact);
 | |
| }
 | |
| } // namespace __sanitizer
 | |
| 
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction)
 | |
| #else
 | |
| #error Platform not supported
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGACTION
 | |
| #endif
 | |
| 
 | |
| #if SANITIZER_LINUX
 | |
| INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
 | |
|             __sanitizer_sigset_t *oldset) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
 | |
|   int res = 0;
 | |
|   if (processSigprocmask(how, set, oldset))
 | |
|     res = REAL(sigprocmask)(how, set, oldset);
 | |
|   if (!res && oldset)
 | |
|     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
 | |
|   return res;
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
 | |
| #else
 | |
| #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
 | |
| #endif
 | |
| 
 | |
| #if !SANITIZER_WINDOWS
 | |
| INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
 | |
|             __sanitizer_sigset_t *oldset) {
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
 | |
|   int res = 0;
 | |
|   if (processSigprocmask(how, set, oldset))
 | |
|     res = REAL(sigprocmask)(how, set, oldset);
 | |
|   if (!res && oldset)
 | |
|     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
 | |
|   return res;
 | |
| }
 | |
| #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
 | |
| #else
 | |
| #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
 | |
| #endif
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Malloc interceptors
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| static const uptr early_alloc_buf_size = 4096;
 | |
| static uptr allocated_bytes;
 | |
| static char early_alloc_buf[early_alloc_buf_size];
 | |
| 
 | |
| static bool isInEarlyAllocBuf(const void *ptr) {
 | |
|   return ((uptr)ptr >= (uptr)early_alloc_buf &&
 | |
|           ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
 | |
| }
 | |
| 
 | |
| static void *handleEarlyAlloc(uptr size) {
 | |
|   // If esan is initialized during an interceptor (which happens with some
 | |
|   // tcmalloc implementations that call pthread_mutex_lock), the call from
 | |
|   // dlsym to calloc will deadlock.
 | |
|   // dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
 | |
|   // We work around it by using a static buffer for the early malloc/calloc
 | |
|   // requests.
 | |
|   // This solution will also allow us to deliberately intercept malloc & family
 | |
|   // in the future (to perform tool actions on each allocation, without
 | |
|   // replacing the allocator), as it also solves the problem of intercepting
 | |
|   // calloc when it will itself be called before its REAL pointer is
 | |
|   // initialized.
 | |
|   // We do not handle multiple threads here.  This only happens at process init
 | |
|   // time, and while it's possible for a shared library to create early threads
 | |
|   // that race here, we consider that to be a corner case extreme enough that
 | |
|   // it's not worth the effort to handle.
 | |
|   void *mem = (void *)&early_alloc_buf[allocated_bytes];
 | |
|   allocated_bytes += size;
 | |
|   CHECK_LT(allocated_bytes, early_alloc_buf_size);
 | |
|   return mem;
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(void*, calloc, uptr size, uptr n) {
 | |
|   if (EsanDuringInit && REAL(calloc) == nullptr)
 | |
|     return handleEarlyAlloc(size * n);
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n);
 | |
|   void *res = REAL(calloc)(size, n);
 | |
|   // The memory is zeroed and thus is all written.
 | |
|   COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(void*, malloc, uptr size) {
 | |
|   if (EsanDuringInit && REAL(malloc) == nullptr)
 | |
|     return handleEarlyAlloc(size);
 | |
|   void *ctx;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
 | |
|   return REAL(malloc)(size);
 | |
| }
 | |
| 
 | |
| INTERCEPTOR(void, free, void *p) {
 | |
|   void *ctx;
 | |
|   // There are only a few early allocation requests, so we simply skip the free.
 | |
|   if (isInEarlyAllocBuf(p))
 | |
|     return;
 | |
|   COMMON_INTERCEPTOR_ENTER(ctx, free, p);
 | |
|   REAL(free)(p);
 | |
| }
 | |
| 
 | |
| namespace __esan {
 | |
| 
 | |
| void initializeInterceptors() {
 | |
|   InitializeCommonInterceptors();
 | |
| 
 | |
|   INTERCEPT_FUNCTION(strcpy); // NOLINT
 | |
|   INTERCEPT_FUNCTION(strncpy);
 | |
| 
 | |
|   INTERCEPT_FUNCTION(open);
 | |
|   ESAN_MAYBE_INTERCEPT_OPEN64;
 | |
|   INTERCEPT_FUNCTION(creat);
 | |
|   ESAN_MAYBE_INTERCEPT_CREAT64;
 | |
|   INTERCEPT_FUNCTION(unlink);
 | |
|   INTERCEPT_FUNCTION(fread);
 | |
|   INTERCEPT_FUNCTION(fwrite);
 | |
|   INTERCEPT_FUNCTION(puts);
 | |
|   INTERCEPT_FUNCTION(rmdir);
 | |
| 
 | |
|   INTERCEPT_FUNCTION(mmap);
 | |
|   ESAN_MAYBE_INTERCEPT_MMAP64;
 | |
| 
 | |
|   ESAN_MAYBE_INTERCEPT_SIGNAL;
 | |
|   ESAN_MAYBE_INTERCEPT_SIGACTION;
 | |
|   ESAN_MAYBE_INTERCEPT_SIGPROCMASK;
 | |
|   ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
 | |
| 
 | |
|   INTERCEPT_FUNCTION(calloc);
 | |
|   INTERCEPT_FUNCTION(malloc);
 | |
|   INTERCEPT_FUNCTION(free);
 | |
| 
 | |
|   // TODO(bruening): intercept routines that other sanitizers intercept that
 | |
|   // are not in the common pool or here yet, ideally by adding to the common
 | |
|   // pool.  Examples include wcslen and bcopy.
 | |
| 
 | |
|   // TODO(bruening): there are many more libc routines that read or write data
 | |
|   // structures that no sanitizer is intercepting: sigaction, strtol, etc.
 | |
| }
 | |
| 
 | |
| } // namespace __esan
 |