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

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


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (goussepi)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/105660.diff


3 Files Affected:

- (modified) compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp (+11-3) 
- (modified) compiler-rt/lib/sanitizer_common/sanitizer_libignore.h (+30-2) 
- (modified) compiler-rt/test/tsan/ignore_lib3.cpp (+21-8) 


``````````diff
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
 

``````````

</details>


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


More information about the llvm-commits mailing list