Skip to content

[tsan] Allow unloading of ignored libraries #105660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 16, 2024

Conversation

goussepi
Copy link
Collaborator

@goussepi goussepi commented Aug 22, 2024

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

@llvmbot
Copy link
Member

llvmbot commented Aug 22, 2024

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

Author: None (goussepi)

Changes

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

Copy link

github-actions bot commented Aug 22, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@fmayer
Copy link
Contributor

fmayer commented Aug 22, 2024

nit: typo in title

@goussepi goussepi force-pushed the tsan-close-ignore-lib branch from 2a52bd0 to 6fdb73e Compare August 23, 2024 10:20
@goussepi goussepi changed the title [tsan] Allow unloading of ignored librairies [tsan] Allow unloading of ignored libraries Aug 23, 2024
@vitalybuka
Copy link
Collaborator

Please don't forget to "re-request review" after responding

@goussepi goussepi requested a review from vitalybuka August 28, 2024 09:22
@goussepi
Copy link
Collaborator Author

goussepi commented Sep 4, 2024

ping!

1 similar comment
@goussepi
Copy link
Collaborator Author

ping!

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, if you agree with my changes

@goussepi
Copy link
Collaborator Author

LGTM, if you agree with my changes

Thank you for the review and update, looks much better, will squash rebase and push thanks!

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

Co-authored-by: Vitaly Buka <[email protected]>
@goussepi goussepi force-pushed the tsan-close-ignore-lib branch from ec93a2c to 55a3b39 Compare September 16, 2024 13:18
@goussepi goussepi merged commit 79c4ece into llvm:main Sep 16, 2024
7 checks passed
@goussepi goussepi deleted the tsan-close-ignore-lib branch September 16, 2024 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants