summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-05-06 19:35:22 +0000
committerDmitry Vyukov <dvyukov@google.com>2016-05-06 19:35:22 +0000
commita7f0947760499f1a12d966e7ff307c8b71bafc93 (patch)
tree8a146b02b42142dd7bd5884fe5c3ce42c5d03314
parentf859b82b71d49dc61b7a1123766f28b5098490c1 (diff)
tsan: fix a crash
Fixes crash reported in: https://bugs.chromium.org/p/v8/issues/detail?id=4995 The problem is that we don't have a processor in a free interceptor during thread exit. The crash was introduced by introduction of Processors. However, previously we silently leaked memory which wasn't any better. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@268782 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/tsan/rtl/tsan_mman.cc37
-rw-r--r--lib/tsan/rtl/tsan_mman.h1
-rw-r--r--lib/tsan/rtl/tsan_mutex.cc1
-rw-r--r--lib/tsan/rtl/tsan_mutex.h1
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc1
-rw-r--r--lib/tsan/rtl/tsan_stat.cc1
-rw-r--r--lib/tsan/rtl/tsan_stat.h1
-rw-r--r--test/tsan/pthread_key.cc39
8 files changed, 82 insertions, 0 deletions
diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc
index 038407f07..d3af9a08c 100644
--- a/lib/tsan/rtl/tsan_mman.cc
+++ b/lib/tsan/rtl/tsan_mman.cc
@@ -63,10 +63,29 @@ Allocator *allocator() {
return reinterpret_cast<Allocator*>(&allocator_placeholder);
}
+struct GlobalProc {
+ Mutex mtx;
+ Processor *proc;
+
+ GlobalProc()
+ : mtx(MutexTypeGlobalProc, StatMtxGlobalProc)
+ , proc(ProcCreate()) {
+ }
+};
+
+static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64);
+GlobalProc *global_proc() {
+ return reinterpret_cast<GlobalProc*>(&global_proc_placeholder);
+}
+
void InitializeAllocator() {
allocator()->Init(common_flags()->allocator_may_return_null);
}
+void InitializeAllocatorLate() {
+ new(global_proc()) GlobalProc();
+}
+
void AllocatorProcStart(Processor *proc) {
allocator()->InitCache(&proc->alloc_cache);
internal_allocator()->InitCache(&proc->internal_alloc_cache);
@@ -118,11 +137,29 @@ void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) {
}
void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
+ GlobalProc *gp = nullptr;
+ if (thr->proc() == nullptr) {
+ // If we don't have a proc, use the global one.
+ // There is currently only one known case where this path is triggered:
+ // __interceptor_free
+ // __nptl_deallocate_tsd
+ // start_thread
+ // clone
+ // Ideally, we destroy thread state (and unwire proc) when a thread actually
+ // exits (i.e. when we join/wait it). Then we would not need the global proc
+ gp = global_proc();
+ gp->mtx.Lock();
+ ProcWire(gp->proc, thr);
+ }
if (ctx && ctx->initialized)
OnUserFree(thr, pc, (uptr)p, true);
allocator()->Deallocate(&thr->proc()->alloc_cache, p);
if (signal)
SignalUnsafeCall(thr, pc);
+ if (gp) {
+ ProcUnwire(gp->proc, thr);
+ gp->mtx.Unlock();
+ }
}
void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h
index cc9b0fa3c..8cdeeb35a 100644
--- a/lib/tsan/rtl/tsan_mman.h
+++ b/lib/tsan/rtl/tsan_mman.h
@@ -20,6 +20,7 @@ namespace __tsan {
const uptr kDefaultAlignment = 16;
void InitializeAllocator();
+void InitializeAllocatorLate();
void ReplaceSystemMalloc();
void AllocatorProcStart(Processor *proc);
void AllocatorProcFinish(Processor *proc);
diff --git a/lib/tsan/rtl/tsan_mutex.cc b/lib/tsan/rtl/tsan_mutex.cc
index 9dd24803b..22afefce3 100644
--- a/lib/tsan/rtl/tsan_mutex.cc
+++ b/lib/tsan/rtl/tsan_mutex.cc
@@ -43,6 +43,7 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
/*11 MutexTypeDDetector*/ {},
/*12 MutexTypeFired*/ {MutexTypeLeaf},
/*13 MutexTypeRacy*/ {MutexTypeLeaf},
+ /*14 MutexTypeGlobalProc*/ {},
};
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
diff --git a/lib/tsan/rtl/tsan_mutex.h b/lib/tsan/rtl/tsan_mutex.h
index 27f55385c..22ee2f33f 100644
--- a/lib/tsan/rtl/tsan_mutex.h
+++ b/lib/tsan/rtl/tsan_mutex.h
@@ -34,6 +34,7 @@ enum MutexType {
MutexTypeDDetector,
MutexTypeFired,
MutexTypeRacy,
+ MutexTypeGlobalProc,
// This must be the last.
MutexTypeCount
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index f19a2a0ab..865b726de 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -341,6 +341,7 @@ void Initialize(ThreadState *thr) {
InitializeDynamicAnnotations();
#ifndef SANITIZER_GO
InitializeShadowMemory();
+ InitializeAllocatorLate();
#endif
// Setup correct file descriptor for error reports.
__sanitizer_set_report_path(common_flags()->log_path);
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index a5cca9679..d1d6ed24d 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -168,6 +168,7 @@ void StatOutput(u64 *stat) {
name[StatMtxFired] = " FiredSuppressions ";
name[StatMtxRacy] = " RacyStacks ";
name[StatMtxFD] = " FD ";
+ name[StatMtxGlobalProc] = " GlobalProc ";
Printf("Statistics:\n");
for (int i = 0; i < StatCnt; i++)
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
index 8ea32048e..8447dd84f 100644
--- a/lib/tsan/rtl/tsan_stat.h
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -173,6 +173,7 @@ enum StatType {
StatMtxFired,
StatMtxRacy,
StatMtxFD,
+ StatMtxGlobalProc,
// This must be the last.
StatCnt
diff --git a/test/tsan/pthread_key.cc b/test/tsan/pthread_key.cc
new file mode 100644
index 000000000..798caa4ab
--- /dev/null
+++ b/test/tsan/pthread_key.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// Extracted from:
+// https://bugs.chromium.org/p/v8/issues/detail?id=4995
+
+#include "test.h"
+
+void* thr(void* arg) {
+ const int N = 32;
+ pthread_key_t keys_[N];
+ for (size_t i = 0; i < N; ++i) {
+ int err = pthread_key_create(&keys_[i], 0);
+ if (err) {
+ fprintf(stderr, "pthread_key_create failed with %d\n", err);
+ exit(1);
+ }
+ }
+ for (size_t i = 0; i < N; i++)
+ pthread_setspecific(keys_[i], (void*)(long)i);
+ for (size_t i = 0; i < N; i++)
+ pthread_key_delete(keys_[i]);
+ return 0;
+}
+
+int main() {
+ for (int i = 0; i < 10; i++) {
+ pthread_t th;
+ pthread_create(&th, 0, thr, 0);
+ pthread_join(th, 0);
+ }
+ pthread_t th[2];
+ pthread_create(&th[0], 0, thr, 0);
+ pthread_create(&th[1], 0, thr, 0);
+ pthread_join(th[0], 0);
+ pthread_join(th[1], 0);
+ fprintf(stderr, "DONE\n");
+ // CHECK: DONE
+}