tsan: fix and test detection of TLS races
Currently detection of races with TLS/stack initialization is broken because we imitate the write before thread initialization, so it's modelled with a wrong thread/epoch. Fix that and add a test. Reviewed By: melver Differential Revision: https://reviews.llvm.org/D110538
This commit is contained in:
parent
bf980930e5
commit
b4c1e5cb73
|
@ -453,6 +453,8 @@ static void InitializeLongjmpXorKey() {
|
|||
}
|
||||
#endif
|
||||
|
||||
extern "C" void __tsan_tls_initialization() {}
|
||||
|
||||
void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
|
||||
// Check that the thr object is in tls;
|
||||
const uptr thr_beg = (uptr)thr;
|
||||
|
@ -462,9 +464,10 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
|
|||
CHECK_GE(thr_end, tls_addr);
|
||||
CHECK_LE(thr_end, tls_addr + tls_size);
|
||||
// Since the thr object is huge, skip it.
|
||||
MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, thr_beg - tls_addr);
|
||||
MemoryRangeImitateWrite(thr, /*pc=*/2, thr_end,
|
||||
tls_addr + tls_size - thr_end);
|
||||
const uptr pc = StackTrace::GetNextInstructionPc(
|
||||
reinterpret_cast<uptr>(__tsan_tls_initialization));
|
||||
MemoryRangeImitateWrite(thr, pc, tls_addr, thr_beg - tls_addr);
|
||||
MemoryRangeImitateWrite(thr, pc, thr_end, tls_addr + tls_size - thr_end);
|
||||
}
|
||||
|
||||
// Note: this function runs with async signals enabled,
|
||||
|
|
|
@ -156,13 +156,6 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
|
|||
if (thread_type != ThreadType::Fiber)
|
||||
GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr,
|
||||
&tls_size);
|
||||
|
||||
if (tid != kMainTid) {
|
||||
if (stk_addr && stk_size)
|
||||
MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size);
|
||||
|
||||
if (tls_addr && tls_size) ImitateTlsWrite(thr, tls_addr, tls_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
ThreadRegistry *tr = &ctx->thread_registry;
|
||||
|
@ -178,6 +171,16 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
|
|||
ThreadIgnoreSyncBegin(thr, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_GO
|
||||
if (tid != kMainTid) {
|
||||
if (stk_addr && stk_size)
|
||||
MemoryRangeImitateWrite(thr, /*pc=*/1, stk_addr, stk_size);
|
||||
|
||||
if (tls_addr && tls_size)
|
||||
ImitateTlsWrite(thr, tls_addr, tls_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadContext::OnStarted(void *arg) {
|
||||
|
|
35
compiler-rt/test/tsan/tls_race3.cpp
Normal file
35
compiler-rt/test/tsan/tls_race3.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
// RUN: %clangxx_tsan %darwin_min_target_with_tls_support -O1 %s -o %t && \
|
||||
// RUN: %deflake %run %t | FileCheck %s
|
||||
|
||||
// Race with initial TLS initialization:
|
||||
// there is no explicit second write,
|
||||
// but the TLS variable is published unsafely.
|
||||
#include "test.h"
|
||||
|
||||
__thread long X;
|
||||
long *P;
|
||||
|
||||
void *Thread(void *a) {
|
||||
__atomic_store_n(&P, &X, __ATOMIC_RELAXED);
|
||||
barrier_wait(&barrier);
|
||||
barrier_wait(&barrier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
barrier_init(&barrier, 2);
|
||||
pthread_t t;
|
||||
pthread_create(&t, NULL, Thread, NULL);
|
||||
barrier_wait(&barrier);
|
||||
long *p = __atomic_load_n(&P, __ATOMIC_RELAXED);
|
||||
*p = 42;
|
||||
barrier_wait(&barrier);
|
||||
pthread_join(t, 0);
|
||||
}
|
||||
|
||||
// CHECK: WARNING: ThreadSanitizer: data race
|
||||
// CHECK: Write of size 8 at {{.*}} by main thread:
|
||||
// CHECK: #0 main
|
||||
// CHECK: Previous write of size 8 at {{.*}} by thread T1:
|
||||
// CHECK: #0 __tsan_tls_initialization
|
||||
// CHECK: Location is TLS of thread T1.
|
Loading…
Reference in a new issue