]> git.proxmox.com Git - rustc.git/blame - src/libcompiler_builtins/compiler-rt/lib/esan/esan_interceptors.cpp
New upstream version 1.25.0+dfsg1
[rustc.git] / src / libcompiler_builtins / compiler-rt / lib / esan / esan_interceptors.cpp
CommitLineData
5bcae85e
SL
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
23using 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
2c00a5a8
XL
34DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
35
5bcae85e
SL
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#include "sanitizer_common/sanitizer_common_interceptors.inc"
179
180//===----------------------------------------------------------------------===//
181// Syscall interception
182//===----------------------------------------------------------------------===//
183
184// We want the caller's PC b/c unlike the other function interceptors these
185// are separate pre and post functions called around the app's syscall().
186
187#define COMMON_SYSCALL_PRE_READ_RANGE(ptr, size) \
188 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, false)
189
190#define COMMON_SYSCALL_PRE_WRITE_RANGE(ptr, size) \
191 do { \
192 (void)(ptr); \
193 (void)(size); \
194 } while (false)
195
196#define COMMON_SYSCALL_POST_READ_RANGE(ptr, size) \
197 do { \
198 (void)(ptr); \
199 (void)(size); \
200 } while (false)
201
202// The actual amount written is in post, not pre.
203#define COMMON_SYSCALL_POST_WRITE_RANGE(ptr, size) \
204 processRangeAccess(GET_CALLER_PC(), (uptr)ptr, size, true)
205
206#define COMMON_SYSCALL_ACQUIRE(addr) \
207 do { \
208 (void)(addr); \
209 } while (false)
210#define COMMON_SYSCALL_RELEASE(addr) \
211 do { \
212 (void)(addr); \
213 } while (false)
214#define COMMON_SYSCALL_FD_CLOSE(fd) \
215 do { \
216 (void)(fd); \
217 } while (false)
218#define COMMON_SYSCALL_FD_ACQUIRE(fd) \
219 do { \
220 (void)(fd); \
221 } while (false)
222#define COMMON_SYSCALL_FD_RELEASE(fd) \
223 do { \
224 (void)(fd); \
225 } while (false)
226#define COMMON_SYSCALL_PRE_FORK() \
227 do { \
228 } while (false)
229#define COMMON_SYSCALL_POST_FORK(res) \
230 do { \
231 (void)(res); \
232 } while (false)
233
234#include "sanitizer_common/sanitizer_common_syscalls.inc"
235
236//===----------------------------------------------------------------------===//
237// Custom interceptors
238//===----------------------------------------------------------------------===//
239
240// TODO(bruening): move more of these to the common interception pool as they
241// are shared with tsan and asan.
242// While our other files match LLVM style, here we match sanitizer style as we
243// expect to move these to the common pool.
244
245INTERCEPTOR(char *, strcpy, char *dst, const char *src) { // NOLINT
246 void *ctx;
247 COMMON_INTERCEPTOR_ENTER(ctx, strcpy, dst, src);
248 uptr srclen = internal_strlen(src);
249 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, srclen + 1);
250 COMMON_INTERCEPTOR_READ_RANGE(ctx, src, srclen + 1);
251 return REAL(strcpy)(dst, src); // NOLINT
252}
253
254INTERCEPTOR(char *, strncpy, char *dst, char *src, uptr n) {
255 void *ctx;
256 COMMON_INTERCEPTOR_ENTER(ctx, strncpy, dst, src, n);
257 uptr srclen = internal_strnlen(src, n);
258 uptr copied_size = srclen + 1 > n ? n : srclen + 1;
259 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, copied_size);
260 COMMON_INTERCEPTOR_READ_RANGE(ctx, src, copied_size);
261 return REAL(strncpy)(dst, src, n);
262}
263
264INTERCEPTOR(int, open, const char *name, int flags, int mode) {
265 void *ctx;
266 COMMON_INTERCEPTOR_ENTER(ctx, open, name, flags, mode);
267 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
268 return REAL(open)(name, flags, mode);
269}
270
271#if SANITIZER_LINUX
272INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
273 void *ctx;
274 COMMON_INTERCEPTOR_ENTER(ctx, open64, name, flags, mode);
275 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
276 return REAL(open64)(name, flags, mode);
277}
278#define ESAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
279#else
280#define ESAN_MAYBE_INTERCEPT_OPEN64
281#endif
282
283INTERCEPTOR(int, creat, const char *name, int mode) {
284 void *ctx;
285 COMMON_INTERCEPTOR_ENTER(ctx, creat, name, mode);
286 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
287 return REAL(creat)(name, mode);
288}
289
290#if SANITIZER_LINUX
291INTERCEPTOR(int, creat64, const char *name, int mode) {
292 void *ctx;
293 COMMON_INTERCEPTOR_ENTER(ctx, creat64, name, mode);
294 COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
295 return REAL(creat64)(name, mode);
296}
297#define ESAN_MAYBE_INTERCEPT_CREAT64 INTERCEPT_FUNCTION(creat64)
298#else
299#define ESAN_MAYBE_INTERCEPT_CREAT64
300#endif
301
302INTERCEPTOR(int, unlink, char *path) {
303 void *ctx;
304 COMMON_INTERCEPTOR_ENTER(ctx, unlink, path);
305 COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
306 return REAL(unlink)(path);
307}
308
5bcae85e
SL
309INTERCEPTOR(int, puts, const char *s) {
310 void *ctx;
311 COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
312 COMMON_INTERCEPTOR_READ_RANGE(ctx, s, internal_strlen(s));
313 return REAL(puts)(s);
314}
315
316INTERCEPTOR(int, rmdir, char *path) {
317 void *ctx;
318 COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path);
319 COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0);
320 return REAL(rmdir)(path);
321}
322
323//===----------------------------------------------------------------------===//
324// Shadow-related interceptors
325//===----------------------------------------------------------------------===//
326
327// These are candidates for sharing with all sanitizers if shadow memory
328// support is also standardized.
329
330INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
331 int fd, OFF_T off) {
332 if (UNLIKELY(REAL(mmap) == nullptr)) {
333 // With esan init during interceptor init and a static libc preventing
334 // our early-calloc from triggering, we can end up here before our
335 // REAL pointer is set up.
336 return (void *)internal_mmap(addr, sz, prot, flags, fd, off);
337 }
338 void *ctx;
339 COMMON_INTERCEPTOR_ENTER(ctx, mmap, addr, sz, prot, flags, fd, off);
340 if (!fixMmapAddr(&addr, sz, flags))
341 return (void *)-1;
342 void *result = REAL(mmap)(addr, sz, prot, flags, fd, off);
343 return (void *)checkMmapResult((uptr)result, sz);
344}
345
346#if SANITIZER_LINUX
347INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
348 int fd, OFF64_T off) {
349 void *ctx;
350 COMMON_INTERCEPTOR_ENTER(ctx, mmap64, addr, sz, prot, flags, fd, off);
351 if (!fixMmapAddr(&addr, sz, flags))
352 return (void *)-1;
353 void *result = REAL(mmap64)(addr, sz, prot, flags, fd, off);
354 return (void *)checkMmapResult((uptr)result, sz);
355}
356#define ESAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64)
357#else
358#define ESAN_MAYBE_INTERCEPT_MMAP64
359#endif
360
361//===----------------------------------------------------------------------===//
362// Signal-related interceptors
363//===----------------------------------------------------------------------===//
364
365#if SANITIZER_LINUX
366typedef void (*signal_handler_t)(int);
367INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
368 void *ctx;
369 COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler);
370 signal_handler_t result;
371 if (!processSignal(signum, handler, &result))
372 return result;
373 else
374 return REAL(signal)(signum, handler);
375}
376#define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal)
377#else
378#error Platform not supported
379#define ESAN_MAYBE_INTERCEPT_SIGNAL
380#endif
381
382#if SANITIZER_LINUX
383DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
384 struct sigaction *oldact)
385INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
386 struct sigaction *oldact) {
387 void *ctx;
388 COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact);
389 if (!processSigaction(signum, act, oldact))
390 return 0;
391 else
392 return REAL(sigaction)(signum, act, oldact);
393}
394
395// This is required to properly use internal_sigaction.
396namespace __sanitizer {
397int real_sigaction(int signum, const void *act, void *oldact) {
398 if (REAL(sigaction) == nullptr) {
399 // With an instrumented allocator, this is called during interceptor init
400 // and we need a raw syscall solution.
401 return internal_sigaction_syscall(signum, act, oldact);
402 }
403 return REAL(sigaction)(signum, (const struct sigaction *)act,
404 (struct sigaction *)oldact);
405}
406} // namespace __sanitizer
407
408#define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction)
409#else
410#error Platform not supported
411#define ESAN_MAYBE_INTERCEPT_SIGACTION
412#endif
413
414#if SANITIZER_LINUX
415INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
416 __sanitizer_sigset_t *oldset) {
417 void *ctx;
418 COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
419 int res = 0;
420 if (processSigprocmask(how, set, oldset))
421 res = REAL(sigprocmask)(how, set, oldset);
422 if (!res && oldset)
423 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
424 return res;
425}
426#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
427#else
428#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
429#endif
430
431#if !SANITIZER_WINDOWS
432INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
433 __sanitizer_sigset_t *oldset) {
434 void *ctx;
435 COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
436 int res = 0;
437 if (processSigprocmask(how, set, oldset))
438 res = REAL(sigprocmask)(how, set, oldset);
439 if (!res && oldset)
440 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
441 return res;
442}
443#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
444#else
445#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
446#endif
447
448//===----------------------------------------------------------------------===//
449// Malloc interceptors
450//===----------------------------------------------------------------------===//
451
7cac9316
XL
452static const uptr early_alloc_buf_size = 4096;
453static uptr allocated_bytes;
454static char early_alloc_buf[early_alloc_buf_size];
455
456static bool isInEarlyAllocBuf(const void *ptr) {
457 return ((uptr)ptr >= (uptr)early_alloc_buf &&
458 ((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
459}
5bcae85e
SL
460
461static void *handleEarlyAlloc(uptr size) {
462 // If esan is initialized during an interceptor (which happens with some
463 // tcmalloc implementations that call pthread_mutex_lock), the call from
7cac9316
XL
464 // dlsym to calloc will deadlock.
465 // dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
466 // We work around it by using a static buffer for the early malloc/calloc
467 // requests.
5bcae85e
SL
468 // This solution will also allow us to deliberately intercept malloc & family
469 // in the future (to perform tool actions on each allocation, without
470 // replacing the allocator), as it also solves the problem of intercepting
471 // calloc when it will itself be called before its REAL pointer is
472 // initialized.
5bcae85e
SL
473 // We do not handle multiple threads here. This only happens at process init
474 // time, and while it's possible for a shared library to create early threads
475 // that race here, we consider that to be a corner case extreme enough that
476 // it's not worth the effort to handle.
7cac9316
XL
477 void *mem = (void *)&early_alloc_buf[allocated_bytes];
478 allocated_bytes += size;
479 CHECK_LT(allocated_bytes, early_alloc_buf_size);
480 return mem;
5bcae85e
SL
481}
482
483INTERCEPTOR(void*, calloc, uptr size, uptr n) {
484 if (EsanDuringInit && REAL(calloc) == nullptr)
485 return handleEarlyAlloc(size * n);
486 void *ctx;
487 COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n);
488 void *res = REAL(calloc)(size, n);
489 // The memory is zeroed and thus is all written.
490 COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n);
491 return res;
492}
493
7cac9316
XL
494INTERCEPTOR(void*, malloc, uptr size) {
495 if (EsanDuringInit && REAL(malloc) == nullptr)
496 return handleEarlyAlloc(size);
497 void *ctx;
498 COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
499 return REAL(malloc)(size);
500}
501
5bcae85e
SL
502INTERCEPTOR(void, free, void *p) {
503 void *ctx;
7cac9316
XL
504 // There are only a few early allocation requests, so we simply skip the free.
505 if (isInEarlyAllocBuf(p))
5bcae85e 506 return;
7cac9316 507 COMMON_INTERCEPTOR_ENTER(ctx, free, p);
5bcae85e
SL
508 REAL(free)(p);
509}
510
511namespace __esan {
512
513void initializeInterceptors() {
514 InitializeCommonInterceptors();
515
516 INTERCEPT_FUNCTION(strcpy); // NOLINT
517 INTERCEPT_FUNCTION(strncpy);
518
519 INTERCEPT_FUNCTION(open);
520 ESAN_MAYBE_INTERCEPT_OPEN64;
521 INTERCEPT_FUNCTION(creat);
522 ESAN_MAYBE_INTERCEPT_CREAT64;
523 INTERCEPT_FUNCTION(unlink);
524 INTERCEPT_FUNCTION(fread);
525 INTERCEPT_FUNCTION(fwrite);
526 INTERCEPT_FUNCTION(puts);
527 INTERCEPT_FUNCTION(rmdir);
528
529 INTERCEPT_FUNCTION(mmap);
530 ESAN_MAYBE_INTERCEPT_MMAP64;
531
532 ESAN_MAYBE_INTERCEPT_SIGNAL;
533 ESAN_MAYBE_INTERCEPT_SIGACTION;
534 ESAN_MAYBE_INTERCEPT_SIGPROCMASK;
535 ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
536
537 INTERCEPT_FUNCTION(calloc);
7cac9316 538 INTERCEPT_FUNCTION(malloc);
5bcae85e
SL
539 INTERCEPT_FUNCTION(free);
540
541 // TODO(bruening): intercept routines that other sanitizers intercept that
542 // are not in the common pool or here yet, ideally by adding to the common
543 // pool. Examples include wcslen and bcopy.
544
545 // TODO(bruening): there are many more libc routines that read or write data
546 // structures that no sanitizer is intercepting: sigaction, strtol, etc.
547}
548
549} // namespace __esan