Index: lib/sanitizer_common/sanitizer_allocator.cc =================================================================== --- lib/sanitizer_common/sanitizer_allocator.cc +++ lib/sanitizer_common/sanitizer_allocator.cc @@ -180,6 +180,25 @@ // LowLevelAllocator static LowLevelAllocateCallback low_level_alloc_callback; +int InternalReallocArr(void *addr, uptr count, uptr size, + InternalAllocatorCache *cache) { + void *oaddr; + void *naddr = 0; + + internal_memcpy(&oaddr, addr, sizeof(addr)); + if (!count || !size) { + InternalFree(oaddr, cache); + internal_memcpy(addr, &naddr, sizeof(addr)); + return 0; + } + + naddr = InternalRealloc(oaddr, count * size, cache); + if (!naddr) return -1; + + internal_memcpy(addr, &naddr, sizeof(addr)); + return 0; +} + void *LowLevelAllocator::Allocate(uptr size) { // Align allocation size. size = RoundUpTo(size, 8); Index: lib/sanitizer_common/sanitizer_allocator_internal.h =================================================================== --- lib/sanitizer_common/sanitizer_allocator_internal.h +++ lib/sanitizer_common/sanitizer_allocator_internal.h @@ -57,6 +57,8 @@ void *InternalCalloc(uptr countr, uptr size, InternalAllocatorCache *cache = nullptr); void InternalFree(void *p, InternalAllocatorCache *cache = nullptr); +int InternalReallocArr(void *p, uptr count, uptr size, + InternalAllocatorCache *cache = nullptr); InternalAllocator *internal_allocator(); enum InternalAllocEnum { Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -391,10 +391,26 @@ void *arg; }; -static void at_exit_wrapper(void *arg) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - Acquire(thr, pc, (uptr)arg); +static struct AtExitCtx **AtExitStack; +static int AtExitStackSize; + +static void at_exit_wrapper() { + // Pop AtExitCtx from the top of the stack of callback functions + AtExitCtx *ctx = AtExitStack[AtExitStackSize - 1]; + --AtExitStackSize; + if (InternalReallocArr(&AtExitStack, AtExitStackSize, + sizeof(struct AtExitCtx *))) { + Printf("InternalReallocArr error"); + Die(); + } + + Acquire(cur_thread(), (uptr)0, (uptr)ctx); + ((void(*)())ctx->f)(); + InternalFree(ctx); +} + +static void cxa_at_exit_wrapper(void *arg) { + Acquire(cur_thread(), 0, (uptr)arg); AtExitCtx *ctx = (AtExitCtx*)arg; ((void(*)(void *arg))ctx->f)(ctx->arg); InternalFree(ctx); @@ -430,7 +446,24 @@ // Memory allocation in __cxa_atexit will race with free during exit, // because we do not see synchronization around atexit callback list. ThreadIgnoreBegin(thr, pc); - int res = REAL(__cxa_atexit)(at_exit_wrapper, ctx, dso); + int res; + if (!dso) { + // NetBSD does not preserve the 2nd argument if dso is equal to 0 + // Store ctx in a local stack-like structure + res = REAL(__cxa_atexit)((void (*)(void *a))at_exit_wrapper, 0, 0); + // Push AtExitCtx on the top of the stack of callback functions + if (!res) { + ++AtExitStackSize; + if (InternalReallocArr(&AtExitStack, AtExitStackSize, + sizeof(struct AtExitCtx *))) { + Printf("InternalReallocArr error"); + Die(); + } + AtExitStack[AtExitStackSize - 1] = ctx; + } + } else { + res = REAL(__cxa_atexit)(cxa_at_exit_wrapper, ctx, dso); + } ThreadIgnoreEnd(thr, pc); return res; } Index: test/tsan/atexit3.cc =================================================================== --- /dev/null +++ test/tsan/atexit3.cc @@ -0,0 +1,41 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include +#include + +static void atexit5() { + fprintf(stderr, "5"); +} + +static void atexit4() { + fprintf(stderr, "4"); +} + +static void atexit3() { + fprintf(stderr, "3"); +} + +static void atexit2() { + fprintf(stderr, "2"); +} + +static void atexit1() { + fprintf(stderr, "1"); +} + +static void atexit0() { + fprintf(stderr, "\n"); +} + +int main() { + atexit(atexit0); + atexit(atexit1); + atexit(atexit2); + atexit(atexit3); + atexit(atexit4); + atexit(atexit5); +} + +// CHECK-NOT: FATAL: ThreadSanitizer +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: 54321