[compiler-rt] r317735 - Correct atexit(3) support in TSan/NetBSD

Kamil Rytarowski via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 8 14:34:17 PST 2017


Author: kamil
Date: Wed Nov  8 14:34:17 2017
New Revision: 317735

URL: http://llvm.org/viewvc/llvm-project?rev=317735&view=rev
Log:
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

Added:
    compiler-rt/trunk/test/tsan/atexit3.cc
Modified:
    compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc?rev=317735&r1=317734&r2=317735&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc Wed Nov  8 14:34:17 2017
@@ -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);
+
+    // 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();
+  }
 
-static void at_exit_wrapper(void *arg) {
-  ThreadState *thr = cur_thread();
-  uptr pc = 0;
-  Acquire(thr, pc, (uptr)arg);
+  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(ThreadS
   // 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;
 }

Added: compiler-rt/trunk/test/tsan/atexit3.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/atexit3.cc?rev=317735&view=auto
==============================================================================
--- compiler-rt/trunk/test/tsan/atexit3.cc (added)
+++ compiler-rt/trunk/test/tsan/atexit3.cc Wed Nov  8 14:34:17 2017
@@ -0,0 +1,41 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+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




More information about the llvm-commits mailing list