summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKamil Rytarowski <n54@gmx.com>2017-11-08 22:34:17 +0000
committerKamil Rytarowski <n54@gmx.com>2017-11-08 22:34:17 +0000
commit43930933f7e78b57276ddda133ef1952398a882c (patch)
tree6179df280fb080259cd347ba8db4d0f1ee53358c /lib
parent15532ad8e9d85186c2d96135ca45fff893ee8cd7 (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.cc57
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;
}