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
This commit is contained in:
Dmitry Vyukov 2016-05-06 19:35:22 +00:00
parent c46f7d1883
commit 144eafd9ee
8 changed files with 82 additions and 0 deletions

View File

@ -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) {

View File

@ -20,6 +20,7 @@ namespace __tsan {
const uptr kDefaultAlignment = 16;
void InitializeAllocator();
void InitializeAllocatorLate();
void ReplaceSystemMalloc();
void AllocatorProcStart(Processor *proc);
void AllocatorProcFinish(Processor *proc);

View File

@ -43,6 +43,7 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
/*11 MutexTypeDDetector*/ {},
/*12 MutexTypeFired*/ {MutexTypeLeaf},
/*13 MutexTypeRacy*/ {MutexTypeLeaf},
/*14 MutexTypeGlobalProc*/ {},
};
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];

View File

@ -34,6 +34,7 @@ enum MutexType {
MutexTypeDDetector,
MutexTypeFired,
MutexTypeRacy,
MutexTypeGlobalProc,
// This must be the last.
MutexTypeCount

View File

@ -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);

View File

@ -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++)

View File

@ -173,6 +173,7 @@ enum StatType {
StatMtxFired,
StatMtxRacy,
StatMtxFD,
StatMtxGlobalProc,
// This must be the last.
StatCnt

View File

@ -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
}