diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
commit | 8ef50bf3d1c287b5013c3168de77a462dfce3495 (patch) | |
tree | 3467f3372c1195b1546172d89af2205a50b1866d /lib/tsan/rtl | |
parent | 11023dc647fd8f41418da90d59db138400d0f334 (diff) | |
download | src-8ef50bf3d1c287b5013c3168de77a462dfce3495.tar.gz src-8ef50bf3d1c287b5013c3168de77a462dfce3495.zip |
Import compiler-rt release_34 branch r197381.vendor/compiler-rt/compiler-rt-r197381
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=274201
svn path=/vendor/compiler-rt/compiler-rt-r197381/; revision=274202; tag=vendor/compiler-rt/compiler-rt-r197381
Diffstat (limited to 'lib/tsan/rtl')
33 files changed, 1301 insertions, 818 deletions
diff --git a/lib/tsan/rtl/CMakeLists.txt b/lib/tsan/rtl/CMakeLists.txt deleted file mode 100644 index f1a8ff4d6558..000000000000 --- a/lib/tsan/rtl/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -set(TSAN_SOURCES - tsan_clock.cc - tsan_flags.cc - tsan_fd.cc - tsan_interceptors.cc - tsan_interface_ann.cc - tsan_interface_atomic.cc - tsan_interface.cc - tsan_interface_java.cc - tsan_md5.cc - tsan_mman.cc - tsan_mutex.cc - tsan_mutexset.cc - tsan_report.cc - tsan_rtl.cc - tsan_rtl_mutex.cc - tsan_rtl_report.cc - tsan_rtl_thread.cc - tsan_stat.cc - tsan_suppressions.cc - tsan_symbolize.cc - tsan_sync.cc - ) - -if(APPLE) - list(APPEND TSAN_SOURCES tsan_platform_mac.cc) -elseif(UNIX) - # Assume Linux - list(APPEND TSAN_SOURCES - tsan_platform_linux.cc - tsan_symbolize_addr2line_linux.cc) -endif() - -set(TSAN_RUNTIME_LIBRARIES) -# TSan is currently supported on 64-bit Linux only. -if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) - set(TSAN_ASM_SOURCES tsan_rtl_amd64.S) - # Pass ASM file directly to the C++ compiler. - set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES - LANGUAGE C) - set(arch "x86_64") - add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} - SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${TSAN_CFLAGS} - DEFS ${TSAN_COMMON_DEFINITIONS} - SYMS tsan.syms) - list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) -endif() diff --git a/lib/tsan/rtl/tsan.syms b/lib/tsan/rtl/tsan.syms deleted file mode 100644 index 4464a0a231c9..000000000000 --- a/lib/tsan/rtl/tsan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __tsan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra new file mode 100644 index 000000000000..49ed6b46629e --- /dev/null +++ b/lib/tsan/rtl/tsan.syms.extra @@ -0,0 +1,14 @@ +__tsan_init +__tsan_read* +__tsan_write* +__tsan_vptr* +__tsan_func* +__tsan_atomic* +__tsan_java* +__tsan_unaligned* +__tsan_release +__tsan_acquire +Annotate* +WTFAnnotate* +RunningOnValgrind +ValgrindSlowdown diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index 7150e2e255d8..1e53a8e02f0a 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -41,10 +41,8 @@ const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. const int kClkBits = 42; -#ifndef TSAN_GO -const int kShadowStackSize = 4 * 1024; -const int kTraceStackSize = 256; -#endif +const uptr kShadowStackSize = 64 * 1024; +const uptr kTraceStackSize = 256; #ifdef TSAN_SHADOW_COUNT # if TSAN_SHADOW_COUNT == 2 \ @@ -162,7 +160,6 @@ class ReportDesc; class RegionAlloc; class StackTrace; struct MBlock; -struct Suppression; } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc index 14bdbb53b322..86db119fc918 100644 --- a/lib/tsan/rtl/tsan_fd.cc +++ b/lib/tsan/rtl/tsan_fd.cc @@ -42,6 +42,11 @@ struct FdContext { static FdContext fdctx; +static bool bogusfd(int fd) { + // Apparently a bogus fd value. + return fd < 0 || fd >= (1 << 30); +} + static FdSync *allocsync() { FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); atomic_store(&s->rc, 1, memory_order_relaxed); @@ -69,6 +74,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) { } static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { + CHECK_GE(fd, 0); CHECK_LT(fd, kTableSize); atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; uptr l1 = atomic_load(pl1, memory_order_consume); @@ -148,6 +154,8 @@ bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { } void FdAcquire(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); @@ -157,22 +165,28 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { } void FdRelease(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Release(thr, pc, (uptr)s); - MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. MemoryWrite(thr, pc, (uptr)d, kSizeLog8); @@ -187,11 +201,15 @@ void FdClose(ThreadState *thr, uptr pc, int fd) { void FdFileCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.filesync); } void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); + if (bogusfd(oldfd) || bogusfd(newfd)) + return; // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); MemoryRead(thr, pc, (uptr)od, kSizeLog8); @@ -209,32 +227,44 @@ void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { void FdEventCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdPollCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // It can be a UDP socket. init(thr, pc, fd, &fdctx.socksync); } void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Acquire(thr, pc, (uptr)&fdctx.connectsync); init(thr, pc, newfd, &fdctx.socksync); @@ -242,12 +272,16 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Release(thr, pc, (uptr)&fdctx.connectsync); } void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.socksync); } diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index c062592f482d..c6f24bf49abd 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -26,13 +26,42 @@ Flags *flags() { // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS void OverrideFlags(Flags *f); +extern "C" const char* __tsan_default_options(); #else -SANITIZER_INTERFACE_ATTRIBUTE void WEAK OverrideFlags(Flags *f) { (void)f; } +extern "C" const char *WEAK __tsan_default_options() { + return ""; +} #endif +static void ParseFlags(Flags *f, const char *env) { + ParseFlag(env, &f->enable_annotations, "enable_annotations"); + ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); + ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); + ParseFlag(env, &f->suppress_java, "suppress_java"); + ParseFlag(env, &f->report_bugs, "report_bugs"); + ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); + ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); + ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); + ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); + ParseFlag(env, &f->suppressions, "suppressions"); + ParseFlag(env, &f->print_suppressions, "print_suppressions"); + ParseFlag(env, &f->print_benign, "print_benign"); + ParseFlag(env, &f->exitcode, "exitcode"); + ParseFlag(env, &f->halt_on_error, "halt_on_error"); + ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); + ParseFlag(env, &f->profile_memory, "profile_memory"); + ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); + ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); + ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb"); + ParseFlag(env, &f->stop_on_start, "stop_on_start"); + ParseFlag(env, &f->history_size, "history_size"); + ParseFlag(env, &f->io_sync, "io_sync"); +} + void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); @@ -47,53 +76,35 @@ void InitializeFlags(Flags *f, const char *env) { f->report_signal_unsafe = true; f->report_atomic_races = true; f->force_seq_cst_atomics = false; - f->strip_path_prefix = ""; f->suppressions = ""; f->print_suppressions = false; f->print_benign = false; f->exitcode = 66; - f->log_path = "stderr"; + f->halt_on_error = false; f->atexit_sleep_ms = 1000; - f->verbosity = 0; f->profile_memory = ""; f->flush_memory_ms = 0; f->flush_symbolizer_ms = 5000; + f->memory_limit_mb = 0; f->stop_on_start = false; f->running_on_valgrind = false; - f->external_symbolizer_path = ""; f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. f->io_sync = 1; + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + *static_cast<CommonFlags*>(f) = *cf; + // Let a frontend override. OverrideFlags(f); - + ParseFlags(f, __tsan_default_options()); + ParseCommonFlagsFromString(__tsan_default_options()); // Override from command line. - ParseFlag(env, &f->enable_annotations, "enable_annotations"); - ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); - ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); - ParseFlag(env, &f->suppress_java, "suppress_java"); - ParseFlag(env, &f->report_bugs, "report_bugs"); - ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); - ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); - ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); - ParseFlag(env, &f->suppressions, "suppressions"); - ParseFlag(env, &f->print_suppressions, "print_suppressions"); - ParseFlag(env, &f->print_benign, "print_benign"); - ParseFlag(env, &f->exitcode, "exitcode"); - ParseFlag(env, &f->log_path, "log_path"); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); - ParseFlag(env, &f->verbosity, "verbosity"); - ParseFlag(env, &f->profile_memory, "profile_memory"); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); - ParseFlag(env, &f->stop_on_start, "stop_on_start"); - ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path"); - ParseFlag(env, &f->history_size, "history_size"); - ParseFlag(env, &f->io_sync, "io_sync"); + ParseFlags(f, env); + ParseCommonFlagsFromString(env); + *static_cast<CommonFlags*>(f) = *cf; + // Sanity check. if (!f->report_bugs) { f->report_thread_leaks = false; f->report_destroy_locked = false; diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h index aaacd98a6223..3916df3cc9e1 100644 --- a/lib/tsan/rtl/tsan_flags.h +++ b/lib/tsan/rtl/tsan_flags.h @@ -20,9 +20,11 @@ // header may be included in the user code, and shouldn't include // other headers from TSan or common sanitizer runtime. +#include "sanitizer_common/sanitizer_flags.h" + namespace __tsan { -struct Flags { +struct Flags : CommonFlags { // Enable dynamic annotations, otherwise they are no-ops. bool enable_annotations; // Supress a race report if we've already output another race report @@ -48,8 +50,6 @@ struct Flags { // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; - // Strip that prefix from file paths in reports. - const char *strip_path_prefix; // Suppressions filename. const char *suppressions; // Print matched suppressions at exit. @@ -58,27 +58,24 @@ struct Flags { bool print_benign; // Override exit status if something was reported. int exitcode; - // Write logs to "log_path.pid". - // The special values are "stdout" and "stderr". - // The default is "stderr". - const char *log_path; + // Exit after first reported error. + bool halt_on_error; // Sleep in main thread before exiting for that many ms // (useful to catch "at exit" races). int atexit_sleep_ms; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // If set, periodically write memory profile to that file. const char *profile_memory; // Flush shadow memory every X ms. int flush_memory_ms; // Flush symbolizer caches every X ms. int flush_symbolizer_ms; + // Resident memory limit in MB to aim at. + // If the process consumes more memory, then TSan will flush shadow memory. + int memory_limit_mb; // Stops on start until __tsan_resume() is called (for debugging). bool stop_on_start; // Controls whether RunningOnValgrind() returns true or false. bool running_on_valgrind; - // Path to external symbolizer. - const char *external_symbolizer_path; // Per-thread history size, controls how many previous memory accesses // are remembered per thread. Possible values are [0..7]. // history_size=0 amounts to 32K memory accesses. Each next value doubles diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index f18b26f6abe4..ef38f7987cde 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -22,6 +22,7 @@ #include "interception/interception.h" #include "tsan_interface.h" #include "tsan_platform.h" +#include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_fd.h" @@ -35,11 +36,6 @@ struct my_siginfo_t { u64 opaque[128 / sizeof(u64)]; }; -struct sigset_t { - // The size is determined by looking at sizeof of real sigset_t on linux. - u64 val[128 / sizeof(u64)]; -}; - struct ucontext_t { // The size is determined by looking at sizeof of real ucontext_t on linux. u64 opaque[936 / sizeof(u64) + 1]; @@ -47,15 +43,16 @@ struct ucontext_t { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); +DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); extern "C" int pthread_mutexattr_gettype(void *a, int *type); extern "C" int pthread_yield(); -extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); -extern "C" int sigfillset(sigset_t *set); +extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset); +// REAL(sigfillset) defined in common interceptors. +DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); @@ -67,9 +64,9 @@ extern "C" void __libc_free(void *ptr); extern "C" int mallopt(int param, int value); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; -const int kPthreadAttrSize = 56; const int EINVAL = 22; const int EBUSY = 16; +const int EOWNERDEAD = 130; const int EPOLL_CTL_ADD = 1; const int SIGILL = 4; const int SIGABRT = 6; @@ -77,6 +74,7 @@ const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGBUS = 7; +const int SIGSYS = 31; void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; @@ -97,7 +95,7 @@ struct sigaction_t { sighandler_t sa_handler; void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); }; - sigset_t sa_mask; + __sanitizer_sigset_t sa_mask; int sa_flags; void (*sa_restorer)(); }; @@ -128,6 +126,19 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; + +// The object is 64-byte aligned, because we want hot data to be located in +// a single cache line if possible (it's accessed in every interceptor). +static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)]; +static LibIgnore *libignore() { + return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]); +} + +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(0); +} + } // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { @@ -150,12 +161,14 @@ class ScopedInterceptor { private: ThreadState *const thr_; const int in_rtl_; + bool in_ignored_lib_; }; ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) - , in_rtl_(thr->in_rtl) { + , in_rtl_(thr->in_rtl) + , in_ignored_lib_(false) { if (thr_->in_rtl == 0) { Initialize(thr); FuncEntry(thr, pc); @@ -164,9 +177,18 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, } else { thr_->in_rtl++; } + if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { + in_ignored_lib_ = true; + thr_->in_ignored_lib = true; + ThreadIgnoreBegin(thr_); + } } ScopedInterceptor::~ScopedInterceptor() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = false; + ThreadIgnoreEnd(thr_); + } thr_->in_rtl--; if (thr_->in_rtl == 0) { FuncExit(thr_); @@ -181,8 +203,7 @@ ScopedInterceptor::~ScopedInterceptor() { StatInc(thr, StatInt_##func); \ const uptr caller_pc = GET_CALLER_PC(); \ ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \ - __sanitizer::StackTrace::GetCurrentPc()); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ @@ -192,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() { Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1) \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -235,6 +256,28 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { return res; } +TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); + // dlopen will execute global constructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + void *res = REAL(dlopen)(filename, flag); + thr->in_rtl = 1; + libignore()->OnLibraryLoaded(filename); + return res; +} + +TSAN_INTERCEPTOR(int, dlclose, void *handle) { + SCOPED_INTERCEPTOR_RAW(dlclose, handle); + // dlclose will execute global destructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + int res = REAL(dlclose)(handle); + thr->in_rtl = 1; + libignore()->OnLibraryUnloaded(); + return res; +} + class AtExitContext { public: AtExitContext() @@ -314,8 +357,14 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); - if (dso) - return REAL(__cxa_atexit)(f, arg, dso); + if (dso) { + // Memory allocation in __cxa_atexit will race with free during exit, + // because we do not see synchronization around atexit callback list. + ThreadIgnoreBegin(thr); + int res = REAL(__cxa_atexit)(f, arg, dso); + ThreadIgnoreEnd(thr); + return res; + } return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } @@ -362,27 +411,37 @@ static void LongJmp(ThreadState *thr, uptr *env) { CHECK(0); } +// FIXME: put everything below into a common extern "C" block? extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { ScopedInRtl in_rtl; SetJmp(cur_thread(), sp, mangled_sp); } // Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_setjmp(void *env); extern "C" int __interceptor_setjmp(void *env) { CHECK(0); return 0; } +// FIXME: any reason to have a separate declaration? +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor__setjmp(void *env); extern "C" int __interceptor__setjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_sigsetjmp(void *env); extern "C" int __interceptor_sigsetjmp(void *env) { CHECK(0); return 0; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor___sigsetjmp(void *env); extern "C" int __interceptor___sigsetjmp(void *env) { CHECK(0); return 0; @@ -433,7 +492,8 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) + return AllocatorReturnNull(); void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); @@ -494,15 +554,26 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { invoke_malloc_hook(p, size); \ return p; +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size); void *operator new(__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znwm); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size); void *operator new[](__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znam); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size, std::nothrow_t const&); void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); } @@ -515,15 +586,26 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr); void operator delete(void *ptr) { OPERATOR_DELETE_BODY(_ZdlPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr); void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&); void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPv); } + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&); void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); } @@ -561,30 +643,6 @@ TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { return res; } -TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2); - uptr len = 0; - for (; s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false); - return s1[len] - s2[len]; -} - -TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) { - SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n); - uptr len = 0; - for (; len < n && s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); - MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); - return len == n ? 0 : s1[len] - s2[len]; -} - TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) { SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n); void *res = REAL(memchr)(s, c, n); @@ -654,6 +712,12 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { return res; } +TSAN_INTERCEPTOR(char*, strdup, const char *str) { + SCOPED_TSAN_INTERCEPTOR(strdup, str); + // strdup will call malloc, so no instrumentation is required here. + return REAL(strdup)(str); +} + static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { @@ -704,23 +768,23 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { } TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(valloc, sz); + SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(pvalloc, sz); + SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz); + SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } @@ -789,7 +853,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) { { ThreadState *thr = cur_thread(); ScopedInRtl in_rtl; - if (pthread_setspecific(g_thread_finalize_key, (void*)4)) { + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } @@ -809,21 +874,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) { TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { - SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param); + SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; - pthread_attr_getdetachstate(attr, &detached); - -#if defined(TSAN_DEBUG_OUTPUT) - int verbosity = (TSAN_DEBUG_OUTPUT); -#else - int verbosity = 0; -#endif - AdjustStackSizeLinux(attr, verbosity); + REAL(pthread_attr_getdetachstate)(attr, &detached); + AdjustStackSizeLinux(attr); ThreadParam p; p.callback = callback; @@ -843,7 +902,7 @@ TSAN_INTERCEPTOR(int, pthread_create, } TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { - SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret); + SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret); int tid = ThreadTid(thr, pc, (uptr)th); int res = BLOCK_REAL(pthread_join)(th, ret); if (res == 0) { @@ -887,21 +946,13 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m); - int res = REAL(pthread_mutex_lock)(m); - if (res == 0) { - MutexLock(thr, pc, (uptr)m); - } - return res; -} - TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); int res = REAL(pthread_mutex_trylock)(m); - if (res == 0) { + if (res == EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == EOWNERDEAD) MutexLock(thr, pc, (uptr)m); - } return res; } @@ -914,13 +965,6 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_mutex_unlock)(m); - return res; -} - TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); @@ -1043,45 +1087,18 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } -// libpthread.so contains several versions of pthread_cond_init symbol. -// When we just dlsym() it, we get the wrong (old) version. -/* -TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a); - int res = REAL(pthread_cond_init)(c, a); - return res; -} -*/ - TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c); + MemoryWrite(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_destroy)(c); return res; } -TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c); - int res = REAL(pthread_cond_signal)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c); - int res = REAL(pthread_cond_broadcast)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_cond_wait)(c, m); - MutexLock(thr, pc, (uptr)m); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { +TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, + void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime); MutexUnlock(thr, pc, (uptr)m); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_timedwait)(c, m, abstime); MutexLock(thr, pc, (uptr)m); return res; @@ -1114,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { } TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { - SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f); + SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); + // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, + // the user callback must be executed with thr->in_rtl == 0. if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o); @@ -1126,14 +1145,16 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { (*f)(); CHECK_EQ(thr->in_rtl, 0); thr->in_rtl = old_in_rtl; - Release(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); } else { while (v != 2) { pthread_yield(); v = atomic_load(a, memory_order_acquire); } - Acquire(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Acquire(thr, pc, (uptr)o); } return 0; } @@ -1392,22 +1413,6 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { return res; } -TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { - SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); - int fd2 = REAL(accept)(fd, addr, addrlen); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - -TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { - SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f); - int fd2 = REAL(accept4)(fd, addr, addrlen, f); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); @@ -1466,58 +1471,30 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt); - int res = REAL(readv)(fd, vec, cnt); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off); - int res = REAL(preadv64)(fd, vec, cnt, off); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(writev)(fd, vec, cnt); - return res; -} - -TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(pwritev64)(fd, vec, cnt, off); - return res; -} - TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(send)(fd, buf, len, flags); return res; } TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(sendmsg)(fd, msg, flags); return res; } TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); + if (fd >= 0) + FdAccess(thr, pc, fd); int res = REAL(recv)(fd, buf, len, flags); if (res >= 0 && fd >= 0) { FdAcquire(thr, pc, fd); @@ -1525,15 +1502,6 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) { - SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags); - int res = REAL(recvmsg)(fd, msg, flags); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - TSAN_INTERCEPTOR(int, unlink, char *path) { SCOPED_TSAN_INTERCEPTOR(unlink, path); Release(thr, pc, File2addr(path)); @@ -1571,6 +1539,7 @@ TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) { } TSAN_INTERCEPTOR(int, fclose, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fclose, stream); if (stream) { @@ -1583,6 +1552,7 @@ TSAN_INTERCEPTOR(int, fclose, void *stream) { } TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); @@ -1591,6 +1561,7 @@ TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); @@ -1599,7 +1570,10 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(int, fflush, void *stream) { - SCOPED_TSAN_INTERCEPTOR(fflush, stream); + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + } return REAL(fflush)(stream); } @@ -1632,27 +1606,23 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) { TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); - if (op == EPOLL_CTL_ADD && epfd >= 0) { + if (epfd >= 0) + FdAccess(thr, pc, epfd); + if (epfd >= 0 && fd >= 0) + FdAccess(thr, pc, fd); + if (op == EPOLL_CTL_ADD && epfd >= 0) FdRelease(thr, pc, epfd); - } int res = REAL(epoll_ctl)(epfd, op, fd, ev); - if (fd >= 0) - FdAccess(thr, pc, fd); return res; } TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); + if (epfd >= 0) + FdAccess(thr, pc, epfd); int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); - if (res > 0 && epfd >= 0) { + if (res > 0 && epfd >= 0) FdAcquire(thr, pc, epfd); - } - return res; -} - -TSAN_INTERCEPTOR(int, poll, void *fds, long_t nfds, int timeout) { - SCOPED_TSAN_INTERCEPTOR(poll, fds, nfds, timeout); - int res = BLOCK_REAL(poll)(fds, nfds, timeout); return res; } @@ -1662,7 +1632,7 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, SignalContext *sctx = SigCtx(thr); // Don't mess with synchronous signals. if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || - sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || + sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || // If we are sending signal to ourselves, we must process it now. (sctx && sig == sctx->int_signal_send) || // If we are in blocking function, we can safely process it now @@ -1714,7 +1684,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { internal_memcpy(&sigactions[sig], act, sizeof(*act)); sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); - sigfillset(&newact.sa_mask); + REAL(sigfillset)(&newact.sa_mask); if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { if (newact.sa_flags & SA_SIGINFO) newact.sa_sigaction = rtl_sigaction; @@ -1737,6 +1707,11 @@ TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { return old.sa_handler; } +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); SignalContext *sctx = SigCtx(thr); @@ -1787,13 +1762,30 @@ TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { return REAL(gettimeofday)(tv, tz); } +TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, + void *hints, void *rv) { + SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); + // We miss atomic synchronization in getaddrinfo, + // and can report false race between malloc and free + // inside of getaddrinfo. So ignore memory accesses. + ThreadIgnoreBegin(thr); + // getaddrinfo calls fopen, which can be intercepted by user. + thr->in_rtl--; + CHECK_EQ(thr->in_rtl, 0); + int res = REAL(getaddrinfo)(node, service, hints, rv); + thr->in_rtl++; + ThreadIgnoreEnd(thr); + return res; +} + // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; - Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) + Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); } TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) { @@ -1817,8 +1809,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) { } TSAN_INTERCEPTOR(int, fork, int fake) { - SCOPED_TSAN_INTERCEPTOR(fork, fake); - // It's intercepted merely to process pending signals. + SCOPED_INTERCEPTOR_RAW(fork, fake); int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -1829,6 +1820,12 @@ TSAN_INTERCEPTOR(int, fork, int fake) { return pid; } +static int OnExit(ThreadState *thr) { + int status = Finalize(thr); + REAL(fflush)(0); + return status; +} + struct TsanInterceptorContext { ThreadState *thr; const uptr caller_pc; @@ -1839,39 +1836,150 @@ struct TsanInterceptorContext { // Causes interceptor recursion (getpwuid_r() calls fopen()) #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS #undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// Causes interceptor recursion (getaddrinfo() and fopen()) +#undef SANITIZER_INTERCEPT_GETADDRINFO +#undef SANITIZER_INTERCEPT_GETNAMEINFO // Causes interceptor recursion (glob64() calls lstat64()) #undef SANITIZER_INTERCEPT_GLOB -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, true) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, false) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \ - TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ - ctx = (void*)&_ctx; \ - (void)ctx; +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ + true) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ + ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ + false) + +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ + TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ + ctx = (void *)&_ctx; \ + (void) ctx; + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ - FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) + #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ - FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) + #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name) + ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) + +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + CTX()->thread_registry->SetThreadNameByUserId(thread, name) + +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) + +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ + OnExit(((TsanInterceptorContext *) ctx)->thr) + +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + MutexLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + #include "sanitizer_common/sanitizer_common_interceptors.inc" -// FIXME: Implement these with MemoryAccessRange(). -#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) -#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#define TSAN_SYSCALL() \ + ThreadState *thr = cur_thread(); \ + ScopedSyscall scoped_syscall(thr) \ +/**/ + +struct ScopedSyscall { + ThreadState *thr; + + explicit ScopedSyscall(ThreadState *thr) + : thr(thr) { + if (thr->in_rtl == 0) + Initialize(thr); + thr->in_rtl++; + } + + ~ScopedSyscall() { + thr->in_rtl--; + if (thr->in_rtl == 0) + ProcessPendingSignals(thr); + } +}; + +static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { + TSAN_SYSCALL(); + MemoryAccessRange(thr, pc, p, s, write); +} + +static void syscall_fd_close(uptr pc, int fd) { + TSAN_SYSCALL(); + if (fd >= 0) + FdClose(thr, pc, fd); +} + +static void syscall_pre_fork(uptr pc) { + TSAN_SYSCALL(); +} + +static void syscall_post_fork(uptr pc, int res) { + TSAN_SYSCALL(); + if (res == 0) { + // child + FdOnFork(thr, pc); + } else if (res > 0) { + // parent + } +} + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) +#define COMMON_SYSCALL_PRE_FORK() \ + syscall_pre_fork(GET_CALLER_PC()) +#define COMMON_SYSCALL_POST_FORK(res) \ + syscall_post_fork(GET_CALLER_PC(), res) #include "sanitizer_common/sanitizer_common_syscalls.inc" namespace __tsan { +static void finalize(void *arg) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(thr); + REAL(fflush)(0); + if (status) + REAL(_exit)(status); +} + void ProcessPendingSignals(ThreadState *thr) { CHECK_EQ(thr->in_rtl, 0); SignalContext *sctx = SigCtx(thr); @@ -1881,8 +1989,8 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = true; sctx->pending_signal_count = 0; // These are too big for stack. - static THREADLOCAL sigset_t emptyset, oldset; - sigfillset(&emptyset); + static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; + REAL(sigfillset)(&emptyset); pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; @@ -1903,6 +2011,7 @@ void ProcessPendingSignals(ThreadState *thr) { uptr pc = signal->sigaction ? (uptr)sigactions[sig].sa_sigaction : (uptr)sigactions[sig].sa_handler; + pc += 1; // return address is expected, OutputReport() will undo this stack.Init(&pc, 1); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); @@ -1920,16 +2029,6 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = false; } -static void finalize(void *arg) { - ThreadState * thr = cur_thread(); - uptr pc = 0; - atexit_ctx->exit(thr, pc); - int status = Finalize(cur_thread()); - REAL(fflush)(0); - if (status) - _exit(status); -} - static void unreachable() { Printf("FATAL: ThreadSanitizer: unreachable called\n"); Die(); @@ -1973,7 +2072,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strlen); TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); - TSAN_INTERCEPT(strcmp); TSAN_INTERCEPT(memchr); TSAN_INTERCEPT(memrchr); TSAN_INTERCEPT(memmove); @@ -1981,10 +2079,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); - TSAN_INTERCEPT(strncmp); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); TSAN_INTERCEPT(strstr); + TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); TSAN_INTERCEPT(pthread_join); @@ -1992,10 +2090,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); - TSAN_INTERCEPT(pthread_mutex_lock); TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); - TSAN_INTERCEPT(pthread_mutex_unlock); TSAN_INTERCEPT(pthread_spin_init); TSAN_INTERCEPT(pthread_spin_destroy); @@ -2013,12 +2109,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); - // TSAN_INTERCEPT(pthread_cond_init); - TSAN_INTERCEPT(pthread_cond_destroy); - TSAN_INTERCEPT(pthread_cond_signal); - TSAN_INTERCEPT(pthread_cond_broadcast); - TSAN_INTERCEPT(pthread_cond_wait); - TSAN_INTERCEPT(pthread_cond_timedwait); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); @@ -2062,8 +2154,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(connect); TSAN_INTERCEPT(bind); TSAN_INTERCEPT(listen); - TSAN_INTERCEPT(accept); - TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); @@ -2072,14 +2162,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); - TSAN_INTERCEPT(readv); - TSAN_INTERCEPT(preadv64); - TSAN_INTERCEPT(writev); - TSAN_INTERCEPT(pwritev64); TSAN_INTERCEPT(send); TSAN_INTERCEPT(sendmsg); TSAN_INTERCEPT(recv); - TSAN_INTERCEPT(recvmsg); TSAN_INTERCEPT(unlink); TSAN_INTERCEPT(fopen); @@ -2095,10 +2180,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(epoll_ctl); TSAN_INTERCEPT(epoll_wait); - TSAN_INTERCEPT(poll); TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); + TSAN_INTERCEPT(sigsuspend); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); @@ -2106,6 +2191,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(usleep); TSAN_INTERCEPT(nanosleep); TSAN_INTERCEPT(gettimeofday); + TSAN_INTERCEPT(getaddrinfo); TSAN_INTERCEPT(mlock); TSAN_INTERCEPT(munlock); @@ -2113,8 +2199,11 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); + TSAN_INTERCEPT(dlclose); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); + TSAN_INTERCEPT(_exit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -2136,9 +2225,15 @@ void InitializeInterceptors() { } void internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal users + // signals. + __sanitizer_kernel_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); REAL(pthread_detach)(th); + internal_sigprocmask(SIG_SETMASK, &old, 0); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc index efad8c192d6e..9de3808e79ff 100644 --- a/lib/tsan/rtl/tsan_interface.cc +++ b/lib/tsan/rtl/tsan_interface.cc @@ -38,49 +38,55 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } -u16 __tsan_unaligned_read2(void *addr) { +u16 __tsan_unaligned_read2(const uu16 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); - return *(u16*)addr; + return *addr; } -u32 __tsan_unaligned_read4(void *addr) { +u32 __tsan_unaligned_read4(const uu32 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); - return *(u32*)addr; + return *addr; } -u64 __tsan_unaligned_read8(void *addr) { +u64 __tsan_unaligned_read8(const uu64 *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); - return *(u64*)addr; + return *addr; } -void __tsan_unaligned_write2(void *addr, u16 v) { +void __tsan_unaligned_write2(uu16 *addr, u16 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); - *(u16*)addr = v; + *addr = v; } -void __tsan_unaligned_write4(void *addr, u32 v) { +void __tsan_unaligned_write4(uu32 *addr, u32 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); - *(u32*)addr = v; + *addr = v; } -void __tsan_unaligned_write8(void *addr, u64 v) { +void __tsan_unaligned_write8(uu64 *addr, u64 v) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); - *(u64*)addr = v; + *addr = v; } extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE uint16_t __sanitizer_unaligned_load16(void *addr) - ALIAS("__tsan_unaligned_read2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read2"); +SANITIZER_INTERFACE_ATTRIBUTE uint32_t __sanitizer_unaligned_load32(void *addr) - ALIAS("__tsan_unaligned_read4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read4"); +SANITIZER_INTERFACE_ATTRIBUTE uint64_t __sanitizer_unaligned_load64(void *addr) - ALIAS("__tsan_unaligned_read8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_read8"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store16(void *addr, uint16_t v) - ALIAS("__tsan_unaligned_write2") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write2"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store32(void *addr, uint32_t v) - ALIAS("__tsan_unaligned_write4") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write4"); +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store64(void *addr, uint64_t v) - ALIAS("__tsan_unaligned_write8") SANITIZER_INTERFACE_ATTRIBUTE; + ALIAS("__tsan_unaligned_write8"); } void __tsan_acquire(void *addr) { diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h index 457fb55e0d2d..70450697d480 100644 --- a/lib/tsan/rtl/tsan_interface.h +++ b/lib/tsan/rtl/tsan_interface.h @@ -27,38 +27,38 @@ extern "C" { // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. -void __tsan_init() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); -void __tsan_read1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); -void __tsan_write1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); -u16 __tsan_unaligned_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u32 __tsan_unaligned_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -u64 __tsan_unaligned_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write2(void *addr, u16 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write4(void *addr, u32 v) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_unaligned_write8(void *addr, u64 v) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); -void __tsan_vptr_read(void **vptr_p) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_vptr_update(void **vptr_p, void *new_val) - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_vptr_update(void **vptr_p, void *new_val); -void __tsan_func_entry(void *call_pc) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_func_exit() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); -void __tsan_read_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range(void *addr, unsigned long size); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range(void *addr, unsigned long size); // NOLINT #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc index 04b4b455d15e..cacbc0281d0f 100644 --- a/lib/tsan/rtl/tsan_interface_ann.cc +++ b/lib/tsan/rtl/tsan_interface_ann.cc @@ -13,6 +13,7 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_ann.h" #include "tsan_mutex.h" #include "tsan_report.h" @@ -229,12 +230,12 @@ using namespace __tsan; // NOLINT extern "C" { void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensBefore); - Release(cur_thread(), CALLERPC, addr); + Release(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensAfter); - Acquire(cur_thread(), CALLERPC, addr); + Acquire(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) { @@ -382,22 +383,32 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace( void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); - IgnoreCtl(cur_thread(), false, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); - IgnoreCtl(cur_thread(), false, false); + ThreadIgnoreEnd(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); - IgnoreCtl(cur_thread(), true, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); - IgnoreCtl(thr, true, false); + ThreadIgnoreEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); + ThreadIgnoreSyncBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd); + ThreadIgnoreSyncEnd(thr); } void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( @@ -447,4 +458,7 @@ const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) { else return "0"; } + +void INTERFACE_ATTRIBUTE +AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_ann.h b/lib/tsan/rtl/tsan_interface_ann.h index 8e45328e7ec1..963bcc55afaa 100644 --- a/lib/tsan/rtl/tsan_interface_ann.h +++ b/lib/tsan/rtl/tsan_interface_ann.h @@ -23,8 +23,8 @@ extern "C" { #endif -void __tsan_acquire(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_release(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_acquire(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_release(void *addr); #ifdef __cplusplus } // extern "C" diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc index 80266969849a..d9f8cdf5b106 100644 --- a/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/lib/tsan/rtl/tsan_interface_atomic.cc @@ -30,23 +30,37 @@ using namespace __tsan; // NOLINT #define SCOPED_ATOMIC(func, ...) \ const uptr callpc = (uptr)__builtin_return_address(0); \ uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ - pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ mo = ConvertOrder(mo); \ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ ThreadState *const thr = cur_thread(); \ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ - ScopedAtomic sa(thr, callpc, __FUNCTION__); \ + ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \ return Atomic##func(thr, pc, __VA_ARGS__); \ /**/ +// Some shortcuts. +typedef __tsan_memory_order morder; +typedef __tsan_atomic8 a8; +typedef __tsan_atomic16 a16; +typedef __tsan_atomic32 a32; +typedef __tsan_atomic64 a64; +typedef __tsan_atomic128 a128; +const morder mo_relaxed = __tsan_memory_order_relaxed; +const morder mo_consume = __tsan_memory_order_consume; +const morder mo_acquire = __tsan_memory_order_acquire; +const morder mo_release = __tsan_memory_order_release; +const morder mo_acq_rel = __tsan_memory_order_acq_rel; +const morder mo_seq_cst = __tsan_memory_order_seq_cst; + class ScopedAtomic { public: - ScopedAtomic(ThreadState *thr, uptr pc, const char *func) + ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a, + morder mo, const char *func) : thr_(thr) { CHECK_EQ(thr_->in_rtl, 0); ProcessPendingSignals(thr); FuncEntry(thr_, pc); - DPrintf("#%d: %s\n", thr_->tid, func); + DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo); thr_->in_rtl++; } ~ScopedAtomic() { @@ -58,20 +72,6 @@ class ScopedAtomic { ThreadState *thr_; }; -// Some shortcuts. -typedef __tsan_memory_order morder; -typedef __tsan_atomic8 a8; -typedef __tsan_atomic16 a16; -typedef __tsan_atomic32 a32; -typedef __tsan_atomic64 a64; -typedef __tsan_atomic128 a128; -const morder mo_relaxed = __tsan_memory_order_relaxed; -const morder mo_consume = __tsan_memory_order_consume; -const morder mo_acquire = __tsan_memory_order_acquire; -const morder mo_release = __tsan_memory_order_release; -const morder mo_acq_rel = __tsan_memory_order_acq_rel; -const morder mo_seq_cst = __tsan_memory_order_seq_cst; - static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) { StatInc(thr, StatAtomic); StatInc(thr, t); @@ -251,11 +251,10 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, // Assume the access is atomic. if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); - return *a; + return *a; // as if atomic } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); @@ -273,13 +272,15 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, // Strictly saying even relaxed store cuts off release sequence, // so must reset the clock. if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) { - *a = v; + *a = v; // as if atomic return; } __sync_synchronize(); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); *a = v; s->mtx.Unlock(); // Trainling memory barrier to provide sequential consistency @@ -293,13 +294,15 @@ static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } v = F(a, v); if (s) @@ -357,13 +360,15 @@ static bool AtomicCAS(ThreadState *thr, uptr pc, SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } T cc = *c; T pr = func_cas(a, cc, v); @@ -659,14 +664,14 @@ a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, } #if __TSAN_HAS_INT128 -a128 __tsan_atomic64_compare_exchange_val(volatile a128 *a, a128 c, a128 v, +a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v, morder mo, morder fmo) { SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); } #endif void __tsan_atomic_thread_fence(morder mo) { - char* a; + char* a = 0; SCOPED_ATOMIC(Fence, mo); } diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc index 71e0747c3646..53f14cf07b59 100644 --- a/lib/tsan/rtl/tsan_interface_java.cc +++ b/lib/tsan/rtl/tsan_interface_java.cc @@ -17,6 +17,8 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_procmaps.h" using namespace __tsan; // NOLINT @@ -157,7 +159,7 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ - const uptr pc = (uptr)&func; \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc index b6671b1abf09..8547f714be13 100644 --- a/lib/tsan/rtl/tsan_mman.cc +++ b/lib/tsan/rtl/tsan_mman.cc @@ -75,10 +75,12 @@ void InitializeAllocator() { void AllocatorThreadStart(ThreadState *thr) { allocator()->InitCache(&thr->alloc_cache); + internal_allocator()->InitCache(&thr->internal_alloc_cache); } void AllocatorThreadFinish(ThreadState *thr) { allocator()->DestroyCache(&thr->alloc_cache); + internal_allocator()->DestroyCache(&thr->internal_alloc_cache); } void AllocatorPrintStats() { @@ -102,14 +104,18 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { CHECK_GT(thr->in_rtl, 0); if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return 0; + return AllocatorReturnNull(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; MBlock *b = new(allocator()->GetMetaData(p)) MBlock; b->Init(sz, thr->tid, CurrentStackId(thr, pc)); - if (CTX() && CTX()->initialized) - MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + if (CTX() && CTX()->initialized) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + else + MemoryResetRange(thr, pc, (uptr)p, sz); + } DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); SignalUnsafeCall(thr, pc); return p; @@ -132,8 +138,10 @@ void user_free(ThreadState *thr, uptr pc, void *p) { } b->ListReset(); } - if (CTX() && CTX()->initialized && thr->in_rtl == 1) - MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + if (CTX() && CTX()->initialized && thr->in_rtl == 1) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + } allocator()->Deallocate(&thr->alloc_cache, p); SignalUnsafeCall(thr, pc); } @@ -194,11 +202,12 @@ void invoke_free_hook(void *ptr) { void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); CHECK_GT(thr->in_rtl, 0); + CHECK_LE(sz, InternalSizeClassMap::kMaxSize); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - return InternalAlloc(sz); + return InternalAlloc(sz, &thr->internal_alloc_cache); } void internal_free(void *p) { @@ -208,7 +217,7 @@ void internal_free(void *p) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - InternalFree(p); + InternalFree(p, &thr->internal_alloc_cache); } } // namespace __tsan @@ -261,5 +270,6 @@ uptr __tsan_get_allocated_size(void *p) { void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->alloc_cache); + internal_allocator()->SwallowCache(&thr->internal_alloc_cache); } } // extern "C" diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h index 666b4d0c482f..32e22ba60434 100644 --- a/lib/tsan/rtl/tsan_platform.h +++ b/lib/tsan/rtl/tsan_platform.h @@ -134,17 +134,24 @@ static inline uptr AlternativeAddress(uptr addr) { void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size); +uptr GetRSS(); const char *InitializePlatform(); void FinalizePlatform(); + +// The additional page is to catch shadow stack overflow as paging fault. +const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096 + + 4095) & ~4095; + uptr ALWAYS_INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize; DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize + + kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index a0d71e8589d6..906b5dc73be4 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -19,6 +19,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -45,6 +46,14 @@ #include <resolv.h> #include <malloc.h> +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + extern "C" struct mallinfo __libc_mallinfo(); namespace __tsan { @@ -72,70 +81,33 @@ ScopedInRtl::~ScopedInRtl() { } #endif -static bool ishex(char c) { - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f'); -} - -static uptr readhex(const char *p) { - uptr v = 0; - for (; ishex(p[0]); p++) { - if (p[0] >= '0' && p[0] <= '9') - v = v * 16 + p[0] - '0'; - else - v = v * 16 + p[0] - 'a' + 10; - } - return v; -} - -static uptr readdec(const char *p) { - uptr v = 0; - for (; p[0] >= '0' && p[0] <= '9' ; p++) - v = v * 10 + p[0] - '0'; - return v; +void FillProfileCallback(uptr start, uptr rss, bool file, + uptr *mem, uptr stats_size) { + CHECK_EQ(7, stats_size); + mem[6] += rss; // total + start >>= 40; + if (start < 0x10) // shadow + mem[0] += rss; + else if (start >= 0x20 && start < 0x30) // compat modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x7e) // modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x60 && start < 0x62) // traces + mem[3] += rss; + else if (start >= 0x7d && start < 0x7e) // heap + mem[4] += rss; + else // other + mem[5] += rss; } void WriteMemoryProfile(char *buf, uptr buf_size) { - char *smaps = 0; - uptr smaps_cap = 0; - uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", - &smaps, &smaps_cap, 64<<20); - uptr mem[6] = {}; - uptr total = 0; - uptr start = 0; - bool file = false; - const char *pos = smaps; - while (pos < smaps + smaps_len) { - if (ishex(pos[0])) { - start = readhex(pos); - for (; *pos != '/' && *pos > '\n'; pos++) {} - file = *pos == '/'; - } else if (internal_strncmp(pos, "Rss:", 4) == 0) { - for (; *pos < '0' || *pos > '9'; pos++) {} - uptr rss = readdec(pos) * 1024; - total += rss; - start >>= 40; - if (start < 0x10) // shadow - mem[0] += rss; - else if (start >= 0x20 && start < 0x30) // compat modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x7e) // modules - mem[file ? 1 : 2] += rss; - else if (start >= 0x60 && start < 0x62) // traces - mem[3] += rss; - else if (start >= 0x7d && start < 0x7e) // heap - mem[4] += rss; - else // other - mem[5] += rss; - } - while (*pos++ != '\n') {} - } - UnmapOrDie(smaps, smaps_cap); + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); char *buf_pos = buf; char *buf_end = buf + buf_size; buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n", - total >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, + mem[6] >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, mem[3] >> 20, mem[4] >> 20, mem[5] >> 20); struct mallinfo mi = __libc_mallinfo(); buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, @@ -143,10 +115,23 @@ void WriteMemoryProfile(char *buf, uptr buf_size) { mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20); } -void FlushShadowMemory() { +uptr GetRSS() { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + return mem[6]; +} + + +void FlushShadowMemoryCallback( + const SuspendedThreadsList &suspended_threads_list, + void *argument) { FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); } +void FlushShadowMemory() { + StopTheWorld(FlushShadowMemoryCallback, 0); +} + #ifndef TSAN_GO static void ProtectRange(uptr beg, uptr end) { ScopedInRtl in_rtl; diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index c95c5c86be69..66307ae09dcd 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -13,9 +13,27 @@ #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_report_decorator.h" namespace __tsan { +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *ThreadDescription() { return Cyan(); } + const char *EndThreadDescription() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Sleep() { return Yellow(); } + const char *EndSleep() { return Default(); } + const char *Mutex() { return Magenta(); } + const char *EndMutex() { return Default(); } +}; + ReportDesc::ReportDesc() : stacks(MBlockReportStack) , mops(MBlockReportMop) @@ -97,26 +115,32 @@ static const char *MopDesc(bool first, bool write, bool atomic) { } static void PrintMop(const ReportMop *mop, bool first) { + Decorator d; char thrbuf[kThreadBufSize]; + Printf("%s", d.Access()); Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); Printf(":\n"); + Printf("%s", d.EndAccess()); PrintStack(mop->stack); } static void PrintLocation(const ReportLocation *loc) { + Decorator d; char thrbuf[kThreadBufSize]; + bool print_stack = false; + Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { - Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->module, loc->offset); } else if (loc->type == ReportLocationHeap) { char thrbuf[kThreadBufSize]; Printf(" Location is heap block of size %zu at %p allocated by %s:\n", loc->size, loc->addr, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } else if (loc->type == ReportLocationStack) { Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); } else if (loc->type == ReportLocationTLS) { @@ -124,22 +148,32 @@ static void PrintLocation(const ReportLocation *loc) { } else if (loc->type == ReportLocationFD) { Printf(" Location is file descriptor %d created by %s at:\n", loc->fd, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } + Printf("%s", d.EndLocation()); + if (print_stack) + PrintStack(loc->stack); } static void PrintMutex(const ReportMutex *rm) { + Decorator d; if (rm->destroyed) { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); + Printf("%s", d.EndMutex()); } else { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu created at:\n", rm->id); + Printf("%s", d.EndMutex()); PrintStack(rm->stack); } } static void PrintThread(const ReportThread *rt) { + Decorator d; if (rt->id == 0) // Little sense in describing the main thread. return; + Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); @@ -150,11 +184,15 @@ static void PrintThread(const ReportThread *rt) { if (rt->stack) Printf(" at:"); Printf("\n"); + Printf("%s", d.EndThreadDescription()); PrintStack(rt->stack); } static void PrintSleep(const ReportStack *s) { + Decorator d; + Printf("%s", d.Sleep()); Printf(" As if synchronized via sleep:\n"); + Printf("%s", d.EndSleep()); PrintStack(s); } @@ -177,10 +215,13 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent) { } void PrintReport(const ReportDesc *rep) { + Decorator d; Printf("==================\n"); const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("%s", d.Warning()); Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, (int)internal_getpid()); + Printf("%s", d.EndWarning()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -212,31 +253,37 @@ void PrintReport(const ReportDesc *rep) { Printf("==================\n"); } -#else +#else // #ifndef TSAN_GO + +const int kMainThreadId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0) { - Printf(" [failed to restore the stack]\n\n"); + Printf(" [failed to restore the stack]\n"); return; } for (int i = 0; ent; ent = ent->next, i++) { Printf(" %s()\n %s:%d +0x%zx\n", ent->func, ent->file, ent->line, (void*)ent->offset); } - Printf("\n"); } static void PrintMop(const ReportMop *mop, bool first) { - Printf("%s by goroutine %d:\n", + Printf("\n"); + Printf("%s by ", (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), - mop->tid); + : (mop->write ? "Previous write" : "Previous read"))); + if (mop->tid == kMainThreadId) + Printf("main goroutine:\n"); + else + Printf("goroutine %d:\n", mop->tid); PrintStack(mop->stack); } static void PrintThread(const ReportThread *rt) { - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainThreadId) return; + Printf("\n"); Printf("Goroutine %d (%s) created at:\n", rt->id, rt->running ? "running" : "finished"); PrintStack(rt->stack); @@ -244,7 +291,7 @@ static void PrintThread(const ReportThread *rt) { void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - Printf("WARNING: DATA RACE\n"); + Printf("WARNING: DATA RACE"); for (uptr i = 0; i < rep->mops.Size(); i++) PrintMop(rep->mops[i], i == 0); for (uptr i = 0; i < rep->threads.Size(); i++) diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index 5924858c84c5..1f66d29a741c 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -39,9 +39,13 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); static char ctx_placeholder[sizeof(Context)] ALIGNED(64); // Can be overriden by a front-end. -bool CPP_WEAK OnFinalize(bool failed) { +#ifdef TSAN_EXTERNAL_HOOKS +bool OnFinalize(bool failed); +#else +bool WEAK OnFinalize(bool failed) { return failed; } +#endif static Context *ctx; Context *CTX() { @@ -74,7 +78,7 @@ Context::Context() CreateThreadContext, kMaxTid, kThreadQuarantineSize)) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) - , fired_suppressions(MBlockRacyAddresses) { + , fired_suppressions(8) { } // The objects are allocated in TLS, so one may rely on zero-initialization. @@ -86,7 +90,6 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, // they may be accessed before the ctor. // , ignore_reads_and_writes() // , in_rtl() - , shadow_stack_pos(&shadow_stack[0]) #ifndef TSAN_GO , jmp_bufs(MBlockJmpBuf) #endif @@ -130,17 +133,38 @@ static void BackgroundThread(void *arg) { } u64 last_flush = NanoTime(); + uptr last_rss = 0; for (int i = 0; ; i++) { SleepForSeconds(1); u64 now = NanoTime(); // Flush memory if requested. - if (flags()->flush_memory_ms) { + if (flags()->flush_memory_ms > 0) { if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: periodic memory flush\n"); FlushShadowMemory(); last_flush = NanoTime(); } } + if (flags()->memory_limit_mb > 0) { + uptr rss = GetRSS(); + uptr limit = uptr(flags()->memory_limit_mb) << 20; + if (flags()->verbosity > 0) { + Printf("ThreadSanitizer: memory flush check" + " RSS=%llu LAST=%llu LIMIT=%llu\n", + (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20); + } + if (2 * rss > limit + last_rss) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: flushing memory due to RSS\n"); + FlushShadowMemory(); + rss = GetRSS(); + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); + } + last_rss = rss; + } // Write memory profile if requested. if (mprof_fd != kInvalidFd) @@ -176,8 +200,10 @@ void MapThreadTrace(uptr addr, uptr size) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); CHECK_GE(addr, kTraceMemBegin); CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize); - if (addr != (uptr)MmapFixedNoReserve(addr, size)) { - Printf("FATAL: ThreadSanitizer can not mmap thread trace\n"); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + if (addr1 != addr) { + Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", + addr, size, addr1); Die(); } } @@ -206,23 +232,21 @@ void Initialize(ThreadState *thr) { #endif InitializeFlags(&ctx->flags, env); // Setup correct file descriptor for error reports. - if (internal_strcmp(flags()->log_path, "stdout") == 0) - __sanitizer_set_report_fd(kStdoutFd); - else if (internal_strcmp(flags()->log_path, "stderr") == 0) - __sanitizer_set_report_fd(kStderrFd); - else - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(flags()->log_path); InitializeSuppressions(); #ifndef TSAN_GO + InitializeLibIgnore(); // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; - if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { - if (!InitializeExternalSymbolizer(external_symbolizer)) { - Printf("Failed to start external symbolizer: '%s'\n", - external_symbolizer); - Die(); - } + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer != 0 && external_symbolizer[0] != '\0' && + !external_symbolizer_started) { + Printf("Failed to start external symbolizer: '%s'\n", + external_symbolizer); + Die(); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif internal_start_thread(&BackgroundThread, 0); @@ -556,7 +580,9 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, // Don't want to touch lots of shadow memory. // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); - if (size < 64*1024) { + // UnmapOrDie/MmapFixedNoReserve does not work on Windows, + // so we do it only for C/C++. + if (kGoMode || size < 64*1024) { u64 *p = (u64*)MemToShadow(addr); CHECK(IsShadowMem((uptr)p)); CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); @@ -636,9 +662,9 @@ void FuncEntry(ThreadState *thr, uptr pc) { // Shadow stack maintenance can be replaced with // stack unwinding during trace switch (which presumably must be faster). - DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) { const int sz = thr->shadow_stack_end - thr->shadow_stack; @@ -664,23 +690,40 @@ void FuncExit(ThreadState *thr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); - DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; } -void IgnoreCtl(ThreadState *thr, bool write, bool begin) { - DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin); - thr->ignore_reads_and_writes += begin ? 1 : -1; +void ThreadIgnoreBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); + thr->ignore_reads_and_writes++; + CHECK_GT(thr->ignore_reads_and_writes, 0); + thr->fast_state.SetIgnoreBit(); +} + +void ThreadIgnoreEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + thr->ignore_reads_and_writes--; CHECK_GE(thr->ignore_reads_and_writes, 0); - if (thr->ignore_reads_and_writes) - thr->fast_state.SetIgnoreBit(); - else + if (thr->ignore_reads_and_writes == 0) thr->fast_state.ClearIgnoreBit(); } +void ThreadIgnoreSyncBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); + thr->ignore_sync++; + CHECK_GT(thr->ignore_sync, 0); +} + +void ThreadIgnoreSyncEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + thr->ignore_sync--; + CHECK_GE(thr->ignore_sync, 0); +} + bool MD5Hash::operator==(const MD5Hash &other) const { return hash[0] == other.hash[0] && hash[1] == other.hash[1]; } diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index f1a73e457331..4ee667543a6e 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -27,7 +27,10 @@ #define TSAN_RTL_H #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libignore.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" @@ -409,21 +412,19 @@ struct ThreadState { // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; + int ignore_sync; + // C/C++ uses fixed size shadow stack embed into Trace. + // Go uses malloc-allocated shadow stack with dynamic size. + uptr *shadow_stack; + uptr *shadow_stack_end; uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; -#ifndef TSAN_GO - // C/C++ uses embed shadow stack of fixed size. - uptr shadow_stack[kShadowStackSize]; -#else - // Go uses satellite shadow stack with dynamic size. - uptr *shadow_stack; - uptr *shadow_stack_end; -#endif MutexSet mset; ThreadClock clock; #ifndef TSAN_GO AllocatorCache alloc_cache; + InternalAllocatorCache internal_alloc_cache; Vector<JmpBuf> jmp_bufs; #endif u64 stat[StatCnt]; @@ -431,6 +432,7 @@ struct ThreadState { const int unique_id; int in_rtl; bool in_symbolizer; + bool in_ignored_lib; bool is_alive; bool is_freeing; bool is_vptr_access; @@ -531,7 +533,8 @@ struct Context { Vector<RacyStacks> racy_stacks; Vector<RacyAddress> racy_addresses; - Vector<FiredSuppression> fired_suppressions; + // Number of fired suppressions may be large enough. + InternalMmapVector<FiredSuppression> fired_suppressions; Flags flags; @@ -594,13 +597,15 @@ void MapThreadTrace(uptr addr, uptr size); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); +void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1 = 0, - const ReportStack *suppress_stack2 = 0); + const ReportStack *suppress_stack2 = 0, + const ReportLocation *suppress_loc = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); @@ -672,7 +677,11 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void IgnoreCtl(ThreadState *thr, bool write, bool begin); + +void ThreadIgnoreBegin(ThreadState *thr); +void ThreadIgnoreEnd(ThreadState *thr); +void ThreadIgnoreSyncBegin(ThreadState *thr); +void ThreadIgnoreSyncEnd(ThreadState *thr); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); @@ -696,12 +705,17 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void Acquire(ThreadState *thr, uptr pc, uptr addr); void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller @@ -714,11 +728,11 @@ void AfterSleep(ThreadState *thr, uptr pc); // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset 1024;*/" \ + ".cfi_adjust_cfa_offset 1024;" \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset -1024;*/" \ + ".cfi_adjust_cfa_offset -1024;" \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index cf2e44dd09ee..409391678f01 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -95,16 +95,13 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { } else if (s->owner_tid == thr->tid) { CHECK_GT(s->recursion, 0); } else { - Printf("ThreadSanitizer WARNING: double lock\n"); + Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr); PrintCurrentStack(thr, pc); } if (s->recursion == 0) { StatInc(thr, StatMutexLock); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); - thr->clock.acquire(&s->read_clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); + AcquireImpl(thr, pc, &s->read_clock); } else if (!s->is_recursive) { StatInc(thr, StatMutexRecLock); } @@ -125,13 +122,14 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n"); + Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr); PrintCurrentStack(thr, pc); } } else if (s->owner_tid != thr->tid) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } } else { @@ -140,10 +138,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } @@ -163,13 +158,12 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n"); + Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); - StatInc(thr, StatSyncAcquire); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); s->mtx.ReadUnlock(); } @@ -184,14 +178,11 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read unlock of a write " - "locked mutex\n"); + Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); s->mtx.Unlock(); thr->mset.Del(s->GetId(), false); } @@ -209,10 +200,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { StatInc(thr, StatMutexReadUnlock); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); } else if (s->owner_tid == thr->tid) { // Seems to be write unlock. thr->fast_state.IncrementEpoch(); @@ -222,33 +210,37 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - // FIXME: Refactor me, plz. - // The sequence of events is quite tricky and doubled in several places. - // First, it's a bug to increment the epoch w/o writing to the trace. - // Then, the acquire/release logic can be factored out as well. - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } } else if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } thr->mset.Del(s->GetId(), write); s->mtx.Unlock(); } +void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->owner_tid = SyncVar::kInvalidTid; + s->recursion = 0; + s->mtx.Unlock(); +} + void Acquire(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Acquire %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); s->mtx.ReadUnlock(); } @@ -262,6 +254,9 @@ static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AcquireGlobal(ThreadState *thr, uptr pc) { + DPrintf("#%d: AcquireGlobal\n", thr->tid); + if (thr->ignore_sync) + return; ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( UpdateClockCallback, thr); @@ -270,20 +265,26 @@ void AcquireGlobal(ThreadState *thr, uptr pc) { void Release(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Release %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.release(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); s->mtx.Unlock(); } void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseStoreImpl(thr, pc, &s->clock); s->mtx.Unlock(); } @@ -298,6 +299,9 @@ static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AfterSleep(ThreadState *thr, uptr pc) { + DPrintf("#%d: AfterSleep %zx\n", thr->tid); + if (thr->ignore_sync) + return; thr->last_sleep_stack_id = CurrentStackId(thr, pc); ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( @@ -305,4 +309,40 @@ void AfterSleep(ThreadState *thr, uptr pc) { } #endif +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->clock.acquire(c); + StatInc(thr, StatSyncAcquire); +} + +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.release(c); + StatInc(thr, StatSyncRelease); +} + +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.ReleaseStore(c); + StatInc(thr, StatSyncRelease); +} + +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.acq_rel(c); + StatInc(thr, StatSyncAcquire); + StatInc(thr, StatSyncRelease); +} + } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index f77a7a2efa96..4fed43faf25f 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -95,8 +95,9 @@ static void StackStripMain(ReportStack *stack) { DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); } #else - if (last && 0 == internal_strcmp(last, "schedunlock")) - last_frame2->next = 0; + // The last frame always point into runtime (gosched0, goexit0, runtime.main). + last_frame2->next = 0; + (void)last; #endif } @@ -105,17 +106,25 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) { return 0; ReportStack *stack = 0; for (uptr si = 0; si < trace.Size(); si++) { + const uptr pc = trace.Get(si); +#ifndef TSAN_GO // We obtain the return address, that is, address of the next instruction, // so offset it by 1 byte. - bool is_last = (si == trace.Size() - 1); - ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last); + const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); +#else + // FIXME(dvyukov): Go sometimes uses address of a function as top pc. + uptr pc1 = pc; + if (si != trace.Size() - 1) + pc1 -= 1; +#endif + ReportStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); ReportStack *last = ent; while (last->next) { - last->pc += !is_last; + last->pc = pc; // restore original pc for report last = last->next; } - last->pc += !is_last; + last->pc = pc; // restore original pc for report last->next = stack; stack = ent; } @@ -401,7 +410,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) { const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); - InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024 + InternalScopedBuffer<uptr> stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.Size(); i++) { stack[i] = hdr->stack0.Get(i); DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); @@ -501,28 +510,33 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1, - const ReportStack *suppress_stack2) { + const ReportStack *suppress_stack2, + const ReportLocation *suppress_loc) { atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); const ReportDesc *rep = srep.GetReport(); Suppression *supp = 0; uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp); if (suppress_pc == 0) suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp); if (suppress_pc != 0) { FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; - ctx->fired_suppressions.PushBack(s); + ctx->fired_suppressions.push_back(s); } if (OnReport(rep, suppress_pc != 0)) return false; PrintReport(rep); - CTX()->nreported++; + ctx->nreported++; + if (flags()->halt_on_error) + internal__exit(flags()->exitcode); return true; } bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace) { - for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; for (uptr j = 0; j < trace.Size(); j++) { @@ -537,6 +551,22 @@ bool IsFiredSuppression(Context *ctx, return false; } +static bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + uptr addr) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (addr == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; + } + } + return false; +} + bool FrameIsInternal(const ReportStack *frame) { return frame != 0 && frame->file != 0 && (internal_strstr(frame->file, "tsan_interceptors.cc") || @@ -569,7 +599,7 @@ static bool IsJavaNonsense(const ReportDesc *rep) { && frame->module == 0)) { if (frame) { FiredSuppression supp = {rep->typ, frame->pc, 0}; - CTX()->fired_suppressions.PushBack(supp); + CTX()->fired_suppressions.push_back(supp); } return true; } @@ -630,6 +660,8 @@ void ReportRace(ThreadState *thr) { else if (freed) typ = ReportTypeUseAfterFree; ScopedReport rep(typ); + if (IsFiredSuppression(ctx, rep, addr)) + return; const uptr kMop = 2; StackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); @@ -640,6 +672,8 @@ void ReportRace(ThreadState *thr) { new(mset2.data()) MutexSet(); Shadow s2(thr->racy_state[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); + if (IsFiredSuppression(ctx, rep, traces[1])) + return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; @@ -672,8 +706,11 @@ void ReportRace(ThreadState *thr) { } #endif + ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ? + rep.GetReport()->locs[0] : 0; if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, - rep.GetReport()->mops[1]->stack)) + rep.GetReport()->mops[1]->stack, + suppress_loc)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -689,8 +726,8 @@ void PrintCurrentStackSlow() { #ifndef TSAN_GO __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; - ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), - kStackTraceMax); + ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), + 0, 0, 0, false); for (uptr i = 0; i < ptrace->size / 2; i++) { uptr tmp = ptrace->trace[i]; ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1]; diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index ee13fa18db3f..4e451b042947 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -41,8 +41,7 @@ void ThreadContext::OnDead() { void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast<ThreadState *>(arg); - caller_thr->clock.acquire(&sync); - StatInc(caller_thr, StatSyncAcquire); + AcquireImpl(caller_thr, 0, &sync); sync.Reset(); } @@ -59,10 +58,7 @@ void ThreadContext::OnCreated(void *arg) { args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); - args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch()); - args->thr->fast_synch_epoch = args->thr->fast_state.epoch(); - args->thr->clock.release(&sync); - StatInc(args->thr, StatSyncRelease); + ReleaseImpl(args->thr, 0, &sync); #ifdef TSAN_GO creation_stack.ObtainCurrent(args->thr, args->pc); #else @@ -95,21 +91,23 @@ void ThreadContext::OnStarted(void *arg) { epoch1 = (u64)-1; new(thr) ThreadState(CTX(), tid, unique_id, epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); -#ifdef TSAN_GO +#ifndef TSAN_GO + thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; +#else // Setup dynamic shadow stack. const int kInitStackSize = 8; - args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); - args->thr->shadow_stack_pos = thr->shadow_stack; - args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif #ifndef TSAN_GO - AllocatorThreadStart(args->thr); + AllocatorThreadStart(thr); #endif - thr = args->thr; thr->fast_synch_epoch = epoch0; - thr->clock.set(tid, epoch0); - thr->clock.acquire(&sync); + AcquireImpl(thr, 0, &sync); thr->fast_state.SetHistorySize(flags()->history_size); const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); Trace *thr_trace = ThreadTrace(thr->tid); @@ -128,10 +126,7 @@ void ThreadContext::OnFinished() { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&sync); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); @@ -170,6 +165,10 @@ static void ThreadCheckIgnore(ThreadState *thr) { Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", thr->tid); } + if (thr->ignore_sync) { + Printf("ThreadSanitizer: thread T%d finished with sync ignores enabled.\n", + thr->tid); + } } void ThreadFinalize(ThreadState *thr) { @@ -374,25 +373,4 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, } } -void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, - uptr size, uptr step, bool is_write) { - if (size == 0) - return; - FastState fast_state = thr->fast_state; - if (fast_state.GetIgnoreBit()) - return; - StatInc(thr, StatMopRange); - fast_state.IncrementEpoch(); - thr->fast_state = fast_state; - TraceAddEvent(thr, fast_state, EventTypeMop, pc); - - for (uptr addr_end = addr + size; addr < addr_end; addr += step) { - u64 *shadow_mem = (u64*)MemToShadow(addr); - Shadow cur(fast_state); - cur.SetWrite(is_write); - cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kSizeLog1); - MemoryAccessImpl(thr, addr, kSizeLog1, is_write, false, - shadow_mem, cur); - } -} } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index 9676e0872e08..f9dbf121cef4 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -138,9 +138,11 @@ void StatOutput(u64 *stat) { name[StatInt_strcpy] = " strcpy "; name[StatInt_strncpy] = " strncpy "; name[StatInt_strstr] = " strstr "; + name[StatInt_strdup] = " strdup "; name[StatInt_strcasecmp] = " strcasecmp "; name[StatInt_strncasecmp] = " strncasecmp "; name[StatInt_atexit] = " atexit "; + name[StatInt__exit] = " _exit "; name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire "; name[StatInt___cxa_guard_release] = " __cxa_guard_release "; name[StatInt___cxa_guard_abort] = " __cxa_guard_abort "; @@ -180,6 +182,7 @@ void StatOutput(u64 *stat) { name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait "; name[StatInt_pthread_once] = " pthread_once "; name[StatInt_pthread_getschedparam] = " pthread_getschedparam "; + name[StatInt_pthread_setname_np] = " pthread_setname_np "; name[StatInt_sem_init] = " sem_init "; name[StatInt_sem_destroy] = " sem_destroy "; name[StatInt_sem_wait] = " sem_wait "; @@ -229,11 +232,13 @@ void StatOutput(u64 *stat) { name[StatInt_pread] = " pread "; name[StatInt_pread64] = " pread64 "; name[StatInt_readv] = " readv "; + name[StatInt_preadv] = " preadv "; name[StatInt_preadv64] = " preadv64 "; name[StatInt_write] = " write "; name[StatInt_pwrite] = " pwrite "; name[StatInt_pwrite64] = " pwrite64 "; name[StatInt_writev] = " writev "; + name[StatInt_pwritev] = " pwritev "; name[StatInt_pwritev64] = " pwritev64 "; name[StatInt_send] = " send "; name[StatInt_sendmsg] = " sendmsg "; @@ -253,8 +258,10 @@ void StatOutput(u64 *stat) { name[StatInt_epoll_ctl] = " epoll_ctl "; name[StatInt_epoll_wait] = " epoll_wait "; name[StatInt_poll] = " poll "; + name[StatInt_ppoll] = " ppoll "; name[StatInt_sigaction] = " sigaction "; name[StatInt_signal] = " signal "; + name[StatInt_sigsuspend] = " sigsuspend "; name[StatInt_raise] = " raise "; name[StatInt_kill] = " kill "; name[StatInt_pthread_kill] = " pthread_kill "; @@ -285,6 +292,7 @@ void StatOutput(u64 *stat) { name[StatInt_ctime_r] = " ctime_r "; name[StatInt_asctime] = " asctime "; name[StatInt_asctime_r] = " asctime_r "; + name[StatInt_strptime] = " strptime "; name[StatInt_frexp] = " frexp "; name[StatInt_frexpf] = " frexpf "; name[StatInt_frexpl] = " frexpl "; @@ -311,7 +319,9 @@ void StatOutput(u64 *stat) { name[StatInt_wait4] = " wait4 "; name[StatInt_inet_ntop] = " inet_ntop "; name[StatInt_inet_pton] = " inet_pton "; + name[StatInt_inet_aton] = " inet_aton "; name[StatInt_getaddrinfo] = " getaddrinfo "; + name[StatInt_getnameinfo] = " getnameinfo "; name[StatInt_getsockname] = " getsockname "; name[StatInt_gethostent] = " gethostent "; name[StatInt_gethostbyname] = " gethostbyname "; @@ -322,6 +332,99 @@ void StatOutput(u64 *stat) { name[StatInt_gethostbyname2_r] = " gethostbyname2_r "; name[StatInt_gethostbyaddr_r] = " gethostbyaddr_r "; name[StatInt_getsockopt] = " getsockopt "; + name[StatInt_modf] = " modf "; + name[StatInt_modff] = " modff "; + name[StatInt_modfl] = " modfl "; + name[StatInt_getpeername] = " getpeername "; + name[StatInt_ioctl] = " ioctl "; + name[StatInt_sysinfo] = " sysinfo "; + name[StatInt_readdir] = " readdir "; + name[StatInt_readdir64] = " readdir64 "; + name[StatInt_readdir_r] = " readdir_r "; + name[StatInt_readdir64_r] = " readdir64_r "; + name[StatInt_ptrace] = " ptrace "; + name[StatInt_setlocale] = " setlocale "; + name[StatInt_getcwd] = " getcwd "; + name[StatInt_get_current_dir_name] = " get_current_dir_name "; + name[StatInt_strtoimax] = " strtoimax "; + name[StatInt_strtoumax] = " strtoumax "; + name[StatInt_mbstowcs] = " mbstowcs "; + name[StatInt_mbsrtowcs] = " mbsrtowcs "; + name[StatInt_mbsnrtowcs] = " mbsnrtowcs "; + name[StatInt_wcstombs] = " wcstombs "; + name[StatInt_wcsrtombs] = " wcsrtombs "; + name[StatInt_wcsnrtombs] = " wcsnrtombs "; + name[StatInt_tcgetattr] = " tcgetattr "; + name[StatInt_realpath] = " realpath "; + name[StatInt_canonicalize_file_name] = " canonicalize_file_name "; + name[StatInt_confstr] = " confstr "; + name[StatInt_sched_getaffinity] = " sched_getaffinity "; + name[StatInt_strerror] = " strerror "; + name[StatInt_strerror_r] = " strerror_r "; + name[StatInt_scandir] = " scandir "; + name[StatInt_scandir64] = " scandir64 "; + name[StatInt_getgroups] = " getgroups "; + name[StatInt_wordexp] = " wordexp "; + name[StatInt_sigwait] = " sigwait "; + name[StatInt_sigwaitinfo] = " sigwaitinfo "; + name[StatInt_sigtimedwait] = " sigtimedwait "; + name[StatInt_sigemptyset] = " sigemptyset "; + name[StatInt_sigfillset] = " sigfillset "; + name[StatInt_sigpending] = " sigpending "; + name[StatInt_sigprocmask] = " sigprocmask "; + name[StatInt_backtrace] = " backtrace "; + name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; + name[StatInt_dlclose] = " dlclose "; + name[StatInt_getmntent] = " getmntent "; + name[StatInt_getmntent_r] = " getmntent_r "; + name[StatInt_statfs] = " statfs "; + name[StatInt_statfs64] = " statfs64 "; + name[StatInt_fstatfs] = " fstatfs "; + name[StatInt_fstatfs64] = " fstatfs64 "; + name[StatInt_statvfs] = " statvfs "; + name[StatInt_statvfs64] = " statvfs64 "; + name[StatInt_fstatvfs] = " fstatvfs "; + name[StatInt_fstatvfs64] = " fstatvfs64 "; + name[StatInt_initgroups] = " initgroups "; + name[StatInt_ether_ntoa] = " ether_ntoa "; + name[StatInt_ether_aton] = " ether_aton "; + name[StatInt_ether_ntoa_r] = " ether_ntoa_r "; + name[StatInt_ether_aton_r] = " ether_aton_r "; + name[StatInt_ether_ntohost] = " ether_ntohost "; + name[StatInt_ether_hostton] = " ether_hostton "; + name[StatInt_ether_line] = " ether_line "; + name[StatInt_shmctl] = " shmctl "; + name[StatInt_random_r] = " random_r "; + name[StatInt_tmpnam] = " tmpnam "; + name[StatInt_tmpnam_r] = " tmpnam_r "; + name[StatInt_tempnam] = " tempnam "; + name[StatInt_sincos] = " sincos "; + name[StatInt_sincosf] = " sincosf "; + name[StatInt_sincosl] = " sincosl "; + name[StatInt_remquo] = " remquo "; + name[StatInt_remquof] = " remquof "; + name[StatInt_remquol] = " remquol "; + name[StatInt_lgamma] = " lgamma "; + name[StatInt_lgammaf] = " lgammaf "; + name[StatInt_lgammal] = " lgammal "; + name[StatInt_lgamma_r] = " lgamma_r "; + name[StatInt_lgammaf_r] = " lgammaf_r "; + name[StatInt_lgammal_r] = " lgammal_r "; + name[StatInt_drand48_r] = " drand48_r "; + name[StatInt_lrand48_r] = " lrand48_r "; + name[StatInt_getline] = " getline "; + name[StatInt_getdelim] = " getdelim "; + + name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT + name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT + name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT + name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT + name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT + name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT + name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT + name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT + name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -353,6 +456,8 @@ void StatOutput(u64 *stat) { name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd "; name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin "; name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd "; + name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin "; + name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd "; name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; name[StatAnnotateThreadName] = " ThreadName "; diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h index d5c8b4389394..a44edfcd65c6 100644 --- a/lib/tsan/rtl/tsan_stat.h +++ b/lib/tsan/rtl/tsan_stat.h @@ -137,7 +137,9 @@ enum StatType { StatInt_strcasecmp, StatInt_strncasecmp, StatInt_strstr, + StatInt_strdup, StatInt_atexit, + StatInt__exit, StatInt___cxa_guard_acquire, StatInt___cxa_guard_release, StatInt___cxa_guard_abort, @@ -175,6 +177,7 @@ enum StatType { StatInt_pthread_barrier_wait, StatInt_pthread_once, StatInt_pthread_getschedparam, + StatInt_pthread_setname_np, StatInt_sem_init, StatInt_sem_destroy, StatInt_sem_wait, @@ -224,11 +227,13 @@ enum StatType { StatInt_pread, StatInt_pread64, StatInt_readv, + StatInt_preadv, StatInt_preadv64, StatInt_write, StatInt_pwrite, StatInt_pwrite64, StatInt_writev, + StatInt_pwritev, StatInt_pwritev64, StatInt_send, StatInt_sendmsg, @@ -248,8 +253,10 @@ enum StatType { StatInt_epoll_ctl, StatInt_epoll_wait, StatInt_poll, + StatInt_ppoll, StatInt_sigaction, StatInt_signal, + StatInt_sigsuspend, StatInt_raise, StatInt_kill, StatInt_pthread_kill, @@ -280,6 +287,7 @@ enum StatType { StatInt_ctime_r, StatInt_asctime, StatInt_asctime_r, + StatInt_strptime, StatInt_frexp, StatInt_frexpf, StatInt_frexpl, @@ -306,7 +314,9 @@ enum StatType { StatInt_wait4, StatInt_inet_ntop, StatInt_inet_pton, + StatInt_inet_aton, StatInt_getaddrinfo, + StatInt_getnameinfo, StatInt_getsockname, StatInt_gethostent, StatInt_gethostbyname, @@ -317,6 +327,99 @@ enum StatType { StatInt_gethostbyname2_r, StatInt_gethostbyaddr_r, StatInt_getsockopt, + StatInt_modf, + StatInt_modff, + StatInt_modfl, + StatInt_getpeername, + StatInt_ioctl, + StatInt_sysinfo, + StatInt_readdir, + StatInt_readdir64, + StatInt_readdir_r, + StatInt_readdir64_r, + StatInt_ptrace, + StatInt_setlocale, + StatInt_getcwd, + StatInt_get_current_dir_name, + StatInt_strtoimax, + StatInt_strtoumax, + StatInt_mbstowcs, + StatInt_mbsrtowcs, + StatInt_mbsnrtowcs, + StatInt_wcstombs, + StatInt_wcsrtombs, + StatInt_wcsnrtombs, + StatInt_tcgetattr, + StatInt_realpath, + StatInt_canonicalize_file_name, + StatInt_confstr, + StatInt_sched_getaffinity, + StatInt_strerror, + StatInt_strerror_r, + StatInt_scandir, + StatInt_scandir64, + StatInt_getgroups, + StatInt_wordexp, + StatInt_sigwait, + StatInt_sigwaitinfo, + StatInt_sigtimedwait, + StatInt_sigemptyset, + StatInt_sigfillset, + StatInt_sigpending, + StatInt_sigprocmask, + StatInt_backtrace, + StatInt_backtrace_symbols, + StatInt_dlopen, + StatInt_dlclose, + StatInt_getmntent, + StatInt_getmntent_r, + StatInt_statfs, + StatInt_statfs64, + StatInt_fstatfs, + StatInt_fstatfs64, + StatInt_statvfs, + StatInt_statvfs64, + StatInt_fstatvfs, + StatInt_fstatvfs64, + StatInt_initgroups, + StatInt_ether_ntoa, + StatInt_ether_aton, + StatInt_ether_ntoa_r, + StatInt_ether_aton_r, + StatInt_ether_ntohost, + StatInt_ether_hostton, + StatInt_ether_line, + StatInt_shmctl, + StatInt_random_r, + StatInt_tmpnam, + StatInt_tmpnam_r, + StatInt_tempnam, + StatInt_sincos, + StatInt_sincosf, + StatInt_sincosl, + StatInt_remquo, + StatInt_remquof, + StatInt_remquol, + StatInt_lgamma, + StatInt_lgammaf, + StatInt_lgammal, + StatInt_lgamma_r, + StatInt_lgammaf_r, + StatInt_lgammal_r, + StatInt_drand48_r, + StatInt_lrand48_r, + StatInt_getline, + StatInt_getdelim, + + StatInt_pthread_attr_getdetachstate, + StatInt_pthread_attr_getguardsize, + StatInt_pthread_attr_getschedparam, + StatInt_pthread_attr_getschedpolicy, + StatInt_pthread_attr_getinheritsched, + StatInt_pthread_attr_getscope, + StatInt_pthread_attr_getstacksize, + StatInt_pthread_attr_getstack, + StatInt_pthread_attr_getaffinity_np, // Dynamic annotations. StatAnnotation, @@ -349,6 +452,8 @@ enum StatType { StatAnnotateIgnoreReadsEnd, StatAnnotateIgnoreWritesBegin, StatAnnotateIgnoreWritesEnd, + StatAnnotateIgnoreSyncBegin, + StatAnnotateIgnoreSyncEnd, StatAnnotatePublishMemoryRange, StatAnnotateUnpublishMemoryRange, StatAnnotateThreadName, diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc index 6c49355bed88..4b0ac04c48a6 100644 --- a/lib/tsan/rtl/tsan_suppressions.cc +++ b/lib/tsan/rtl/tsan_suppressions.cc @@ -13,12 +13,25 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include "tsan_mman.h" #include "tsan_platform.h" +// Suppressions for true/false positives in standard libraries. +static const char *const std_suppressions = +// Libstdc++ 4.4 has data races in std::string. +// See http://crbug.com/181502 for an example. +"race:^_M_rep$\n" +"race:^_M_is_leaked$\n" +// False positive when using std <thread>. +// Happens because we miss atomic synchronization in libstdc++. +// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. +"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; + // Can be overriden in frontend. #ifndef TSAN_GO extern "C" const char *WEAK __tsan_default_suppressions() { @@ -28,7 +41,7 @@ extern "C" const char *WEAK __tsan_default_suppressions() { namespace __tsan { -static Suppression *g_suppressions; +static SuppressionContext* g_ctx; static char *ReadFile(const char *filename) { if (filename == 0 || filename[0] == 0) @@ -62,143 +75,96 @@ static char *ReadFile(const char *filename) { return buf; } -bool SuppressionMatch(char *templ, const char *str) { - if (str == 0 || str[0] == 0) - return false; - char *tpos; - const char *spos; - while (templ && templ[0]) { - if (templ[0] == '*') { - templ++; - continue; - } - if (str[0] == 0) - return false; - tpos = (char*)internal_strchr(templ, '*'); - if (tpos != 0) - tpos[0] = 0; - spos = internal_strstr(str, templ); - str = spos + internal_strlen(templ); - templ = tpos; - if (tpos) - tpos[0] = '*'; - if (spos == 0) - return false; - } - return true; -} - -Suppression *SuppressionParse(Suppression *head, const char* supp) { - const char *line = supp; - while (line) { - while (line[0] == ' ' || line[0] == '\t') - line++; - const char *end = internal_strchr(line, '\n'); - if (end == 0) - end = line + internal_strlen(line); - if (line != end && line[0] != '#') { - const char *end2 = end; - while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) - end2--; - SuppressionType stype; - if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) { - stype = SuppressionRace; - line += sizeof("race:") - 1; - } else if (0 == internal_strncmp(line, "thread:", - sizeof("thread:") - 1)) { - stype = SuppressionThread; - line += sizeof("thread:") - 1; - } else if (0 == internal_strncmp(line, "mutex:", - sizeof("mutex:") - 1)) { - stype = SuppressionMutex; - line += sizeof("mutex:") - 1; - } else if (0 == internal_strncmp(line, "signal:", - sizeof("signal:") - 1)) { - stype = SuppressionSignal; - line += sizeof("signal:") - 1; - } else { - Printf("ThreadSanitizer: failed to parse suppressions file\n"); - Die(); - } - Suppression *s = (Suppression*)internal_alloc(MBlockSuppression, - sizeof(Suppression)); - s->next = head; - head = s; - s->type = stype; - s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); - internal_memcpy(s->templ, line, end2 - line); - s->templ[end2 - line] = 0; - s->hit_count = 0; - } - if (end[0] == 0) - break; - line = end + 1; - } - return head; -} - void InitializeSuppressions() { + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + g_ctx = new(placeholder_) SuppressionContext; const char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(0, supp); + g_ctx->Parse(supp); #ifndef TSAN_GO supp = __tsan_default_suppressions(); - g_suppressions = SuppressionParse(g_suppressions, supp); + g_ctx->Parse(supp); + g_ctx->Parse(std_suppressions); #endif } -uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { - if (g_suppressions == 0 || stack == 0) - return 0; - SuppressionType stype; +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + +SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) - stype = SuppressionRace; + return SuppressionRace; + else if (typ == ReportTypeVptrRace) + return SuppressionRace; + else if (typ == ReportTypeUseAfterFree) + return SuppressionRace; else if (typ == ReportTypeThreadLeak) - stype = SuppressionThread; + return SuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) - stype = SuppressionMutex; + return SuppressionMutex; else if (typ == ReportTypeSignalUnsafe) - stype = SuppressionSignal; - else + return SuppressionSignal; + else if (typ == ReportTypeErrnoInSignal) + return SuppressionNone; + Printf("ThreadSanitizer: unknown report type %d\n", typ), + Die(); +} + +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || stack == 0) return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) return 0; + Suppression *s; for (const ReportStack *frame = stack; frame; frame = frame->next) { - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (stype == supp->type && - (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file) || - SuppressionMatch(supp->templ, frame->module))) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); - supp->hit_count++; - *sp = supp; - return frame->pc; - } + if (g_ctx->Match(frame->func, stype, &s) || + g_ctx->Match(frame->file, stype, &s) || + g_ctx->Match(frame->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return frame->pc; } } return 0; } -static const char *SuppTypeStr(SuppressionType t) { - switch (t) { - case SuppressionRace: return "race"; - case SuppressionMutex: return "mutex"; - case SuppressionThread: return "thread"; - case SuppressionSignal: return "signal"; +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || loc == 0 || + loc->type != ReportLocationGlobal) + return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + return 0; + Suppression *s; + if (g_ctx->Match(loc->name, stype, &s) || + g_ctx->Match(loc->file, stype, &s) || + g_ctx->Match(loc->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return loc->addr; } - CHECK(0); - return "unknown"; + return 0; } void PrintMatchedSuppressions() { - int hit_count = 0; - for (Suppression *supp = g_suppressions; supp; supp = supp->next) - hit_count += supp->hit_count; - if (hit_count == 0) + CHECK(g_ctx); + InternalMmapVector<Suppression *> matched(1); + g_ctx->GetMatched(&matched); + if (!matched.size()) return; - Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", - hit_count, (int)internal_getpid()); - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (supp->hit_count == 0) - continue; - Printf("%d %s:%s\n", supp->hit_count, SuppTypeStr(supp->type), supp->templ); + int hit_count = 0; + for (uptr i = 0; i < matched.size(); i++) + hit_count += matched[i]->hit_count; + Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, + (int)internal_getpid()); + for (uptr i = 0; i < matched.size(); i++) { + Printf("%d %s:%s\n", matched[i]->hit_count, + SuppressionTypeString(matched[i]->type), matched[i]->templ); } } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h index 1c98363383dc..fe7db588f50f 100644 --- a/lib/tsan/rtl/tsan_suppressions.h +++ b/lib/tsan/rtl/tsan_suppressions.h @@ -13,31 +13,16 @@ #ifndef TSAN_SUPPRESSIONS_H #define TSAN_SUPPRESSIONS_H +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_report.h" namespace __tsan { -// Exposed for testing. -enum SuppressionType { - SuppressionRace, - SuppressionMutex, - SuppressionThread, - SuppressionSignal -}; - -struct Suppression { - Suppression *next; - SuppressionType type; - char *templ; - int hit_count; -}; - void InitializeSuppressions(); -void FinalizeSuppressions(); void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); -Suppression *SuppressionParse(Suppression *head, const char* supp); -bool SuppressionMatch(char *templ, const char *str); +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc index 12226064f5a4..75acf1d8438b 100644 --- a/lib/tsan/rtl/tsan_symbolize.cc +++ b/lib/tsan/rtl/tsan_symbolize.cc @@ -22,19 +22,17 @@ namespace __tsan { -struct ScopedInSymbolizer { - ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(!thr->in_symbolizer); - thr->in_symbolizer = true; - } +void EnterSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; +} - ~ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(thr->in_symbolizer); - thr->in_symbolizer = false; - } -}; +void ExitSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; +} ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, @@ -44,18 +42,6 @@ ReportStack *NewReportStackEntry(uptr addr) { return ent; } -// Strip module path to make output shorter. -static char *StripModuleName(const char *module) { - if (module == 0) - return 0; - const char *short_module_name = internal_strrchr(module, '/'); - if (short_module_name) - short_module_name += 1; - else - short_module_name = module; - return internal_strdup(short_module_name); -} - static ReportStack *NewReportStackEntry(const AddressInfo &info) { ReportStack *ent = NewReportStackEntry(info.address); ent->module = StripModuleName(info.module); @@ -69,16 +55,64 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { return ent; } + + ReportStack *next; + char *module; + uptr offset; + uptr pc; + char *func; + char *file; + int line; + int col; + + +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const uptr kExternalPCBit = 1ULL << 60; + +// May be overriden by JIT/JAVA/etc, +// whatever produces PCs marked with kExternalPCBit. +extern "C" bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) + SANITIZER_WEAK_ATTRIBUTE; + +bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) { + return false; +} + ReportStack *SymbolizeCode(uptr addr) { - if (!IsSymbolizerAvailable()) + // Check if PC comes from non-native land. + if (addr & kExternalPCBit) { + // Declare static to not consume too much stack space. + // We symbolize reports in a single thread, so this is fine. + static char func_buf[1024]; + static char file_buf[1024]; + int line, col; + if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), + file_buf, sizeof(file_buf), &line, &col)) + return NewReportStackEntry(addr); + ReportStack *ent = NewReportStackEntry(addr); + ent->module = 0; + ent->offset = 0; + ent->func = internal_strdup(func_buf); + ent->file = internal_strdup(file_buf); + ent->line = line; + ent->col = col; + return ent; + } + if (!Symbolizer::Get()->IsAvailable()) return SymbolizeCodeAddr2Line(addr); - ScopedInSymbolizer in_symbolizer; static const uptr kMaxAddrFrames = 16; InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); if (addr_frames_num == 0) return NewReportStackEntry(addr); ReportStack *top = 0; @@ -97,11 +131,10 @@ ReportStack *SymbolizeCode(uptr addr) { } ReportLocation *SymbolizeData(uptr addr) { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return 0; - ScopedInSymbolizer in_symbolizer; DataInfo info; - if (!__sanitizer::SymbolizeData(addr, &info)) + if (!Symbolizer::Get()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, sizeof(ReportLocation)); @@ -117,10 +150,9 @@ ReportLocation *SymbolizeData(uptr addr) { } void SymbolizeFlush() { - if (!IsSymbolizerAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return; - ScopedInSymbolizer in_symbolizer; - __sanitizer::FlushSymbolizer(); + Symbolizer::Get()->Flush(); } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h index 7bc6123df57d..6282e45b40d5 100644 --- a/lib/tsan/rtl/tsan_symbolize.h +++ b/lib/tsan/rtl/tsan_symbolize.h @@ -18,6 +18,8 @@ namespace __tsan { +void EnterSymbolizer(); +void ExitSymbolizer(); ReportStack *SymbolizeCode(uptr addr); ReportLocation *SymbolizeData(uptr addr); void SymbolizeFlush(); diff --git a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc index 47f9e1fbf418..b186d3b38ec9 100644 --- a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc +++ b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc @@ -60,7 +60,6 @@ static void NOINLINE InitModule(ModuleDesc *m) { } int pid = fork(); if (pid == 0) { - __sanitizer_set_report_fd(STDERR_FILENO); internal_close(STDOUT_FILENO); internal_close(STDIN_FILENO); internal_dup2(outfd[0], STDIN_FILENO); diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index c6ddcdb37426..f8f3c40fab04 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -265,6 +265,11 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) { n_ = c_ - !!toppc; } } else { + // Cap potentially huge stacks. + if (n_ + !!toppc > kTraceStackSize) { + start = n_ - kTraceStackSize + !!toppc; + n_ = kTraceStackSize - !!toppc; + } s_ = (uptr*)internal_alloc(MBlockStackTrace, (n_ + !!toppc) * sizeof(s_[0])); } diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h index 7df716046567..5ed0356e2bf5 100644 --- a/lib/tsan/rtl/tsan_trace.h +++ b/lib/tsan/rtl/tsan_trace.h @@ -62,6 +62,11 @@ struct TraceHeader { struct Trace { TraceHeader headers[kTraceParts]; Mutex mtx; +#ifndef TSAN_GO + // Must be last to catch overflow as paging fault. + // Go shadow stack is dynamically allocated. + uptr shadow_stack[kShadowStackSize]; +#endif Trace() : mtx(MutexTypeTrace, StatMtxTrace) { diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h index e7c036c5dea8..a11c9bc52509 100644 --- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h +++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h @@ -57,8 +57,7 @@ do { goto RACE; } // Do the memory access intersect? - // In Go all memory accesses are 1 byte, so there can be no intersections. - if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); |