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 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 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 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 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 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 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 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 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); 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 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 { 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 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 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 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 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 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 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 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 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