diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2013-10-03 13:37:17 +0000 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2013-10-03 13:37:17 +0000 |
| commit | 5ba736457c94580b9cf8a0a65fccf9a89da0b1a6 (patch) | |
| tree | afa352b57a63c0e8b42932077375b7c6310a3515 /compiler-rt/lib/sanitizer_common | |
| parent | d2757ba1be2915c8f41dd850ffdda8ff6a673382 (diff) | |
| download | bcm5719-llvm-5ba736457c94580b9cf8a0a65fccf9a89da0b1a6.tar.gz bcm5719-llvm-5ba736457c94580b9cf8a0a65fccf9a89da0b1a6.zip | |
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.
llvm-svn: 191897
Diffstat (limited to 'compiler-rt/lib/sanitizer_common')
6 files changed, 183 insertions, 5 deletions
diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt index 63c49630c13..6a960c9257b 100644 --- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/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/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc new file mode 100644 index 00000000000..9a0213e7b2b --- /dev/null +++ b/compiler-rt/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/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h new file mode 100644 index 00000000000..4e6f5a97ada --- /dev/null +++ b/compiler-rt/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/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc index 0676c0bd80a..5f3d2cee8ce 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/compiler-rt/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/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h index e0b562e5825..92cb09365b5 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h +++ b/compiler-rt/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/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc index f44911e1b98..ea8741d4f01 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ b/compiler-rt/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 { |

