forked from OSchip/llvm-project
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:
parent
c46f7d1883
commit
144eafd9ee
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace __tsan {
|
|||
const uptr kDefaultAlignment = 16;
|
||||
|
||||
void InitializeAllocator();
|
||||
void InitializeAllocatorLate();
|
||||
void ReplaceSystemMalloc();
|
||||
void AllocatorProcStart(Processor *proc);
|
||||
void AllocatorProcFinish(Processor *proc);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
|
|||
/*11 MutexTypeDDetector*/ {},
|
||||
/*12 MutexTypeFired*/ {MutexTypeLeaf},
|
||||
/*13 MutexTypeRacy*/ {MutexTypeLeaf},
|
||||
/*14 MutexTypeGlobalProc*/ {},
|
||||
};
|
||||
|
||||
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ enum MutexType {
|
|||
MutexTypeDDetector,
|
||||
MutexTypeFired,
|
||||
MutexTypeRacy,
|
||||
MutexTypeGlobalProc,
|
||||
|
||||
// This must be the last.
|
||||
MutexTypeCount
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ enum StatType {
|
|||
StatMtxFired,
|
||||
StatMtxRacy,
|
||||
StatMtxFD,
|
||||
StatMtxGlobalProc,
|
||||
|
||||
// This must be the last.
|
||||
StatCnt
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue