1 //===-- esan_interceptors.cpp ---------------------------------------------===//
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 EfficiencySanitizer, a family of performance tuners.
12 // Interception routines for the esan run-time.
13 //===----------------------------------------------------------------------===//
16 #include "esan_shadow.h"
17 #include "interception/interception.h"
18 #include "sanitizer_common/sanitizer_common.h"
19 #include "sanitizer_common/sanitizer_libc.h"
20 #include "sanitizer_common/sanitizer_linux.h"
21 #include "sanitizer_common/sanitizer_stacktrace.h"
23 using namespace __esan
; // NOLINT
25 #define CUR_PC() (StackTrace::GetCurrentPc())
27 //===----------------------------------------------------------------------===//
28 // Interception via sanitizer common interceptors
29 //===----------------------------------------------------------------------===//
31 // Get the per-platform defines for what is possible to intercept
32 #include "sanitizer_common/sanitizer_platform_interceptors.h"
34 // TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming
35 // that interception is a perf hit: should we do the same?
37 // We have no need to intercept:
38 #undef SANITIZER_INTERCEPT_TLS_GET_ADDR
40 // TODO(bruening): the common realpath interceptor assumes malloc is
41 // intercepted! We should try to parametrize that, though we'll
42 // intercept malloc soon ourselves and can then remove this undef.
43 #undef SANITIZER_INTERCEPT_REALPATH
45 // We provide our own version:
46 #undef SANITIZER_INTERCEPT_SIGPROCMASK
48 #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized)
50 #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
51 #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
52 INTERCEPT_FUNCTION_VER(name, ver)
54 // We must initialize during early interceptors, to support tcmalloc.
55 // This means that for some apps we fully initialize prior to
56 // __esan_init() being called.
57 // We currently do not use ctx.
58 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
60 if (UNLIKELY(COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)) { \
61 if (!UNLIKELY(EsanDuringInit)) \
62 initializeLibrary(__esan_which_tool); \
63 return REAL(func)(__VA_ARGS__); \
69 #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \
70 COMMON_INTERCEPTOR_ENTER(ctx, func, __VA_ARGS__)
72 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
73 processRangeAccess(CUR_PC(), (uptr)ptr, size, true)
75 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
76 processRangeAccess(CUR_PC(), (uptr)ptr, size, false)
78 // This is only called if the app explicitly calls exit(), not on
80 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) finalizeLibrary()
82 #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
88 #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \
93 #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
98 #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \
101 #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \
106 #define COMMON_INTERCEPTOR_RELEASE(ctx, u) \
111 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
116 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
121 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
126 #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \
131 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
137 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
142 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
148 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
149 #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \
154 #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \
159 #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
164 #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
169 #define COMMON_INTERCEPTOR_USER_CALLBACK_START() \
172 #define COMMON_INTERCEPTOR_USER_CALLBACK_END() \
176 #include "sanitizer_common/sanitizer_common_interceptors.inc"
178 //===----------------------------------------------------------------------===//
179 // Syscall interception
180 //===----------------------------------------------------------------------===//
182 // We want the caller's PC b/c unlike the other function interceptors these
183 // are separate pre and post functions called around the app's syscall().
185 #define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size) \
186 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false)
188 #define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size) \
194 #define COMMON_SYSCALL_POST_READ_RANGE(ptr, size) \
200 // The actual amount written is in post, not pre.
201 #define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size) \
202 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true)
204 #define COMMON_SYSCALL_ACQUIRE(addr) \
208 #define COMMON_SYSCALL_RELEASE(addr) \
212 #define COMMON_SYSCALL_FD_CLOSE(fd) \
216 #define COMMON_SYSCALL_FD_ACQUIRE(fd) \
220 #define COMMON_SYSCALL_FD_RELEASE(fd) \
224 #define COMMON_SYSCALL_PRE_FORK() \
227 #define COMMON_SYSCALL_POST_FORK(res) \
232 #include "sanitizer_common/sanitizer_common_syscalls.inc"
234 //===----------------------------------------------------------------------===//
235 // Custom interceptors
236 //===----------------------------------------------------------------------===//
238 // TODO(bruening): move more of these to the common interception pool as they
239 // are shared with tsan and asan.
240 // While our other files match LLVM style, here we match sanitizer style as we
241 // expect to move these to the common pool.
243 INTERCEPTOR(char *, strcpy
, char *dst
, const char *src
) { // NOLINT
245 COMMON_INTERCEPTOR_ENTER(ctx
, strcpy
, dst
, src
);
246 uptr srclen
= internal_strlen(src
);
247 COMMON_INTERCEPTOR_WRITE_RANGE(ctx
, dst
, srclen
+ 1);
248 COMMON_INTERCEPTOR_READ_RANGE(ctx
, src
, srclen
+ 1);
249 return REAL(strcpy
)(dst
, src
); // NOLINT
252 INTERCEPTOR(char *, strncpy
, char *dst
, char *src
, uptr n
) {
254 COMMON_INTERCEPTOR_ENTER(ctx
, strncpy
, dst
, src
, n
);
255 uptr srclen
= internal_strnlen(src
, n
);
256 uptr copied_size
= srclen
+ 1 > n
? n
: srclen
+ 1;
257 COMMON_INTERCEPTOR_WRITE_RANGE(ctx
, dst
, copied_size
);
258 COMMON_INTERCEPTOR_READ_RANGE(ctx
, src
, copied_size
);
259 return REAL(strncpy
)(dst
, src
, n
);
262 INTERCEPTOR(int, open
, const char *name
, int flags
, int mode
) {
264 COMMON_INTERCEPTOR_ENTER(ctx
, open
, name
, flags
, mode
);
265 COMMON_INTERCEPTOR_READ_STRING(ctx
, name
, 0);
266 return REAL(open
)(name
, flags
, mode
);
270 INTERCEPTOR(int, open64
, const char *name
, int flags
, int mode
) {
272 COMMON_INTERCEPTOR_ENTER(ctx
, open64
, name
, flags
, mode
);
273 COMMON_INTERCEPTOR_READ_STRING(ctx
, name
, 0);
274 return REAL(open64
)(name
, flags
, mode
);
276 #define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
278 #define ESAN_MAYBE_INTERCEPT_OPEN64
281 INTERCEPTOR(int, creat
, const char *name
, int mode
) {
283 COMMON_INTERCEPTOR_ENTER(ctx
, creat
, name
, mode
);
284 COMMON_INTERCEPTOR_READ_STRING(ctx
, name
, 0);
285 return REAL(creat
)(name
, mode
);
289 INTERCEPTOR(int, creat64
, const char *name
, int mode
) {
291 COMMON_INTERCEPTOR_ENTER(ctx
, creat64
, name
, mode
);
292 COMMON_INTERCEPTOR_READ_STRING(ctx
, name
, 0);
293 return REAL(creat64
)(name
, mode
);
295 #define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64)
297 #define ESAN_MAYBE_INTERCEPT_CREAT64
300 INTERCEPTOR(int, unlink
, char *path
) {
302 COMMON_INTERCEPTOR_ENTER(ctx
, unlink
, path
);
303 COMMON_INTERCEPTOR_READ_STRING(ctx
, path
, 0);
304 return REAL(unlink
)(path
);
307 INTERCEPTOR(uptr
, fread
, void *ptr
, uptr size
, uptr nmemb
, void *f
) {
309 COMMON_INTERCEPTOR_ENTER(ctx
, fread
, ptr
, size
, nmemb
, f
);
310 COMMON_INTERCEPTOR_WRITE_RANGE(ctx
, ptr
, size
* nmemb
);
311 return REAL(fread
)(ptr
, size
, nmemb
, f
);
314 INTERCEPTOR(uptr
, fwrite
, const void *p
, uptr size
, uptr nmemb
, void *f
) {
316 COMMON_INTERCEPTOR_ENTER(ctx
, fwrite
, p
, size
, nmemb
, f
);
317 COMMON_INTERCEPTOR_READ_RANGE(ctx
, p
, size
* nmemb
);
318 return REAL(fwrite
)(p
, size
, nmemb
, f
);
321 INTERCEPTOR(int, puts
, const char *s
) {
323 COMMON_INTERCEPTOR_ENTER(ctx
, puts
, s
);
324 COMMON_INTERCEPTOR_READ_RANGE(ctx
, s
, internal_strlen(s
));
325 return REAL(puts
)(s
);
328 INTERCEPTOR(int, rmdir
, char *path
) {
330 COMMON_INTERCEPTOR_ENTER(ctx
, rmdir
, path
);
331 COMMON_INTERCEPTOR_READ_STRING(ctx
, path
, 0);
332 return REAL(rmdir
)(path
);
335 //===----------------------------------------------------------------------===//
336 // Shadow-related interceptors
337 //===----------------------------------------------------------------------===//
339 // These are candidates for sharing with all sanitizers if shadow memory
340 // support is also standardized.
342 INTERCEPTOR(void *, mmap
, void *addr
, SIZE_T sz
, int prot
, int flags
,
344 if (UNLIKELY(REAL(mmap
) == nullptr)) {
345 // With esan init during interceptor init and a static libc preventing
346 // our early-calloc from triggering, we can end up here before our
347 // REAL pointer is set up.
348 return (void *)internal_mmap(addr
, sz
, prot
, flags
, fd
, off
);
351 COMMON_INTERCEPTOR_ENTER(ctx
, mmap
, addr
, sz
, prot
, flags
, fd
, off
);
352 if (!fixMmapAddr(&addr
, sz
, flags
))
354 void *result
= REAL(mmap
)(addr
, sz
, prot
, flags
, fd
, off
);
355 return (void *)checkMmapResult((uptr
)result
, sz
);
359 INTERCEPTOR(void *, mmap64
, void *addr
, SIZE_T sz
, int prot
, int flags
,
360 int fd
, OFF64_T off
) {
362 COMMON_INTERCEPTOR_ENTER(ctx
, mmap64
, addr
, sz
, prot
, flags
, fd
, off
);
363 if (!fixMmapAddr(&addr
, sz
, flags
))
365 void *result
= REAL(mmap64
)(addr
, sz
, prot
, flags
, fd
, off
);
366 return (void *)checkMmapResult((uptr
)result
, sz
);
368 #define ESAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64)
370 #define ESAN_MAYBE_INTERCEPT_MMAP64
373 //===----------------------------------------------------------------------===//
374 // Signal-related interceptors
375 //===----------------------------------------------------------------------===//
378 typedef void (*signal_handler_t
)(int);
379 INTERCEPTOR(signal_handler_t
, signal
, int signum
, signal_handler_t handler
) {
381 COMMON_INTERCEPTOR_ENTER(ctx
, signal
, signum
, handler
);
382 signal_handler_t result
;
383 if (!processSignal(signum
, handler
, &result
))
386 return REAL(signal
)(signum
, handler
);
388 #define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal)
390 #error Platform not supported
391 #define ESAN_MAYBE_INTERCEPT_SIGNAL
395 DECLARE_REAL(int, sigaction
, int signum
, const struct sigaction
*act
,
396 struct sigaction
*oldact
)
397 INTERCEPTOR(int, sigaction
, int signum
, const struct sigaction
*act
,
398 struct sigaction
*oldact
) {
400 COMMON_INTERCEPTOR_ENTER(ctx
, sigaction
, signum
, act
, oldact
);
401 if (!processSigaction(signum
, act
, oldact
))
404 return REAL(sigaction
)(signum
, act
, oldact
);
407 // This is required to properly use internal_sigaction.
408 namespace __sanitizer
{
409 int real_sigaction(int signum
, const void *act
, void *oldact
) {
410 if (REAL(sigaction
) == nullptr) {
411 // With an instrumented allocator, this is called during interceptor init
412 // and we need a raw syscall solution.
413 return internal_sigaction_syscall(signum
, act
, oldact
);
415 return REAL(sigaction
)(signum
, (const struct sigaction
*)act
,
416 (struct sigaction
*)oldact
);
418 } // namespace __sanitizer
420 #define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction)
422 #error Platform not supported
423 #define ESAN_MAYBE_INTERCEPT_SIGACTION
427 INTERCEPTOR(int, sigprocmask
, int how
, __sanitizer_sigset_t
*set
,
428 __sanitizer_sigset_t
*oldset
) {
430 COMMON_INTERCEPTOR_ENTER(ctx
, sigprocmask
, how
, set
, oldset
);
432 if (processSigprocmask(how
, set
, oldset
))
433 res
= REAL(sigprocmask
)(how
, set
, oldset
);
435 COMMON_INTERCEPTOR_WRITE_RANGE(ctx
, oldset
, sizeof(*oldset
));
438 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
440 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
443 #if !SANITIZER_WINDOWS
444 INTERCEPTOR(int, pthread_sigmask
, int how
, __sanitizer_sigset_t
*set
,
445 __sanitizer_sigset_t
*oldset
) {
447 COMMON_INTERCEPTOR_ENTER(ctx
, pthread_sigmask
, how
, set
, oldset
);
449 if (processSigprocmask(how
, set
, oldset
))
450 res
= REAL(sigprocmask
)(how
, set
, oldset
);
452 COMMON_INTERCEPTOR_WRITE_RANGE(ctx
, oldset
, sizeof(*oldset
));
455 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
457 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
460 //===----------------------------------------------------------------------===//
461 // Malloc interceptors
462 //===----------------------------------------------------------------------===//
464 static char early_alloc_buf
[128];
465 static bool used_early_alloc_buf
;
467 static void *handleEarlyAlloc(uptr size
) {
468 // If esan is initialized during an interceptor (which happens with some
469 // tcmalloc implementations that call pthread_mutex_lock), the call from
470 // dlsym to calloc will deadlock. There is only one such calloc (dlsym
471 // allocates a single pthread key), so we work around it by using a
472 // static buffer for the calloc request. The loader currently needs
473 // 32 bytes but we size at 128 to allow for future changes.
474 // This solution will also allow us to deliberately intercept malloc & family
475 // in the future (to perform tool actions on each allocation, without
476 // replacing the allocator), as it also solves the problem of intercepting
477 // calloc when it will itself be called before its REAL pointer is
479 CHECK(!used_early_alloc_buf
&& size
< sizeof(early_alloc_buf
));
480 // We do not handle multiple threads here. This only happens at process init
481 // time, and while it's possible for a shared library to create early threads
482 // that race here, we consider that to be a corner case extreme enough that
483 // it's not worth the effort to handle.
484 used_early_alloc_buf
= true;
485 return (void *)early_alloc_buf
;
488 INTERCEPTOR(void*, calloc
, uptr size
, uptr n
) {
489 if (EsanDuringInit
&& REAL(calloc
) == nullptr)
490 return handleEarlyAlloc(size
* n
);
492 COMMON_INTERCEPTOR_ENTER(ctx
, calloc
, size
, n
);
493 void *res
= REAL(calloc
)(size
, n
);
494 // The memory is zeroed and thus is all written.
495 COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr
)res
, size
* n
);
499 INTERCEPTOR(void, free
, void *p
) {
501 COMMON_INTERCEPTOR_ENTER(ctx
, free
, p
);
502 if (p
== (void *)early_alloc_buf
) {
503 // We expect just a singleton use but we clear this for cleanliness.
504 used_early_alloc_buf
= false;
512 void initializeInterceptors() {
513 InitializeCommonInterceptors();
515 INTERCEPT_FUNCTION(strcpy
); // NOLINT
516 INTERCEPT_FUNCTION(strncpy
);
518 INTERCEPT_FUNCTION(open
);
519 ESAN_MAYBE_INTERCEPT_OPEN64
;
520 INTERCEPT_FUNCTION(creat
);
521 ESAN_MAYBE_INTERCEPT_CREAT64
;
522 INTERCEPT_FUNCTION(unlink
);
523 INTERCEPT_FUNCTION(fread
);
524 INTERCEPT_FUNCTION(fwrite
);
525 INTERCEPT_FUNCTION(puts
);
526 INTERCEPT_FUNCTION(rmdir
);
528 INTERCEPT_FUNCTION(mmap
);
529 ESAN_MAYBE_INTERCEPT_MMAP64
;
531 ESAN_MAYBE_INTERCEPT_SIGNAL
;
532 ESAN_MAYBE_INTERCEPT_SIGACTION
;
533 ESAN_MAYBE_INTERCEPT_SIGPROCMASK
;
534 ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
;
536 INTERCEPT_FUNCTION(calloc
);
537 INTERCEPT_FUNCTION(free
);
539 // TODO(bruening): intercept routines that other sanitizers intercept that
540 // are not in the common pool or here yet, ideally by adding to the common
541 // pool. Examples include wcslen and bcopy.
543 // TODO(bruening): there are many more libc routines that read or write data
544 // structures that no sanitizer is intercepting: sigaction, strtol, etc.
547 } // namespace __esan