1 //===-- esan_interceptors.cpp ---------------------------------------------===//
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 EfficiencySanitizer, a family of performance tuners.
11 //
12 // Interception routines for the esan run-time.
13 //===----------------------------------------------------------------------===//
14
15 #include "esan.h"
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"
22
23 using namespace __esan; // NOLINT
24
25 #define CUR_PC() (StackTrace::GetCurrentPc())
26
27 //===----------------------------------------------------------------------===//
28 // Interception via sanitizer common interceptors
29 //===----------------------------------------------------------------------===//
30
31 // Get the per-platform defines for what is possible to intercept
32 #include "sanitizer_common/sanitizer_platform_interceptors.h"
33
DECLARE_REAL_AND_INTERCEPTOR(void *,malloc,uptr)34 DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
35
36 // TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming
37 // that interception is a perf hit: should we do the same?
38
39 // We have no need to intercept:
40 #undef SANITIZER_INTERCEPT_TLS_GET_ADDR
41
42 // TODO(bruening): the common realpath interceptor assumes malloc is
43 // intercepted! We should try to parametrize that, though we'll
44 // intercept malloc soon ourselves and can then remove this undef.
45 #undef SANITIZER_INTERCEPT_REALPATH
46
47 // We provide our own version:
48 #undef SANITIZER_INTERCEPT_SIGPROCMASK
49
50 #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized)
51
52 #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
53 #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
54 INTERCEPT_FUNCTION_VER(name, ver)
55
56 // We must initialize during early interceptors, to support tcmalloc.
57 // This means that for some apps we fully initialize prior to
58 // __esan_init() being called.
59 // We currently do not use ctx.
60 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
61 do { \
62 if (UNLIKELY(COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)) { \
63 if (!UNLIKELY(EsanDuringInit)) \
64 initializeLibrary(__esan_which_tool); \
65 return REAL(func)(__VA_ARGS__); \
66 } \
67 ctx = nullptr; \
68 (void)ctx; \
69 } while (false)
70
71 #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \
72 COMMON_INTERCEPTOR_ENTER(ctx, func, __VA_ARGS__)
73
74 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
75 processRangeAccess(CUR_PC(), (uptr)ptr, size, true)
76
77 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
78 processRangeAccess(CUR_PC(), (uptr)ptr, size, false)
79
80 // This is only called if the app explicitly calls exit(), not on
81 // a normal exit.
82 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) finalizeLibrary()
83
84 #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
85 do { \
86 (void)(ctx); \
87 (void)(file); \
88 (void)(path); \
89 } while (false)
90 #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \
91 do { \
92 (void)(ctx); \
93 (void)(file); \
94 } while (false)
95 #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
96 do { \
97 (void)(filename); \
98 (void)(handle); \
99 } while (false)
100 #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \
101 do { \
102 } while (false)
103 #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \
104 do { \
105 (void)(ctx); \
106 (void)(u); \
107 } while (false)
108 #define COMMON_INTERCEPTOR_RELEASE(ctx, u) \
109 do { \
110 (void)(ctx); \
111 (void)(u); \
112 } while (false)
113 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
114 do { \
115 (void)(ctx); \
116 (void)(path); \
117 } while (false)
118 #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
119 do { \
120 (void)(ctx); \
121 (void)(fd); \
122 } while (false)
123 #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
124 do { \
125 (void)(ctx); \
126 (void)(fd); \
127 } while (false)
128 #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \
129 do { \
130 (void)(ctx); \
131 (void)(fd); \
132 } while (false)
133 #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
134 do { \
135 (void)(ctx); \
136 (void)(fd); \
137 (void)(newfd); \
138 } while (false)
139 #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
140 do { \
141 (void)(ctx); \
142 (void)(name); \
143 } while (false)
144 #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
145 do { \
146 (void)(ctx); \
147 (void)(thread); \
148 (void)(name); \
149 } while (false)
150 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
151 #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \
152 do { \
153 (void)(ctx); \
154 (void)(m); \
155 } while (false)
156 #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \
157 do { \
158 (void)(ctx); \
159 (void)(m); \
160 } while (false)
161 #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
162 do { \
163 (void)(ctx); \
164 (void)(m); \
165 } while (false)
166 #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
167 do { \
168 (void)(ctx); \
169 (void)(msg); \
170 } while (false)
171 #define COMMON_INTERCEPTOR_USER_CALLBACK_START() \
172 do { \
173 } while (false)
174 #define COMMON_INTERCEPTOR_USER_CALLBACK_END() \
175 do { \
176 } while (false)
177
178 #define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, sz, prot, flags, fd, \
179 off) \
180 do { \
181 if (!fixMmapAddr(&addr, sz, flags)) \
182 return (void *)-1; \
183 void *result = REAL(mmap)(addr, sz, prot, flags, fd, off); \
184 return (void *)checkMmapResult((uptr)result, sz); \
185 } while (false)
186
187 #include "sanitizer_common/sanitizer_common_interceptors.inc"
188
189 //===----------------------------------------------------------------------===//
190 // Syscall interception
191 //===----------------------------------------------------------------------===//
192
193 // We want the caller's PC b/c unlike the other function interceptors these
194 // are separate pre and post functions called around the app's syscall().
195
196 #define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size) \
197 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false)
198
199 #define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size) \
200 do { \
201 (void)(ptr); \
202 (void)(size); \
203 } while (false)
204
205 #define COMMON_SYSCALL_POST_READ_RANGE(ptr, size) \
206 do { \
207 (void)(ptr); \
208 (void)(size); \
209 } while (false)
210
211 // The actual amount written is in post, not pre.
212 #define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size) \
213 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true)
214
215 #define COMMON_SYSCALL_ACQUIRE(addr) \
216 do { \
217 (void)(addr); \
218 } while (false)
219 #define COMMON_SYSCALL_RELEASE(addr) \
220 do { \
221 (void)(addr); \
222 } while (false)
223 #define COMMON_SYSCALL_FD_CLOSE(fd) \
224 do { \
225 (void)(fd); \
226 } while (false)
227 #define COMMON_SYSCALL_FD_ACQUIRE(fd) \
228 do { \
229 (void)(fd); \
230 } while (false)
231 #define COMMON_SYSCALL_FD_RELEASE(fd) \
232 do { \
233 (void)(fd); \
234 } while (false)
235 #define COMMON_SYSCALL_PRE_FORK() \
236 do { \
237 } while (false)
238 #define COMMON_SYSCALL_POST_FORK(res) \
239 do { \
240 (void)(res); \
241 } while (false)
242
243 #include "sanitizer_common/sanitizer_common_syscalls.inc"
244 #include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
245
246 //===----------------------------------------------------------------------===//
247 // Custom interceptors
248 //===----------------------------------------------------------------------===//
249
250 // TODO(bruening): move more of these to the common interception pool as they
251 // are shared with tsan and asan.
252 // While our other files match LLVM style, here we match sanitizer style as we
253 // expect to move these to the common pool.
254
255 INTERCEPTOR(char *, strcpy, char *dst, const char *src) { // NOLINT
256 void *ctx;
257 COMMON_INTERCEPTOR_ENTER(ctx, strcpy, dst, src);
258 uptr srclen = internal_strlen(src);
259 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, srclen + 1);
260 COMMON_INTERCEPTOR_READ_RANGE(ctx, src, srclen + 1);
261 return REAL(strcpy)(dst, src); // NOLINT
262 }
263
INTERCEPTOR(char *,strncpy,char * dst,char * src,uptr n)264 INTERCEPTOR(char *, strncpy, char *dst, char *src, uptr n) {
265 void *ctx;
266 COMMON_INTERCEPTOR_ENTER(ctx, strncpy, dst, src, n);
267 uptr srclen = internal_strnlen(src, n);
268 uptr copied_size = srclen + 1 > n ? n : srclen + 1;
269 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, copied_size);
270 COMMON_INTERCEPTOR_READ_RANGE(ctx, src, copied_size);
271 return REAL(strncpy)(dst, src, n);
272 }
273
INTERCEPTOR(int,open,const char * name,int flags,int mode)274 INTERCEPTOR(int, open, const char *name, int flags, int mode) {
275 void *ctx;
276 COMMON_INTERCEPTOR_ENTER(ctx, open, name, flags, mode);
277 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
278 return REAL(open)(name, flags, mode);
279 }
280
281 #if SANITIZER_LINUX
INTERCEPTOR(int,open64,const char * name,int flags,int mode)282 INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
283 void *ctx;
284 COMMON_INTERCEPTOR_ENTER(ctx, open64, name, flags, mode);
285 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
286 return REAL(open64)(name, flags, mode);
287 }
288 #define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
289 #else
290 #define ESAN_MAYBE_INTERCEPT_OPEN64
291 #endif
292
INTERCEPTOR(int,creat,const char * name,int mode)293 INTERCEPTOR(int, creat, const char *name, int mode) {
294 void *ctx;
295 COMMON_INTERCEPTOR_ENTER(ctx, creat, name, mode);
296 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
297 return REAL(creat)(name, mode);
298 }
299
300 #if SANITIZER_LINUX
INTERCEPTOR(int,creat64,const char * name,int mode)301 INTERCEPTOR(int, creat64, const char *name, int mode) {
302 void *ctx;
303 COMMON_INTERCEPTOR_ENTER(ctx, creat64, name, mode);
304 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
305 return REAL(creat64)(name, mode);
306 }
307 #define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64)
308 #else
309 #define ESAN_MAYBE_INTERCEPT_CREAT64
310 #endif
311
INTERCEPTOR(int,unlink,char * path)312 INTERCEPTOR(int, unlink, char *path) {
313 void *ctx;
314 COMMON_INTERCEPTOR_ENTER(ctx, unlink, path);
315 COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
316 return REAL(unlink)(path);
317 }
318
INTERCEPTOR(int,rmdir,char * path)319 INTERCEPTOR(int, rmdir, char *path) {
320 void *ctx;
321 COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path);
322 COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
323 return REAL(rmdir)(path);
324 }
325
326 //===----------------------------------------------------------------------===//
327 // Signal-related interceptors
328 //===----------------------------------------------------------------------===//
329
330 #if SANITIZER_LINUX || SANITIZER_FREEBSD
331 typedef void (*signal_handler_t)(int);
INTERCEPTOR(signal_handler_t,signal,int signum,signal_handler_t handler)332 INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
333 void *ctx;
334 COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler);
335 signal_handler_t result;
336 if (!processSignal(signum, handler, &result))
337 return result;
338 else
339 return REAL(signal)(signum, handler);
340 }
341 #define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal)
342 #else
343 #error Platform not supported
344 #define ESAN_MAYBE_INTERCEPT_SIGNAL
345 #endif
346
347 #if SANITIZER_LINUX || SANITIZER_FREEBSD
DECLARE_REAL(int,sigaction,int signum,const struct sigaction * act,struct sigaction * oldact)348 DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
349 struct sigaction *oldact)
350 INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
351 struct sigaction *oldact) {
352 void *ctx;
353 COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact);
354 if (!processSigaction(signum, act, oldact))
355 return 0;
356 else
357 return REAL(sigaction)(signum, act, oldact);
358 }
359
360 // This is required to properly use internal_sigaction.
361 namespace __sanitizer {
real_sigaction(int signum,const void * act,void * oldact)362 int real_sigaction(int signum, const void *act, void *oldact) {
363 if (REAL(sigaction) == nullptr) {
364 // With an instrumented allocator, this is called during interceptor init
365 // and we need a raw syscall solution.
366 #if SANITIZER_LINUX
367 return internal_sigaction_syscall(signum, act, oldact);
368 #else
369 return internal_sigaction(signum, act, oldact);
370 #endif
371 }
372 return REAL(sigaction)(signum, (const struct sigaction *)act,
373 (struct sigaction *)oldact);
374 }
375 } // namespace __sanitizer
376
377 #define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction)
378 #else
379 #error Platform not supported
380 #define ESAN_MAYBE_INTERCEPT_SIGACTION
381 #endif
382
383 #if SANITIZER_LINUX || SANITIZER_FREEBSD
INTERCEPTOR(int,sigprocmask,int how,__sanitizer_sigset_t * set,__sanitizer_sigset_t * oldset)384 INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
385 __sanitizer_sigset_t *oldset) {
386 void *ctx;
387 COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
388 int res = 0;
389 if (processSigprocmask(how, set, oldset))
390 res = REAL(sigprocmask)(how, set, oldset);
391 if (!res && oldset)
392 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
393 return res;
394 }
395 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
396 #else
397 #define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
398 #endif
399
400 #if !SANITIZER_WINDOWS
INTERCEPTOR(int,pthread_sigmask,int how,__sanitizer_sigset_t * set,__sanitizer_sigset_t * oldset)401 INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
402 __sanitizer_sigset_t *oldset) {
403 void *ctx;
404 COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
405 int res = 0;
406 if (processSigprocmask(how, set, oldset))
407 res = REAL(sigprocmask)(how, set, oldset);
408 if (!res && oldset)
409 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
410 return res;
411 }
412 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
413 #else
414 #define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
415 #endif
416
417 //===----------------------------------------------------------------------===//
418 // Malloc interceptors
419 //===----------------------------------------------------------------------===//
420
421 static const uptr early_alloc_buf_size = 4096;
422 static uptr allocated_bytes;
423 static char early_alloc_buf[early_alloc_buf_size];
424
isInEarlyAllocBuf(const void * ptr)425 static bool isInEarlyAllocBuf(const void *ptr) {
426 return ((uptr)ptr >= (uptr)early_alloc_buf &&
427 ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
428 }
429
handleEarlyAlloc(uptr size)430 static void *handleEarlyAlloc(uptr size) {
431 // If esan is initialized during an interceptor (which happens with some
432 // tcmalloc implementations that call pthread_mutex_lock), the call from
433 // dlsym to calloc will deadlock.
434 // dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
435 // We work around it by using a static buffer for the early malloc/calloc
436 // requests.
437 // This solution will also allow us to deliberately intercept malloc & family
438 // in the future (to perform tool actions on each allocation, without
439 // replacing the allocator), as it also solves the problem of intercepting
440 // calloc when it will itself be called before its REAL pointer is
441 // initialized.
442 // We do not handle multiple threads here. This only happens at process init
443 // time, and while it's possible for a shared library to create early threads
444 // that race here, we consider that to be a corner case extreme enough that
445 // it's not worth the effort to handle.
446 void *mem = (void *)&early_alloc_buf[allocated_bytes];
447 allocated_bytes += size;
448 CHECK_LT(allocated_bytes, early_alloc_buf_size);
449 return mem;
450 }
451
INTERCEPTOR(void *,calloc,uptr size,uptr n)452 INTERCEPTOR(void*, calloc, uptr size, uptr n) {
453 if (EsanDuringInit && REAL(calloc) == nullptr)
454 return handleEarlyAlloc(size * n);
455 void *ctx;
456 COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n);
457 void *res = REAL(calloc)(size, n);
458 // The memory is zeroed and thus is all written.
459 COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n);
460 return res;
461 }
462
INTERCEPTOR(void *,malloc,uptr size)463 INTERCEPTOR(void*, malloc, uptr size) {
464 if (EsanDuringInit && REAL(malloc) == nullptr)
465 return handleEarlyAlloc(size);
466 void *ctx;
467 COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
468 return REAL(malloc)(size);
469 }
470
INTERCEPTOR(void,free,void * p)471 INTERCEPTOR(void, free, void *p) {
472 void *ctx;
473 // There are only a few early allocation requests, so we simply skip the free.
474 if (isInEarlyAllocBuf(p))
475 return;
476 COMMON_INTERCEPTOR_ENTER(ctx, free, p);
477 REAL(free)(p);
478 }
479
480 namespace __esan {
481
initializeInterceptors()482 void initializeInterceptors() {
483 InitializeCommonInterceptors();
484
485 INTERCEPT_FUNCTION(strcpy); // NOLINT
486 INTERCEPT_FUNCTION(strncpy);
487
488 INTERCEPT_FUNCTION(open);
489 ESAN_MAYBE_INTERCEPT_OPEN64;
490 INTERCEPT_FUNCTION(creat);
491 ESAN_MAYBE_INTERCEPT_CREAT64;
492 INTERCEPT_FUNCTION(unlink);
493 INTERCEPT_FUNCTION(rmdir);
494
495 ESAN_MAYBE_INTERCEPT_SIGNAL;
496 ESAN_MAYBE_INTERCEPT_SIGACTION;
497 ESAN_MAYBE_INTERCEPT_SIGPROCMASK;
498 ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
499
500 INTERCEPT_FUNCTION(calloc);
501 INTERCEPT_FUNCTION(malloc);
502 INTERCEPT_FUNCTION(free);
503
504 // TODO(bruening): intercept routines that other sanitizers intercept that
505 // are not in the common pool or here yet, ideally by adding to the common
506 // pool. Examples include wcslen and bcopy.
507
508 // TODO(bruening): there are many more libc routines that read or write data
509 // structures that no sanitizer is intercepting: sigaction, strtol, etc.
510 }
511
512 } // namespace __esan
513