Index: sanitizer_common/CMakeLists.txt =================================================================== --- sanitizer_common/CMakeLists.txt +++ sanitizer_common/CMakeLists.txt @@ -6,6 +6,7 @@ sanitizer_common.cc sanitizer_flags.cc sanitizer_libc.cc + sanitizer_libignore.cc sanitizer_linux.cc sanitizer_mac.cc sanitizer_platform_limits_linux.cc Index: sanitizer_common/sanitizer_libignore.h =================================================================== --- sanitizer_common/sanitizer_libignore.h +++ sanitizer_common/sanitizer_libignore.h @@ -0,0 +1,77 @@ +//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// LibIgnore allows to ignore all interceptors called from a particular set +// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions +// from the provided SuppressionContext; finds code ranges for the libraries; +// and checks whether the provided PC value belongs to the code ranges. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIBIGNORE_H +#define SANITIZER_LIBIGNORE_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" +#include "sanitizer_suppressions.h" +#include "sanitizer_atomic.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +class ALIGNED(64) LibIgnore { + public: + LibIgnore(LinkerInitialized); + + // Fetches all "called_from_lib" suppressions from the SuppressionContext. + void Init(const SuppressionContext &supp); + + // Must be called after a new dynamic library is loaded. + void OnLibraryLoaded(); + + // Checks whether the provided PC belongs to one of the ignored libraries. + bool IsIgnored(uptr pc) const; + + private: + struct Lib { + char *name; + bool loaded; + }; + + struct LibCodeRange { + uptr begin; + uptr end; + }; + + static const uptr kMaxLibs = 128; + + // Hot part: + atomic_uintptr_t loaded_count_; + LibCodeRange code_ranges_[kMaxLibs]; + + // Cold part: + BlockingMutex mutex_; + uptr count_; + Lib libs_[kMaxLibs]; + + LibIgnore(const LibIgnore&); // not implemented + void operator = (const LibIgnore&); // not implemented +}; + +inline bool LibIgnore::IsIgnored(uptr pc) const { + const uptr n = atomic_load(&loaded_count_, memory_order_acquire); + for (uptr i = 0; i < n; i++) { + if (pc >= code_ranges_[i].begin && pc < code_ranges_[i].end) + return true; + } + return false; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LIBIGNORE_H Index: sanitizer_common/sanitizer_libignore.cc =================================================================== --- sanitizer_common/sanitizer_libignore.cc +++ sanitizer_common/sanitizer_libignore.cc @@ -0,0 +1,60 @@ +//===-- sanitizer_libignore.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_libignore.h" +#include "sanitizer_procmaps.h" + +namespace __sanitizer { + +LibIgnore::LibIgnore(LinkerInitialized) { +} + +void LibIgnore::Init(const SuppressionContext &supp) { + BlockingMutexLock lock(&mutex_); + CHECK_EQ(count_, 0); + const InternalMmapVector *list = supp.GetSuppressions(); + for (uptr i = 0; i < list->size(); i++) { + const Suppression *s = &(*list)[i]; + if (s->type != SuppressionLib) + continue; + CHECK_LT(count_, kMaxLibs); + Lib *lib = &libs_[count_++]; + lib->name = internal_strdup(s->templ); + lib->loaded = false; + } +} + +void LibIgnore::OnLibraryLoaded() { + if (atomic_load(&loaded_count_, memory_order_relaxed) == count_) + return; + BlockingMutexLock lock(&mutex_); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + InternalMmapVector fn(4096); + fn.push_back(0); + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + if (lib->loaded) + continue; + proc_maps.Reset(); + uptr b, e, off, prot; + while (proc_maps.Next(&b, &e, &off, &fn[0], fn.capacity(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 + && TemplateMatch(lib->name, &fn[0])) { + lib->loaded = true; + const uptr idx = atomic_load(&loaded_count_, memory_order_relaxed); + code_ranges_[idx].begin = b; + code_ranges_[idx].end = e; + atomic_store(&loaded_count_, idx + 1, memory_order_release); + break; + } + } + } +} + +} // namespace __sanitizer Index: sanitizer_common/sanitizer_suppressions.h =================================================================== --- sanitizer_common/sanitizer_suppressions.h +++ sanitizer_common/sanitizer_suppressions.h @@ -25,6 +25,7 @@ SuppressionThread, SuppressionSignal, SuppressionLeak, + SuppressionLib, SuppressionTypeCount }; @@ -42,6 +43,7 @@ bool Match(const char* str, SuppressionType type, Suppression **s); uptr SuppressionCount(); void GetMatched(InternalMmapVector *matched); + const InternalMmapVector *GetSuppressions() const; private: InternalMmapVector suppressions_; @@ -52,7 +54,6 @@ const char *SuppressionTypeString(SuppressionType t); -// Exposed for testing. bool TemplateMatch(char *templ, const char *str); } // namespace __sanitizer Index: sanitizer_common/sanitizer_suppressions.cc =================================================================== --- sanitizer_common/sanitizer_suppressions.cc +++ sanitizer_common/sanitizer_suppressions.cc @@ -20,7 +20,7 @@ namespace __sanitizer { static const char *const kTypeStrings[SuppressionTypeCount] = { - "none", "race", "mutex", "thread", "signal", "leak" + "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" }; bool TemplateMatch(char *templ, const char *str) { @@ -140,6 +140,11 @@ matched->push_back(&suppressions_[i]); } +const InternalMmapVector *SuppressionContext::GetSuppressions() + const { + return &suppressions_; +} + const char *SuppressionTypeString(SuppressionType t) { CHECK(t < SuppressionTypeCount); return kTypeStrings[t]; Index: sanitizer_common/tests/sanitizer_suppressions_test.cc =================================================================== --- sanitizer_common/tests/sanitizer_suppressions_test.cc +++ sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -65,8 +65,10 @@ CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), + "called_from_lib")); // Ensure this test is up-to-date when suppression types are added. - CHECK_EQ(SuppressionTypeCount, 6); + CHECK_EQ(SuppressionTypeCount, 7); } class SuppressionContextTest : public ::testing::Test { Index: tsan/lit_tests/ignore_lib0.cc =================================================================== --- tsan/lit_tests/ignore_lib0.cc +++ tsan/lit_tests/ignore_lib0.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o ./Output/libignore_lib0.so +// RUN: %clangxx_tsan -O1 %s -L./Output/ -lignore_lib0 -o %t +// RUN: LD_LIBRARY_PATH=./Output/ not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: LD_LIBRARY_PATH=./Output/ TSAN_OPTIONS=suppressions=%s.supp not %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +#ifndef LIB + +void libfunc(); + +int main() { + libfunc(); +} + +#else // #ifdef LIB + +#include +#include +#include +#include +#include + +void *volatile mem; +volatile int len; + +void *Thread(void *p) { + while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0) + usleep(100); + memset(p, 0, len); + return 0; +} + +void libfunc() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + len = 10; + __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE); + pthread_join(t, 0); + free(mem); + fprintf(stderr, "OK\n"); +} + +#endif + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + Index: tsan/lit_tests/ignore_lib0.cc.supp =================================================================== --- tsan/lit_tests/ignore_lib0.cc.supp +++ tsan/lit_tests/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + Index: tsan/lit_tests/java_alloc.cc =================================================================== --- tsan/lit_tests/java_alloc.cc +++ tsan/lit_tests/java_alloc.cc @@ -20,7 +20,6 @@ int main() { jptr jheap = (jptr)malloc(kHeapSize); - __tsan_java_preinit("[vdso]"); __tsan_java_init(jheap, kHeapSize); pthread_t th; pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4)); Index: tsan/lit_tests/java_lock.cc =================================================================== --- tsan/lit_tests/java_lock.cc +++ tsan/lit_tests/java_lock.cc @@ -16,7 +16,6 @@ int main() { int const kHeapSize = 1024 * 1024; void *jheap = malloc(kHeapSize); - __tsan_java_preinit(0); __tsan_java_init((jptr)jheap, kHeapSize); const int kBlockSize = 16; __tsan_java_alloc((jptr)jheap, kBlockSize); Index: tsan/rtl/tsan_interceptors.cc =================================================================== --- tsan/rtl/tsan_interceptors.cc +++ 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" @@ -126,18 +127,14 @@ SignalDesc pending_signals[kSigCount]; }; -// Used to ignore interceptors coming directly from libjvm.so. -atomic_uintptr_t libjvm_begin; -atomic_uintptr_t libjvm_end; - -static bool libjvm_check(uptr pc) { - uptr begin = atomic_load(&libjvm_begin, memory_order_relaxed); - if (begin != 0 && pc >= begin) { - uptr end = atomic_load(&libjvm_end, memory_order_relaxed); - if (end != 0 && pc < end) - return true; - } - return false; +static uptr libignore_placeholder[sizeof(LibIgnore) / sizeof(uptr) + 1]; +static LibIgnore *libignore() { + return reinterpret_cast(&libignore_placeholder[0]); +} + +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(); } } // namespace __tsan @@ -162,12 +159,14 @@ 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_() { if (thr_->in_rtl == 0) { Initialize(thr); FuncEntry(thr, pc); @@ -176,9 +175,18 @@ } 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_); @@ -203,7 +211,7 @@ Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1 || libjvm_check(pc)) \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -246,6 +254,13 @@ return res; } +TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); + void *res = REAL(dlopen)(filename, flag); + libignore()->OnLibraryLoaded(); + return res; +} + class AtExitContext { public: AtExitContext() @@ -441,7 +456,7 @@ } TSAN_INTERCEPTOR(void*, malloc, uptr size) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_malloc(size); void *p = 0; { @@ -458,7 +473,7 @@ } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return AllocatorReturnNull(); @@ -474,7 +489,7 @@ } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_realloc(p, size); if (p) invoke_free_hook(p); @@ -489,7 +504,7 @@ TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); @@ -499,7 +514,7 @@ TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); @@ -508,13 +523,11 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); - if (libjvm_check(pc)) - return malloc_usable_size(p); return user_alloc_usable_size(thr, pc, p); } #define OPERATOR_NEW_BODY(mangled_name) \ - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ + if (cur_thread()->in_symbolizer) \ return __libc_malloc(size); \ void *p = 0; \ { \ @@ -550,7 +563,7 @@ #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ + if (cur_thread()->in_symbolizer) \ return __libc_free(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ @@ -684,15 +697,6 @@ TSAN_INTERCEPTOR(char*, strdup, const char *str) { SCOPED_TSAN_INTERCEPTOR(strdup, str); - if (libjvm_check(pc)) { - // The memory must come from libc malloc, - // and we must not instrument accesses in this case. - uptr n = internal_strlen(str) + 1; - void *p = __libc_malloc(n); - if (p == 0) - return 0; - return (char*)internal_memcpy(p, str, n); - } // strdup will call malloc, so no instrumentation is required here. return REAL(strdup)(str); } @@ -747,23 +751,23 @@ } 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; } @@ -852,7 +856,7 @@ 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); @@ -1820,7 +1824,7 @@ } TSAN_INTERCEPTOR(int, fork, int fake) { - SCOPED_TSAN_INTERCEPTOR(fork, fake); + SCOPED_INTERCEPTOR_RAW(fork, fake); int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -2174,6 +2178,7 @@ TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); Index: tsan/rtl/tsan_interface_java.h =================================================================== --- tsan/rtl/tsan_interface_java.h +++ tsan/rtl/tsan_interface_java.h @@ -34,11 +34,7 @@ typedef unsigned long jptr; // NOLINT -// Must be called before any other callback from Java, right after dlopen -// of JVM shared lib. If libjvm_path is specified, then all interceptors -// coming directly from JVM will be ignored. -void __tsan_java_preinit(const char *libjvm_path) INTERFACE_ATTRIBUTE; -// Must be called after __tsan_java_preinit but before any other callback. +// Must be called before any other callback from Java. void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE; // Must be called when the application exits. // Not necessary the last callback (concurrently running threads are OK). Index: tsan/rtl/tsan_interface_java.cc =================================================================== --- tsan/rtl/tsan_interface_java.cc +++ tsan/rtl/tsan_interface_java.cc @@ -96,8 +96,6 @@ static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1]; static JavaContext *jctx; -extern atomic_uintptr_t libjvm_begin; -extern atomic_uintptr_t libjvm_end; static BlockDesc *getblock(uptr addr) { uptr i = (addr - jctx->heap_begin) / kHeapAlignment; @@ -166,17 +164,6 @@ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ -void __tsan_java_preinit(const char *libjvm_path) { - SCOPED_JAVA_FUNC(__tsan_java_preinit); - if (libjvm_path) { - uptr begin, end; - if (GetCodeRangeForFile(libjvm_path, &begin, &end)) { - atomic_store(&libjvm_begin, begin, memory_order_relaxed); - atomic_store(&libjvm_end, end, memory_order_relaxed); - } - } -} - void __tsan_java_init(jptr heap_begin, jptr heap_size) { SCOPED_JAVA_FUNC(__tsan_java_init); DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size); Index: tsan/rtl/tsan_rtl.h =================================================================== --- tsan/rtl/tsan_rtl.h +++ tsan/rtl/tsan_rtl.h @@ -31,6 +31,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" +#include "sanitizer_common/sanitizer_libignore.h" #include "tsan_clock.h" #include "tsan_defs.h" #include "tsan_flags.h" @@ -434,6 +435,7 @@ const int unique_id; int in_rtl; bool in_symbolizer; + bool in_ignored_lib; bool is_alive; bool is_freeing; bool is_vptr_access; @@ -598,6 +600,7 @@ void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); +void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); Index: tsan/rtl/tsan_rtl.cc =================================================================== --- tsan/rtl/tsan_rtl.cc +++ tsan/rtl/tsan_rtl.cc @@ -214,6 +214,9 @@ __sanitizer_set_report_path(flags()->log_path); InitializeSuppressions(); #ifndef TSAN_GO + InitializeLibIgnore(); +#endif +#ifndef TSAN_GO // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { Index: tsan/rtl/tsan_stat.h =================================================================== --- tsan/rtl/tsan_stat.h +++ tsan/rtl/tsan_stat.h @@ -366,6 +366,7 @@ StatInt_sigprocmask, StatInt_backtrace, StatInt_backtrace_symbols, + StatInt_dlopen, // Dynamic annotations. StatAnnotation, Index: tsan/rtl/tsan_stat.cc =================================================================== --- tsan/rtl/tsan_stat.cc +++ tsan/rtl/tsan_stat.cc @@ -371,6 +371,7 @@ name[StatInt_sigprocmask] = " sigprocmask "; name[StatInt_backtrace] = " backtrace "; name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; Index: tsan/rtl/tsan_suppressions.h =================================================================== --- tsan/rtl/tsan_suppressions.h +++ tsan/rtl/tsan_suppressions.h @@ -22,6 +22,7 @@ void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); } // namespace __tsan Index: tsan/rtl/tsan_suppressions.cc =================================================================== --- tsan/rtl/tsan_suppressions.cc +++ tsan/rtl/tsan_suppressions.cc @@ -87,6 +87,11 @@ #endif } +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) return SuppressionRace;