[compiler-rt] [tsan] Allow unloading of ignored librairies (PR #105660)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 22 06:42:14 PDT 2024


https://github.com/goussepi created https://github.com/llvm/llvm-project/pull/105660

Allows unloading and reloading of ignored libraries. We don't attempt to reuse of free memory of unloaded library. So TSan will assert if an ignored library is reloaded 128 times.

>From 2a52bd00a79b4b6ad761b7bb0c35d2d440a26bc1 Mon Sep 17 00:00:00 2001
From: Pierre Gousseau <pierre.gousseau at sony.com>
Date: Wed, 21 Aug 2024 15:52:52 +0000
Subject: [PATCH] [tsan] Allow unloading of ignored librairies

Allows unloading and reloading of ignored libraries.
We don't attempt to reuse of free memory of unloaded library.
So TSan will assert if an ignored library is reloaded 128 times.
---
 .../sanitizer_common/sanitizer_libignore.cpp  | 14 ++++++--
 .../sanitizer_common/sanitizer_libignore.h    | 32 +++++++++++++++++--
 compiler-rt/test/tsan/ignore_lib3.cpp         | 29 ++++++++++++-----
 3 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp
index f0e1e3d69def53..85b97ac0e78a93 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp
@@ -33,6 +33,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
   lib->name = nullptr;
   lib->real_name = nullptr;
   lib->loaded = false;
+  lib->ignored_code_range_id = kInvalidCodeRangeId;
 }
 
 void LibIgnore::OnLibraryLoaded(const char *name) {
@@ -81,17 +82,24 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
         const uptr idx =
             atomic_load(&ignored_ranges_count_, memory_order_relaxed);
         CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
-        ignored_code_ranges_[idx].begin = range.beg;
+        ignored_code_ranges_[idx].begin(range.beg);
         ignored_code_ranges_[idx].end = range.end;
+        ignored_code_ranges_[idx].loaded = 1;
+        // Record the index of the ignored range.
+        lib->ignored_code_range_id = idx;
         atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
         break;
       }
     }
     if (lib->loaded && !loaded) {
-      Report("%s: library '%s' that was matched against called_from_lib"
+      VReport(1, "%s: library '%s' that was matched against called_from_lib"
              " suppression '%s' is unloaded\n",
              SanitizerToolName, lib->name, lib->templ);
-      Die();
+      // The library is unloaded so mark the ignored code range as unloaded.
+      CHECK_NE(lib->ignored_code_range_id, kInvalidCodeRangeId);
+      ignored_code_ranges_[lib->ignored_code_range_id].loaded = 0;
+      lib->ignored_code_range_id = kInvalidCodeRangeId;
+      lib->loaded = false;
     }
   }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h
index 18e4d83ed77fb8..228d15b3733cc7 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h
@@ -54,6 +54,7 @@ class LibIgnore {
     char *name;
     char *real_name;  // target of symlink
     bool loaded;
+    uptr ignored_code_range_id;
   };
 
   struct LibCodeRange {
@@ -61,6 +62,31 @@ class LibIgnore {
     uptr end;
   };
 
+  // Marks a range as loaded by utilizing the least significant bit of the code
+  // range. Assumes the start of the code range is 2-byte aligned.
+  struct LibLoadedCodeRange {
+    uptr begin() const { return begin_ << 1; }
+    void begin(uptr begin) {
+      CHECK_EQ(begin & 0x1, 0);
+      begin_ = begin >> 1;
+    }
+
+  private:
+    uptr begin_ : 63;
+
+  public:
+    uptr loaded : 1;
+    uptr end;
+  };
+
+  static_assert(sizeof(LibLoadedCodeRange) == 16,
+                "LibLoadedCodeRange size expected to be 16-bytes for "
+                "performance reasons.");
+
+  inline bool IsInRange(uptr pc, const LibLoadedCodeRange &range) const {
+    return (pc >= range.begin() && pc < range.end);
+  }
+
   inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
     return (pc >= range.begin && pc < range.end);
   }
@@ -68,10 +94,11 @@ class LibIgnore {
   static const uptr kMaxIgnoredRanges = 128;
   static const uptr kMaxInstrumentedRanges = 1024;
   static const uptr kMaxLibs = 1024;
+  static const uptr kInvalidCodeRangeId = ~0x0ULL;
 
   // Hot part:
   atomic_uintptr_t ignored_ranges_count_;
-  LibCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
+  LibLoadedCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
 
   atomic_uintptr_t instrumented_ranges_count_;
   LibCodeRange instrumented_code_ranges_[kMaxInstrumentedRanges];
@@ -90,7 +117,8 @@ class LibIgnore {
 inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
   const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
   for (uptr i = 0; i < n; i++) {
-    if (IsInRange(pc, ignored_code_ranges_[i])) {
+    if (ignored_code_ranges_[i].loaded &&
+        IsInRange(pc, ignored_code_ranges_[i])) {
       *pc_in_ignored_lib = true;
       return true;
     }
diff --git a/compiler-rt/test/tsan/ignore_lib3.cpp b/compiler-rt/test/tsan/ignore_lib3.cpp
index b1a3940d03b615..c1fac0138f68ef 100644
--- a/compiler-rt/test/tsan/ignore_lib3.cpp
+++ b/compiler-rt/test/tsan/ignore_lib3.cpp
@@ -3,10 +3,10 @@
 
 // RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %t-dir/libignore_lib3.so
 // RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t-dir/executable
-// RUN: %env_tsan_opts=suppressions='%s.supp' %deflake %run %t-dir/executable | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%s.supp':verbosity=1 %run %t-dir/executable 2>&1 | FileCheck %s
 
 // Tests that unloading of a library matched against called_from_lib suppression
-// causes program crash (this is not supported).
+// is supported.
 
 // Some aarch64 kernels do not support non executable write pages
 // REQUIRES: stable-runtime
@@ -22,18 +22,31 @@
 
 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);
+  void *h;
+  void (*f)();
+  // Try opening, closing and reopening the ignored lib.
+  for (unsigned int k = 0; k < 2; k++) {
+    h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+    if (h == 0)
+      exit(printf("failed to load the library (%d)\n", errno));
+    f = (void (*)())dlsym(h, "libfunc");
+    if (f == 0)
+      exit(printf("failed to find the func (%d)\n", errno));
+    f();
+    dlclose(h);
+  }
   fprintf(stderr, "OK\n");
 }
 
 #else  // #ifdef LIB
 
-extern "C" void libfunc() {
-}
+#include "ignore_lib_lib.h"
 
 #endif  // #ifdef LIB
 
-// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
-// CHECK-NOT: OK
+// CHECK: Matched called_from_lib suppression 'ignore_lib3.so'
+// CHECK: library '{{.*}}ignore_lib3.so' that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
+// CHECK: Matched called_from_lib suppression 'ignore_lib3.so'
+// CHECK: library '{{.*}}ignore_lib3.so' that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
+// CHECK: OK
 



More information about the llvm-commits mailing list