-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Conversation
@llvm/pr-subscribers-compiler-rt-sanitizer Author: None (goussepi) ChangesAllows 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:
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
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
nit: typo in title |
2a52bd0
to
6fdb73e
Compare
Please don't forget to "re-request review" after responding |
ping! |
1 similar comment
ping! |
There was a problem hiding this 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
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]>
ec93a2c
to
55a3b39
Compare
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.