diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2016-05-06 19:35:22 +0000 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2016-05-06 19:35:22 +0000 |
commit | a7f0947760499f1a12d966e7ff307c8b71bafc93 (patch) | |
tree | 8a146b02b42142dd7bd5884fe5c3ce42c5d03314 | |
parent | f859b82b71d49dc61b7a1123766f28b5098490c1 (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.cc | 37 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_mman.h | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_mutex.cc | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_mutex.h | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_rtl.cc | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_stat.cc | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_stat.h | 1 | ||||
-rw-r--r-- | test/tsan/pthread_key.cc | 39 |
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 +} |