diff options
author | Kamil Rytarowski <n54@gmx.com> | 2017-11-08 22:34:17 +0000 |
---|---|---|
committer | Kamil Rytarowski <n54@gmx.com> | 2017-11-08 22:34:17 +0000 |
commit | 43930933f7e78b57276ddda133ef1952398a882c (patch) | |
tree | 6179df280fb080259cd347ba8db4d0f1ee53358c /lib | |
parent | 15532ad8e9d85186c2d96135ca45fff893ee8cd7 (diff) |
Correct atexit(3) support in TSan/NetBSD
Summary:
The NetBSD specific implementation of cxa_atexit() does not
preserve the 2nd argument if dso is equal to NULL.
Changes:
- Split paths of handling intercepted __cxa_atexit() and atexit(3).
This affects all supported Operating Systems.
- Add a local stack-like structure to hold the __cxa_atexit() context.
atexit(3) is documented in the C standard as calling callback from the
earliest to the oldest entry. This path also fixes potential ABI
problem of passing an argument to a function from the atexit(3)
callback mechanism.
- Add new test to ensure LIFO style of atexit(3) callbacks: atexit3.cc
Proposal to change the behavior of __cxa_atexit() in NetBSD has been rejected.
With the above changes TSan/NetBSD with the current tsan_interceptors.cc
can bootstrap into operation.
Sponsored by <The NetBSD Foundation>
Reviewers: vitalybuka, dvyukov, joerg, kcc, eugenis
Reviewed By: dvyukov
Subscribers: kubamracek, llvm-commits, #sanitizers
Tags: #sanitizers
Differential Revision: https://reviews.llvm.org/D39619
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@317735 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/tsan/rtl/tsan_interceptors.cc | 57 |
1 files changed, 45 insertions, 12 deletions
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 976fe2967..e14283825 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -231,6 +231,13 @@ struct ThreadSignalContext { __sanitizer_sigset_t oldset; }; +// The sole reason tsan wraps atexit callbacks is to establish synchronization +// between callback setup and callback execution. +struct AtExitCtx { + void (*f)(); + void *arg; +}; + // InterceptorContext holds all global data required for interceptors. // It's explicitly constructed in InitializeInterceptors with placement new // and is never destroyed. This allows usage of members with non-trivial @@ -244,8 +251,11 @@ struct InterceptorContext { unsigned finalize_key; #endif + BlockingMutex atexit_mu; + Vector<struct AtExitCtx *> AtExitStack; + InterceptorContext() - : libignore(LINKER_INITIALIZED) { + : libignore(LINKER_INITIALIZED), AtExitStack(MBlockAtExit) { } }; @@ -398,17 +408,25 @@ TSAN_INTERCEPTOR(int, pause, int fake) { return BLOCK_REAL(pause)(fake); } -// The sole reason tsan wraps atexit callbacks is to establish synchronization -// between callback setup and callback execution. -struct AtExitCtx { - void (*f)(); - void *arg; -}; +static void at_exit_wrapper() { + AtExitCtx *ctx; + { + // Ensure thread-safety. + BlockingMutexLock l(&interceptor_ctx()->atexit_mu); -static void at_exit_wrapper(void *arg) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - Acquire(thr, pc, (uptr)arg); + // Pop AtExitCtx from the top of the stack of callback functions + uptr element = interceptor_ctx()->AtExitStack.Size() - 1; + ctx = interceptor_ctx()->AtExitStack[element]; + interceptor_ctx()->AtExitStack.PopBack(); + } + + 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); @@ -444,7 +462,22 @@ static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(), // 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 + + // Ensure thread-safety. + BlockingMutexLock l(&interceptor_ctx()->atexit_mu); + + 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) { + interceptor_ctx()->AtExitStack.PushBack(ctx); + } + } else { + res = REAL(__cxa_atexit)(cxa_at_exit_wrapper, ctx, dso); + } ThreadIgnoreEnd(thr, pc); return res; } |