summaryrefslogtreecommitdiffstats
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
commit144eafd9ee88eb1ec80c4beb82e95a134ad0738c (patch)
tree29aaed0bde573a69eb691de52cac4465e5bfdfbe
parentc46f7d1883144b3c94f008ed902d6bc4c81a21f8 (diff)
downloadbcm5719-llvm-144eafd9ee88eb1ec80c4beb82e95a134ad0738c.tar.gz
bcm5719-llvm-144eafd9ee88eb1ec80c4beb82e95a134ad0738c.zip
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. llvm-svn: 268782
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_mman.cc37
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_mman.h1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_mutex.cc1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_mutex.h1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_rtl.cc1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_stat.cc1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_stat.h1
-rw-r--r--compiler-rt/test/tsan/pthread_key.cc39
8 files changed, 82 insertions, 0 deletions
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cc b/compiler-rt/lib/tsan/rtl/tsan_mman.cc
index 038407f0749..d3af9a08c19 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cc
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_mman.h b/compiler-rt/lib/tsan/rtl/tsan_mman.h
index cc9b0fa3cc6..8cdeeb35aa6 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.h
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_mutex.cc b/compiler-rt/lib/tsan/rtl/tsan_mutex.cc
index 9dd24803b18..22afefce338 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mutex.cc
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_mutex.h b/compiler-rt/lib/tsan/rtl/tsan_mutex.h
index 27f55385c95..22ee2f33fcc 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mutex.h
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc
index f19a2a0ab9f..865b726def4 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_stat.cc b/compiler-rt/lib/tsan/rtl/tsan_stat.cc
index a5cca967958..d1d6ed24d99 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_stat.cc
+++ b/compiler-rt/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/compiler-rt/lib/tsan/rtl/tsan_stat.h b/compiler-rt/lib/tsan/rtl/tsan_stat.h
index 8ea32048e14..8447dd84fc1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_stat.h
+++ b/compiler-rt/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/compiler-rt/test/tsan/pthread_key.cc b/compiler-rt/test/tsan/pthread_key.cc
new file mode 100644
index 00000000000..798caa4aba8
--- /dev/null
+++ b/compiler-rt/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
+}
OpenPOWER on IntegriCloud