Skip to content

Commit 79c4ece

Browse files
goussepivitalybuka
andauthored
[tsan] Allow unloading of ignored libraries (#105660)
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]>
1 parent b3470c3 commit 79c4ece

File tree

3 files changed

+59
-34
lines changed

3 files changed

+59
-34
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_libignore.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
3232
lib->templ = internal_strdup(name_templ);
3333
lib->name = nullptr;
3434
lib->real_name = nullptr;
35-
lib->loaded = false;
35+
lib->range_id = kInvalidCodeRangeId;
3636
}
3737

3838
void LibIgnore::OnLibraryLoaded(const char *name) {
@@ -43,7 +43,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
4343
buf[0]) {
4444
for (uptr i = 0; i < count_; i++) {
4545
Lib *lib = &libs_[i];
46-
if (!lib->loaded && (!lib->real_name) &&
46+
if (!lib->loaded() && (!lib->real_name) &&
4747
TemplateMatch(lib->templ, name))
4848
lib->real_name = internal_strdup(buf.data());
4949
}
@@ -70,28 +70,31 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
7070
Die();
7171
}
7272
loaded = true;
73-
if (lib->loaded)
73+
if (lib->loaded())
7474
continue;
7575
VReport(1,
7676
"Matched called_from_lib suppression '%s' against library"
7777
" '%s'\n",
7878
lib->templ, mod.full_name());
79-
lib->loaded = true;
8079
lib->name = internal_strdup(mod.full_name());
8180
const uptr idx =
8281
atomic_load(&ignored_ranges_count_, memory_order_relaxed);
8382
CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
84-
ignored_code_ranges_[idx].begin = range.beg;
85-
ignored_code_ranges_[idx].end = range.end;
83+
ignored_code_ranges_[idx].OnLoad(range.beg, range.end);
84+
// Record the index of the ignored range.
85+
lib->range_id = idx;
8686
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
8787
break;
8888
}
8989
}
90-
if (lib->loaded && !loaded) {
91-
Report("%s: library '%s' that was matched against called_from_lib"
92-
" suppression '%s' is unloaded\n",
93-
SanitizerToolName, lib->name, lib->templ);
94-
Die();
90+
if (lib->loaded() && !loaded) {
91+
VReport(1,
92+
"%s: library '%s' that was matched against called_from_lib"
93+
" suppression '%s' is unloaded\n",
94+
SanitizerToolName, lib->name, lib->templ);
95+
// The library is unloaded so mark the ignored code range as unloaded.
96+
ignored_code_ranges_[lib->range_id].OnUnload();
97+
lib->range_id = kInvalidCodeRangeId;
9598
}
9699
}
97100

@@ -110,8 +113,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
110113
const uptr idx =
111114
atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
112115
CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
113-
instrumented_code_ranges_[idx].begin = range.beg;
114-
instrumented_code_ranges_[idx].end = range.end;
116+
instrumented_code_ranges_[idx].OnLoad(range.beg, range.end);
115117
atomic_store(&instrumented_ranges_count_, idx + 1,
116118
memory_order_release);
117119
}

compiler-rt/lib/sanitizer_common/sanitizer_libignore.h

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,36 @@ class LibIgnore {
4949
bool IsPcInstrumented(uptr pc) const;
5050

5151
private:
52+
static const uptr kMaxIgnoredRanges = 128;
53+
static const uptr kMaxInstrumentedRanges = 1024;
54+
static const uptr kMaxLibs = 1024;
55+
static const uptr kInvalidCodeRangeId = -1;
56+
5257
struct Lib {
5358
char *templ;
5459
char *name;
5560
char *real_name; // target of symlink
56-
bool loaded;
61+
uptr range_id;
62+
bool loaded() const { return range_id != kInvalidCodeRangeId; };
5763
};
5864

5965
struct LibCodeRange {
60-
uptr begin;
61-
uptr end;
62-
};
66+
bool IsInRange(uptr pc) const {
67+
return (pc >= begin && pc < atomic_load(&end, memory_order_acquire));
68+
}
6369

64-
inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
65-
return (pc >= range.begin && pc < range.end);
66-
}
70+
void OnLoad(uptr b, uptr e) {
71+
begin = b;
72+
atomic_store(&end, e, memory_order_release);
73+
}
6774

68-
static const uptr kMaxIgnoredRanges = 128;
69-
static const uptr kMaxInstrumentedRanges = 1024;
70-
static const uptr kMaxLibs = 1024;
75+
void OnUnload() { atomic_store(&end, 0, memory_order_release); }
76+
77+
private:
78+
uptr begin;
79+
// A value of 0 means the associated module was unloaded.
80+
atomic_uintptr_t end;
81+
};
7182

7283
// Hot part:
7384
atomic_uintptr_t ignored_ranges_count_;
@@ -90,7 +101,7 @@ class LibIgnore {
90101
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
91102
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
92103
for (uptr i = 0; i < n; i++) {
93-
if (IsInRange(pc, ignored_code_ranges_[i])) {
104+
if (ignored_code_ranges_[i].IsInRange(pc)) {
94105
*pc_in_ignored_lib = true;
95106
return true;
96107
}
@@ -104,7 +115,7 @@ inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
104115
inline bool LibIgnore::IsPcInstrumented(uptr pc) const {
105116
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
106117
for (uptr i = 0; i < n; i++) {
107-
if (IsInRange(pc, instrumented_code_ranges_[i]))
118+
if (instrumented_code_ranges_[i].IsInRange(pc))
108119
return true;
109120
}
110121
return false;

compiler-rt/test/tsan/ignore_lib3.cpp

Lines changed: 21 additions & 9 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,30 @@
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
39-
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

0 commit comments

Comments
 (0)