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