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 == 0) 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 @@ -265,7 +265,7 @@ return ctx; } -#if !SANITIZER_MAC +#if !SANITIZER_MAC && !SANITIZER_NETBSD static unsigned g_thread_finalize_key; #endif @@ -391,7 +391,28 @@ void *arg; }; -static void at_exit_wrapper(void *arg) { +static struct AtExitCtx **AtExitStack; +static int AtExitStackSize; + +static void at_exit_wrapper() { + ThreadState *thr = cur_thread(); + uptr pc = 0; + + // 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(thr, pc, (uptr)ctx); + ((void(*)())ctx->f)(); + InternalFree(ctx); +} + +static void cxa_at_exit_wrapper(void *arg) { ThreadState *thr = cur_thread(); uptr pc = 0; Acquire(thr, pc, (uptr)arg); @@ -430,12 +451,29 @@ // 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 == 0) { + // 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 == 0) { + ++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; } -#if !SANITIZER_MAC +#if !SANITIZER_MAC && !SANITIZER_NETBSD static void on_exit_wrapper(int status, void *arg) { ThreadState *thr = cur_thread(); uptr pc = 0; @@ -460,6 +498,9 @@ ThreadIgnoreEnd(thr, pc); return res; } +#define TSAN_MAYBE_INTERCEPT_ON_EXIT TSAN_INTERCEPT(on_exit) +#else +#define TSAN_MAYBE_INTERCEPT_ON_EXIT #endif // Cleanup old bufs. @@ -866,7 +907,7 @@ } } // namespace __tsan -#if !SANITIZER_MAC +#if !SANITIZER_MAC && !SANITIZER_NETBSD static void thread_finalize(void *v) { uptr iter = (uptr)v; if (iter > 1) { @@ -896,7 +937,7 @@ ThreadState *thr = cur_thread(); // Thread-local state is not initialized yet. ScopedIgnoreInterceptors ignore; -#if !SANITIZER_MAC +#if !SANITIZER_MAC && !SANITIZER_NETBSD ThreadIgnoreBegin(thr, 0); if (pthread_setspecific(g_thread_finalize_key, (void *)GetPthreadDestructorIterations())) { @@ -2445,6 +2486,17 @@ } #endif +#if SANITIZER_NETBSD +TSAN_INTERCEPTOR(void, _lwp_exit) { + SCOPED_TSAN_INTERCEPTOR(_lwp_exit); + REAL(_lwp_exit)(); + DestroyThreadState(); +} +#define TSAN_MAYBE_INTERCEPT__LWP_EXIT TSAN_INTERCEPT(_lwp_exit) +#else +#define TSAN_MAYBE_INTERCEPT__LWP_EXIT +#endif + namespace __tsan { static void finalize(void *arg) { @@ -2605,7 +2657,7 @@ #if !SANITIZER_ANDROID TSAN_INTERCEPT(dl_iterate_phdr); #endif - TSAN_INTERCEPT(on_exit); + TSAN_MAYBE_INTERCEPT_ON_EXIT; TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); @@ -2613,6 +2665,8 @@ TSAN_INTERCEPT(__tls_get_addr); #endif + TSAN_MAYBE_INTERCEPT__LWP_EXIT; + #if !SANITIZER_MAC && !SANITIZER_ANDROID // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -2624,7 +2678,7 @@ Die(); } -#if !SANITIZER_MAC +#if !SANITIZER_MAC && !SANITIZER_NETBSD if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Printf("ThreadSanitizer: failed to create thread key\n"); Die();