Skip to content

Commit 4e031a9

Browse files
committed
[sanitizer] Fix asserts in asan and tsan in pthread interceptors.
Calling one of pthread join/detach interceptor on an already joined/detached thread causes asserts such as: AddressSanitizer: CHECK failed: sanitizer_thread_arg_retval.cpp:56 "((t)) != (0)" (0x0, 0x0) (tid=1236094) #0 0x555555634f8b in __asan::CheckUnwind() compiler-rt/lib/asan/asan_rtl.cpp:69:3 #1 0x55555564e06e in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp:86:24 #2 0x5555556491df in __sanitizer::ThreadArgRetval::BeforeJoin(unsigned long) const compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp:56:3 #3 0x5555556198ed in Join<___interceptor_pthread_tryjoin_np(void*, void**)::<lambda()> > compiler-rt/lib/asan/../sanitizer_common/sanitizer_thread_arg_retval.h:74:26 #4 0x5555556198ed in pthread_tryjoin_np compiler-rt/lib/asan/asan_interceptors.cpp:311:29 This change does 2 things: - Introduce a detect_invalid_join common flags - Replace CHECK assert by Report() and Die()
1 parent f2464ca commit 4e031a9

File tree

5 files changed

+79
-6
lines changed

5 files changed

+79
-6
lines changed

Diff for: compiler-rt/lib/sanitizer_common/sanitizer_flags.inc

+3
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,6 @@ COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false,
279279
COMMON_FLAG(bool, enable_symbolizer_markup, SANITIZER_FUCHSIA,
280280
"Use sanitizer symbolizer markup, available on Linux "
281281
"and always set true for Fuchsia.")
282+
283+
COMMON_FLAG(bool, detect_invalid_join, true,
284+
"If set, check invalid joins of threads.")

Diff for: compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp

+19-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ void ThreadArgRetval::CreateLocked(uptr thread, bool detached,
2323
Data& t = data_[thread];
2424
t = {};
2525
t.gen = gen_++;
26+
static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX);
27+
if (gen_ == kInvalidGen)
28+
gen_ = 0;
2629
t.detached = detached;
2730
t.args = args;
2831
}
@@ -53,16 +56,28 @@ void ThreadArgRetval::Finish(uptr thread, void* retval) {
5356
u32 ThreadArgRetval::BeforeJoin(uptr thread) const {
5457
__sanitizer::Lock lock(&mtx_);
5558
auto t = data_.find(thread);
56-
CHECK(t);
57-
CHECK(!t->second.detached);
58-
return t->second.gen;
59+
if (t && !t->second.detached) {
60+
return t->second.gen;
61+
}
62+
if (!common_flags()->detect_invalid_join)
63+
return kInvalidGen;
64+
const char* reason = "unknown";
65+
if (!t) {
66+
reason = "already joined";
67+
} else if (t->second.detached) {
68+
reason = "detached";
69+
}
70+
Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName,
71+
reason);
72+
Die();
5973
}
6074

6175
void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) {
6276
__sanitizer::Lock lock(&mtx_);
6377
auto t = data_.find(thread);
6478
if (!t || gen != t->second.gen) {
65-
// Thread was reused and erased by any other event.
79+
// Thread was reused and erased by any other event, or we had an invalid
80+
// join.
6681
return;
6782
}
6883
CHECK(!t->second.detached);

Diff for: compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class SANITIZER_MUTEX ThreadArgRetval {
9393
// will keep pointers alive forever, missing leaks caused by cancelation.
9494

9595
private:
96+
static const u32 kInvalidGen = UINT32_MAX;
9697
struct Data {
9798
Args args;
9899
u32 gen; // Avoid collision if thread id re-used.

Diff for: compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// RUN: %clangxx -pthread %s -o %t
2-
// RUN: %run %t 0
2+
// RUN: %env_tool_opts=detect_invalid_join=false %run %t 0
33

44
// FIXME: Crashes on some bots in pthread_exit.
5-
// RUN: %run %t %if tsan %{ 0 %} %else %{ 1 %}
5+
// RUN: %env_tool_opts=detect_invalid_join=false %run %t %if tsan %{ 0 %} %else %{ 1 %}
66

77
// REQUIRES: glibc
88

99
#include <assert.h>
1010
#include <ctime>
11+
#include <errno.h>
1112
#include <pthread.h>
1213
#include <stdint.h>
1314
#include <stdlib.h>
@@ -24,6 +25,7 @@ static void *fn(void *args) {
2425

2526
int main(int argc, char **argv) {
2627
use_exit = atoi(argv[1]);
28+
bool check_invalid_join = !use_exit;
2729
pthread_t thread[5];
2830
assert(!pthread_create(&thread[0], nullptr, fn, (void *)1000));
2931
assert(!pthread_create(&thread[1], nullptr, fn, (void *)1001));
@@ -42,6 +44,8 @@ int main(int argc, char **argv) {
4244
while (pthread_tryjoin_np(thread[1], &res))
4345
sleep(1);
4446
assert(~(uintptr_t)res == 1001);
47+
assert(check_invalid_join ||
48+
(pthread_tryjoin_np(thread[1], &res) == EBUSY));
4549
}
4650

4751
{
@@ -50,12 +54,15 @@ int main(int argc, char **argv) {
5054
while (pthread_timedjoin_np(thread[2], &res, &tm))
5155
sleep(1);
5256
assert(~(uintptr_t)res == 1002);
57+
assert(check_invalid_join ||
58+
(pthread_timedjoin_np(thread[2], &res, &tm) == ESRCH));
5359
}
5460

5561
{
5662
void *res;
5763
assert(!pthread_join(thread[3], &res));
5864
assert(~(uintptr_t)res == 1003);
65+
assert(check_invalid_join || (pthread_join(thread[3], &res) == ESRCH));
5966
}
6067

6168
return 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clangxx -pthread %s -o %t
2+
3+
// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 0 2>&1 | FileCheck %s
4+
// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 1 2>&1 | FileCheck %s
5+
// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 2 2>&1 | FileCheck %s
6+
// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 3 2>&1 | FileCheck %s --check-prefix=DETACH
7+
8+
// REQUIRES: glibc && (asan || hwasan || lsan)
9+
10+
#include <assert.h>
11+
#include <ctime>
12+
#include <errno.h>
13+
#include <pthread.h>
14+
#include <stdint.h>
15+
#include <stdlib.h>
16+
#include <unistd.h>
17+
18+
static void *fn(void *args) {
19+
sleep(1);
20+
return nullptr;
21+
}
22+
23+
int main(int argc, char **argv) {
24+
int n = atoi(argv[1]);
25+
pthread_t thread;
26+
assert(!pthread_create(&thread, nullptr, fn, nullptr));
27+
void *res;
28+
if (n == 0) {
29+
while (pthread_tryjoin_np(thread, &res))
30+
sleep(1);
31+
pthread_tryjoin_np(thread, &res);
32+
} else if (n == 1) {
33+
timespec tm = {0, 1};
34+
while (pthread_timedjoin_np(thread, &res, &tm))
35+
sleep(1);
36+
pthread_timedjoin_np(thread, &res, &tm);
37+
} else if (n == 2) {
38+
assert(!pthread_join(thread, &res));
39+
pthread_join(thread, &res);
40+
} else if (n == 3) {
41+
assert(!pthread_detach(thread));
42+
pthread_join(thread, &res);
43+
}
44+
// CHECK: Joining already joined thread
45+
// DETACH: Joining detached thread
46+
return 0;
47+
}

0 commit comments

Comments
 (0)