summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2013-10-03 13:37:17 +0000
committerDmitry Vyukov <dvyukov@google.com>2013-10-03 13:37:17 +0000
commit4af0f21c0c98950df1136dbec8824a029ed5bb8e (patch)
treec627364c732421ba62a21cbf7f96d86fbfbd3c0d
parent984930932ac6d85af5b07294a91b203ce822dc68 (diff)
tsan: ignore interceptors coming from specified libraries
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. Also make malloc and friends interceptors use SCOPED_INTERCEPTOR_RAW instead of SCOPED_TSAN_INTERCEPTOR, because if they are called from an ignored lib, then must call our internal allocator instead of libc malloc. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@191897 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/sanitizer_common/CMakeLists.txt2
-rw-r--r--lib/sanitizer_common/sanitizer_libignore.cc85
-rw-r--r--lib/sanitizer_common/sanitizer_libignore.h83
-rw-r--r--lib/sanitizer_common/sanitizer_suppressions.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_suppressions.h5
-rw-r--r--lib/sanitizer_common/tests/sanitizer_suppressions_test.cc4
-rw-r--r--lib/tsan/lit_tests/ignore_lib0.cc30
-rw-r--r--lib/tsan/lit_tests/ignore_lib0.cc.supp2
-rw-r--r--lib/tsan/lit_tests/ignore_lib1.cc42
-rw-r--r--lib/tsan/lit_tests/ignore_lib1.cc.supp2
-rw-r--r--lib/tsan/lit_tests/ignore_lib2.cc33
-rw-r--r--lib/tsan/lit_tests/ignore_lib2.cc.supp2
-rw-r--r--lib/tsan/lit_tests/ignore_lib3.cc33
-rw-r--r--lib/tsan/lit_tests/ignore_lib3.cc.supp2
-rw-r--r--lib/tsan/lit_tests/ignore_lib_lib.h25
-rw-r--r--lib/tsan/lit_tests/java_alloc.cc1
-rw-r--r--lib/tsan/lit_tests/java_lock.cc1
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc99
-rw-r--r--lib/tsan/rtl/tsan_interface_java.cc13
-rw-r--r--lib/tsan/rtl/tsan_interface_java.h6
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc1
-rw-r--r--lib/tsan/rtl/tsan_rtl.h3
-rw-r--r--lib/tsan/rtl/tsan_stat.cc2
-rw-r--r--lib/tsan/rtl/tsan_stat.h2
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc5
-rw-r--r--lib/tsan/rtl/tsan_suppressions.h1
26 files changed, 430 insertions, 63 deletions
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 63c49630c..6a960c925 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -6,6 +6,7 @@ set(SANITIZER_SOURCES
sanitizer_common.cc
sanitizer_flags.cc
sanitizer_libc.cc
+ sanitizer_libignore.cc
sanitizer_linux.cc
sanitizer_mac.cc
sanitizer_platform_limits_linux.cc
@@ -44,6 +45,7 @@ set(SANITIZER_HEADERS
sanitizer_internal_defs.h
sanitizer_lfstack.h
sanitizer_libc.h
+ sanitizer_libignore.h
sanitizer_linux.h
sanitizer_list.h
sanitizer_mutex.h
diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc
new file mode 100644
index 000000000..9a0213e7b
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_libignore.cc
@@ -0,0 +1,85 @@
+//===-- 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_platform.h"
+#if SANITIZER_LINUX
+
+#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 uptr n = supp.SuppressionCount();
+ for (uptr i = 0; i < n; i++) {
+ const Suppression *s = supp.SuppressionAt(i);
+ if (s->type != SuppressionLib)
+ continue;
+ if (count_ >= kMaxLibs) {
+ Report("%s: too many called_from_lib suppressions (max: %d)\n",
+ SanitizerToolName, kMaxLibs);
+ Die();
+ }
+ Lib *lib = &libs_[count_++];
+ lib->templ = internal_strdup(s->templ);
+ lib->name = 0;
+ lib->loaded = false;
+ }
+}
+
+void LibIgnore::OnLibraryLoaded() {
+ BlockingMutexLock lock(&mutex_);
+ MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+ InternalScopedBuffer<char> fn(4096);
+ for (uptr i = 0; i < count_; i++) {
+ Lib *lib = &libs_[i];
+ bool loaded = false;
+ proc_maps.Reset();
+ uptr b, e, off, prot;
+ while (proc_maps.Next(&b, &e, &off, fn.data(), fn.size(), &prot)) {
+ if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 &&
+ TemplateMatch(lib->templ, fn.data())) {
+ if (loaded) {
+ Report("%s: called_from_lib suppression '%s' is matched against"
+ " 2 libraries: '%s' and '%s'\n",
+ SanitizerToolName, lib->templ, lib->name, fn.data());
+ Die();
+ }
+ loaded = true;
+ if (!lib->loaded) {
+ lib->loaded = true;
+ lib->name = internal_strdup(fn.data());
+ 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);
+ }
+ }
+ }
+ if (lib->loaded && !loaded) {
+ Report("%s: library '%s' that was matched against called_from_lib"
+ " suppression '%s' is unloaded\n",
+ SanitizerToolName, lib->name, lib->templ);
+ Die();
+ }
+ }
+}
+
+void LibIgnore::OnLibraryUnloaded() {
+ OnLibraryLoaded();
+}
+
+} // namespace __sanitizer
+
+#endif // #if SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_libignore.h b/lib/sanitizer_common/sanitizer_libignore.h
new file mode 100644
index 000000000..4e6f5a97a
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_libignore.h
@@ -0,0 +1,83 @@
+//===-- 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 LibIgnore {
+ public:
+ explicit 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();
+
+ // Must be called after a dynamic library is unloaded.
+ void OnLibraryUnloaded();
+
+ // Checks whether the provided PC belongs to one of the ignored libraries.
+ bool IsIgnored(uptr pc) const;
+
+ private:
+ struct Lib {
+ char *templ;
+ 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];
+
+ // Disallow copying of LibIgnore objects.
+ 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
diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 0676c0bd8..5f3d2cee8 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/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) {
@@ -129,10 +129,15 @@ void SuppressionContext::Parse(const char *str) {
}
}
-uptr SuppressionContext::SuppressionCount() {
+uptr SuppressionContext::SuppressionCount() const {
return suppressions_.size();
}
+const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
+ CHECK_LT(i, suppressions_.size());
+ return &suppressions_[i];
+}
+
void SuppressionContext::GetMatched(
InternalMmapVector<Suppression *> *matched) {
for (uptr i = 0; i < suppressions_.size(); i++)
diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h
index e0b562e58..92cb09365 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.h
+++ b/lib/sanitizer_common/sanitizer_suppressions.h
@@ -25,6 +25,7 @@ enum SuppressionType {
SuppressionThread,
SuppressionSignal,
SuppressionLeak,
+ SuppressionLib,
SuppressionTypeCount
};
@@ -40,7 +41,8 @@ class SuppressionContext {
SuppressionContext() : suppressions_(1), can_parse_(true) {}
void Parse(const char *str);
bool Match(const char* str, SuppressionType type, Suppression **s);
- uptr SuppressionCount();
+ uptr SuppressionCount() const;
+ const Suppression *SuppressionAt(uptr i) const;
void GetMatched(InternalMmapVector<Suppression *> *matched);
private:
@@ -52,7 +54,6 @@ class SuppressionContext {
const char *SuppressionTypeString(SuppressionType t);
-// Exposed for testing.
bool TemplateMatch(char *templ, const char *str);
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
index f44911e1b..ea8741d4f 100644
--- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
@@ -65,8 +65,10 @@ TEST(Suppressions, TypeStrings) {
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 {
diff --git a/lib/tsan/lit_tests/ignore_lib0.cc b/lib/tsan/lit_tests/ignore_lib0.cc
new file mode 100644
index 000000000..ea0f061e6
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib0.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so
+// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t
+// RUN: echo running w/o suppressions:
+// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a library specified in called_from_lib
+// suppression are ignored.
+
+#ifndef LIB
+
+extern "C" void libfunc();
+
+int main() {
+ libfunc();
+}
+
+#else // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib0.cc.supp b/lib/tsan/lit_tests/ignore_lib0.cc.supp
new file mode 100644
index 000000000..7728c926b
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib0.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib0.so
+
diff --git a/lib/tsan/lit_tests/ignore_lib1.cc b/lib/tsan/lit_tests/ignore_lib1.cc
new file mode 100644
index 000000000..c4f2e7344
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib1.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: echo running w/o suppressions:
+// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a dynamically loaded library specified
+// in called_from_lib suppression are ignored.
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so";
+ void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ if (h == 0)
+ exit(printf("failed to load the library (%d)\n", errno));
+ void (*f)() = (void(*)())dlsym(h, "libfunc");
+ if (f == 0)
+ exit(printf("failed to find the func (%d)\n", errno));
+ f();
+}
+
+#else // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib1.cc.supp b/lib/tsan/lit_tests/ignore_lib1.cc.supp
new file mode 100644
index 000000000..9f4119ec0
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib1.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib1.so$
+
diff --git a/lib/tsan/lit_tests/ignore_lib2.cc b/lib/tsan/lit_tests/ignore_lib2.cc
new file mode 100644
index 000000000..97f9419e4
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib2.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that called_from_lib suppression matched against 2 libraries
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so";
+ std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so";
+ dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ fprintf(stderr, "OK\n");
+}
+
+#else // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif // #ifdef LIB
+
+// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries
+// CHECK-NOT: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib2.cc.supp b/lib/tsan/lit_tests/ignore_lib2.cc.supp
new file mode 100644
index 000000000..1419c71c6
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib2.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib2
+
diff --git a/lib/tsan/lit_tests/ignore_lib3.cc b/lib/tsan/lit_tests/ignore_lib3.cc
new file mode 100644
index 000000000..8f237fcc8
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib3.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that unloading of a library matched against called_from_lib suppression
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so";
+ void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ dlclose(h);
+ fprintf(stderr, "OK\n");
+}
+
+#else // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif // #ifdef LIB
+
+// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
+// CHECK-NOT: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib3.cc.supp b/lib/tsan/lit_tests/ignore_lib3.cc.supp
new file mode 100644
index 000000000..975dbcef9
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib3.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib3.so
+
diff --git a/lib/tsan/lit_tests/ignore_lib_lib.h b/lib/tsan/lit_tests/ignore_lib_lib.h
new file mode 100644
index 000000000..2bfe84dfc
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib_lib.h
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+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;
+}
+
+extern "C" 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");
+}
diff --git a/lib/tsan/lit_tests/java_alloc.cc b/lib/tsan/lit_tests/java_alloc.cc
index 079c9b92a..4dbce70c3 100644
--- a/lib/tsan/lit_tests/java_alloc.cc
+++ b/lib/tsan/lit_tests/java_alloc.cc
@@ -20,7 +20,6 @@ void *Thread(void *p) {
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));
diff --git a/lib/tsan/lit_tests/java_lock.cc b/lib/tsan/lit_tests/java_lock.cc
index c01959719..d9db10350 100644
--- a/lib/tsan/lit_tests/java_lock.cc
+++ b/lib/tsan/lit_tests/java_lock.cc
@@ -16,7 +16,6 @@ void *Thread(void *p) {
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);
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 0b6291a7c..c4bde58f1 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"
@@ -126,18 +127,16 @@ struct SignalContext {
SignalDesc pending_signals[kSigCount];
};
-// Used to ignore interceptors coming directly from libjvm.so.
-atomic_uintptr_t libjvm_begin;
-atomic_uintptr_t libjvm_end;
+// 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]);
+}
-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;
+void InitializeLibIgnore() {
+ libignore()->Init(*GetSuppressionContext());
+ libignore()->OnLibraryLoaded();
}
} // namespace __tsan
@@ -162,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);
@@ -176,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_);
@@ -203,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() {
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 +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();
+ 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()
@@ -441,7 +473,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) {
}
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 +490,7 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
}
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 +506,7 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
}
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 +521,7 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
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 +531,7 @@ TSAN_INTERCEPTOR(void, free, void *p) {
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 +540,11 @@ TSAN_INTERCEPTOR(void, cfree, void *p) {
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 +580,7 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
#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 +714,6 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
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 +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;
}
@@ -852,7 +873,7 @@ 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);
@@ -886,7 +907,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) {
@@ -1820,7 +1841,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) {
}
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 +2195,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(munlockall);
TSAN_INTERCEPT(fork);
+ TSAN_INTERCEPT(dlopen);
+ TSAN_INTERCEPT(dlclose);
TSAN_INTERCEPT(on_exit);
TSAN_INTERCEPT(__cxa_atexit);
diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc
index 2ac3249ac..53f14cf07 100644
--- a/lib/tsan/rtl/tsan_interface_java.cc
+++ b/lib/tsan/rtl/tsan_interface_java.cc
@@ -96,8 +96,6 @@ class ScopedJavaFunc {
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 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
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);
diff --git a/lib/tsan/rtl/tsan_interface_java.h b/lib/tsan/rtl/tsan_interface_java.h
index a4a05aa2f..9ac78e074 100644
--- a/lib/tsan/rtl/tsan_interface_java.h
+++ b/lib/tsan/rtl/tsan_interface_java.h
@@ -34,11 +34,7 @@ extern "C" {
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).
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index dcd98b032..aa8934ada 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -214,6 +214,7 @@ void Initialize(ThreadState *thr) {
__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') {
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index ed7b7edcc..9169aeb40 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -29,6 +29,7 @@
#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"
@@ -434,6 +435,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;
@@ -598,6 +600,7 @@ void MapThreadTrace(uptr addr, uptr size);
void DontNeedShadowFor(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();
+void InitializeLibIgnore();
void InitializeDynamicAnnotations();
void ReportRace(ThreadState *thr);
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index 3a2173e28..96d5ee486 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -371,6 +371,8 @@ void StatOutput(u64 *stat) {
name[StatInt_sigprocmask] = " sigprocmask ";
name[StatInt_backtrace] = " backtrace ";
name[StatInt_backtrace_symbols] = " backtrace_symbols ";
+ name[StatInt_dlopen] = " dlopen ";
+ name[StatInt_dlclose] = " dlclose ";
name[StatAnnotation] = "Dynamic annotations ";
name[StatAnnotateHappensBefore] = " HappensBefore ";
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
index 526190c2d..43c6b1cca 100644
--- a/lib/tsan/rtl/tsan_stat.h
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -366,6 +366,8 @@ enum StatType {
StatInt_sigprocmask,
StatInt_backtrace,
StatInt_backtrace_symbols,
+ StatInt_dlopen,
+ StatInt_dlclose,
// Dynamic annotations.
StatAnnotation,
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 0a365b2dc..4b0ac04c4 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -87,6 +87,11 @@ void InitializeSuppressions() {
#endif
}
+SuppressionContext *GetSuppressionContext() {
+ CHECK_NE(g_ctx, 0);
+ return g_ctx;
+}
+
SuppressionType conv(ReportType typ) {
if (typ == ReportTypeRace)
return SuppressionRace;
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index c618b3db4..fe7db588f 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -22,6 +22,7 @@ void InitializeSuppressions();
void PrintMatchedSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
+SuppressionContext *GetSuppressionContext();
} // namespace __tsan