Skip to content

Commit 8291ea2

Browse files
committed
[tsan] Allow unloading of ignored libraries
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.
1 parent 2e78990 commit 8291ea2

File tree

3 files changed

+62
-13
lines changed

3 files changed

+62
-13
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
3333
lib->name = nullptr;
3434
lib->real_name = nullptr;
3535
lib->loaded = false;
36+
lib->ignored_code_range_id = kInvalidCodeRangeId;
3637
}
3738

3839
void LibIgnore::OnLibraryLoaded(const char *name) {
@@ -81,17 +82,24 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
8182
const uptr idx =
8283
atomic_load(&ignored_ranges_count_, memory_order_relaxed);
8384
CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
84-
ignored_code_ranges_[idx].begin = range.beg;
85+
ignored_code_ranges_[idx].begin(range.beg);
8586
ignored_code_ranges_[idx].end = range.end;
87+
ignored_code_ranges_[idx].loaded = 1;
88+
// Record the index of the ignored range.
89+
lib->ignored_code_range_id = idx;
8690
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
8791
break;
8892
}
8993
}
9094
if (lib->loaded && !loaded) {
91-
Report("%s: library '%s' that was matched against called_from_lib"
95+
VReport(1, "%s: library '%s' that was matched against called_from_lib"
9296
" suppression '%s' is unloaded\n",
9397
SanitizerToolName, lib->name, lib->templ);
94-
Die();
98+
// The library is unloaded so mark the ignored code range as unloaded.
99+
CHECK_NE(lib->ignored_code_range_id, kInvalidCodeRangeId);
100+
ignored_code_ranges_[lib->ignored_code_range_id].loaded = 0;
101+
lib->ignored_code_range_id = kInvalidCodeRangeId;
102+
lib->loaded = false;
95103
}
96104
}
97105

compiler-rt/lib/sanitizer_common/sanitizer_libignore.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,51 @@ class LibIgnore {
5454
char *name;
5555
char *real_name; // target of symlink
5656
bool loaded;
57+
uptr ignored_code_range_id;
5758
};
5859

5960
struct LibCodeRange {
6061
uptr begin;
6162
uptr end;
6263
};
6364

65+
// Marks a range as loaded by utilizing the least significant bit of the code
66+
// range. Assumes the start of the code range is 2-byte aligned.
67+
struct LibLoadedCodeRange {
68+
uptr begin() const { return begin_ << 1; }
69+
void begin(uptr begin) {
70+
CHECK_EQ(begin & 0x1, 0);
71+
begin_ = begin >> 1;
72+
}
73+
74+
private:
75+
uptr begin_ : 63;
76+
77+
public:
78+
uptr loaded : 1;
79+
uptr end;
80+
};
81+
82+
static_assert(sizeof(LibLoadedCodeRange) == 16,
83+
"LibLoadedCodeRange size expected to be 16-bytes for "
84+
"performance reasons.");
85+
86+
inline bool IsInRange(uptr pc, const LibLoadedCodeRange &range) const {
87+
return (pc >= range.begin() && pc < range.end);
88+
}
89+
6490
inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
6591
return (pc >= range.begin && pc < range.end);
6692
}
6793

6894
static const uptr kMaxIgnoredRanges = 128;
6995
static const uptr kMaxInstrumentedRanges = 1024;
7096
static const uptr kMaxLibs = 1024;
97+
static const uptr kInvalidCodeRangeId = ~0x0ULL;
7198

7299
// Hot part:
73100
atomic_uintptr_t ignored_ranges_count_;
74-
LibCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
101+
LibLoadedCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
75102

76103
atomic_uintptr_t instrumented_ranges_count_;
77104
LibCodeRange instrumented_code_ranges_[kMaxInstrumentedRanges];
@@ -90,7 +117,8 @@ class LibIgnore {
90117
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
91118
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
92119
for (uptr i = 0; i < n; i++) {
93-
if (IsInRange(pc, ignored_code_ranges_[i])) {
120+
if (ignored_code_ranges_[i].loaded &&
121+
IsInRange(pc, ignored_code_ranges_[i])) {
94122
*pc_in_ignored_lib = true;
95123
return true;
96124
}

compiler-rt/test/tsan/ignore_lib3.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %t-dir/libignore_lib3.so
55
// RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t-dir/executable
6-
// RUN: %env_tsan_opts=suppressions='%s.supp' %deflake %run %t-dir/executable | FileCheck %s
6+
// RUN: %env_tsan_opts=suppressions='%s.supp':verbosity=1 %run %t-dir/executable 2>&1 | FileCheck %s
77

88
// Tests that unloading of a library matched against called_from_lib suppression
9-
// causes program crash (this is not supported).
9+
// is supported.
1010

1111
// Some aarch64 kernels do not support non executable write pages
1212
// REQUIRES: stable-runtime
@@ -22,18 +22,31 @@
2222

2323
int main(int argc, char **argv) {
2424
std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so";
25-
void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
26-
dlclose(h);
25+
void *h;
26+
void (*f)();
27+
// Try opening, closing and reopening the ignored lib.
28+
for (unsigned int k = 0; k < 2; k++) {
29+
h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
30+
if (h == 0)
31+
exit(printf("failed to load the library (%d)\n", errno));
32+
f = (void (*)())dlsym(h, "libfunc");
33+
if (f == 0)
34+
exit(printf("failed to find the func (%d)\n", errno));
35+
f();
36+
dlclose(h);
37+
}
2738
fprintf(stderr, "OK\n");
2839
}
2940

3041
#else // #ifdef LIB
3142

32-
extern "C" void libfunc() {
33-
}
43+
#include "ignore_lib_lib.h"
3444

3545
#endif // #ifdef LIB
3646

37-
// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
38-
// CHECK-NOT: OK
47+
// CHECK: Matched called_from_lib suppression 'ignore_lib3.so'
48+
// CHECK: library '{{.*}}ignore_lib3.so' that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
49+
// CHECK: Matched called_from_lib suppression 'ignore_lib3.so'
50+
// CHECK: library '{{.*}}ignore_lib3.so' that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
51+
// CHECK: OK
3952

0 commit comments

Comments
 (0)