[compiler-rt] r202400 - tsan: intercept vfork

Dmitry Vyukov dvyukov at google.com
Thu Feb 27 06:36:16 PST 2014


Author: dvyukov
Date: Thu Feb 27 08:36:16 2014
New Revision: 202400

URL: http://llvm.org/viewvc/llvm-project?rev=202400&view=rev
Log:
tsan: intercept vfork
this fixes obscure false positives
see the comments and the test for details


Added:
    compiler-rt/trunk/test/tsan/vfork.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=202400&r1=202399&r2=202400&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc Thu Feb 27 08:36:16 2014
@@ -1903,6 +1903,23 @@ TSAN_INTERCEPTOR(int, fork, int fake) {
   return pid;
 }
 
+TSAN_INTERCEPTOR(int, vfork, int fake) {
+  // Some programs (e.g. openjdk) call close for all file descriptors
+  // in the child process. Under tsan it leads to false positives, because
+  // address space is shared, so the parent process also thinks that
+  // the descriptors are closed (while they are actually not).
+  // This leads to false positives due to missed synchronization.
+  // Strictly saying this is undefined behavior, because vfork child is not
+  // allowed to call any functions other than exec/exit. But this is what
+  // openjdk does, so we want to handle it.
+  // We could disable interceptors in the child process. But it's not possible
+  // to simply intercept and wrap vfork, because vfork child is not allowed
+  // to return from the function that calls vfork, and that's exactly what
+  // we would do. So this would require some assembly trickery as well.
+  // Instead we simply turn vfork into fork.
+  return WRAP(fork)(fake);
+}
+
 static int OnExit(ThreadState *thr) {
   int status = Finalize(thr);
   REAL(fflush)(0);
@@ -2289,6 +2306,7 @@ void InitializeInterceptors() {
   TSAN_INTERCEPT(munlockall);
 
   TSAN_INTERCEPT(fork);
+  TSAN_INTERCEPT(vfork);
   TSAN_INTERCEPT(dlopen);
   TSAN_INTERCEPT(dlclose);
   TSAN_INTERCEPT(on_exit);

Added: compiler-rt/trunk/test/tsan/vfork.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/vfork.cc?rev=202400&view=auto
==============================================================================
--- compiler-rt/trunk/test/tsan/vfork.cc (added)
+++ compiler-rt/trunk/test/tsan/vfork.cc Thu Feb 27 08:36:16 2014
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+int fds[2];
+int X;
+
+void *Thread1(void *x) {
+  X = 42;
+  write(fds[1], "a", 1);
+  return NULL;
+}
+
+void *Thread2(void *x) {
+  char buf;
+  while (read(fds[0], &buf, 1) != 1) {
+  }
+  X = 43;
+  return NULL;
+}
+
+int main() {
+  pipe(fds);
+  int pid = vfork();
+  if (pid < 0) {
+    printf("FAIL to vfork\n");
+    exit(1);
+  }
+  if (pid == 0) {  // child
+    // Closing of fds must not affect parent process.
+    // Strictly saying this is undefined behavior, because vfork child is not
+    // allowed to call any functions other than exec/exit. But this is what
+    // openjdk does.
+    close(fds[0]);
+    close(fds[1]);
+    _exit(0);
+  }
+  pthread_t t[2];
+  pthread_create(&t[0], NULL, Thread1, NULL);
+  pthread_create(&t[1], NULL, Thread2, NULL);
+  pthread_join(t[0], NULL);
+  pthread_join(t[1], NULL);
+  printf("DONE\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAIL to vfork
+// CHECK: DONE





More information about the llvm-commits mailing list