[compiler-rt] r217140 - [msan] Make origin tracking fork-safe.
Evgeniy Stepanov
eugeni.stepanov at gmail.com
Thu Sep 4 03:36:14 PDT 2014
Author: eugenis
Date: Thu Sep 4 05:36:14 2014
New Revision: 217140
URL: http://llvm.org/viewvc/llvm-project?rev=217140&view=rev
Log:
[msan] Make origin tracking fork-safe.
Chained origins make plain memory stores async-signal-unsafe.
We already disable it inside signal handlers.
This change grabs all origin-related locks before fork() and releases
them after fork() to avoid a deadlock in the child process.
Added:
compiler-rt/trunk/test/msan/fork.cc (with props)
Modified:
compiler-rt/trunk/lib/msan/msan_chained_origin_depot.cc
compiler-rt/trunk/lib/msan/msan_chained_origin_depot.h
compiler-rt/trunk/lib/msan/msan_interceptors.cc
compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.cc
compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.h
compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepotbase.h
Modified: compiler-rt/trunk/lib/msan/msan_chained_origin_depot.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/msan/msan_chained_origin_depot.cc?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/msan/msan_chained_origin_depot.cc (original)
+++ compiler-rt/trunk/lib/msan/msan_chained_origin_depot.cc Thu Sep 4 05:36:14 2014
@@ -116,4 +116,12 @@ u32 ChainedOriginDepotGet(u32 id, u32 *o
return desc.here_id;
}
+void ChainedOriginDepotLockAll() {
+ chainedOriginDepot.LockAll();
+}
+
+void ChainedOriginDepotUnlockAll() {
+ chainedOriginDepot.UnlockAll();
+}
+
} // namespace __msan
Modified: compiler-rt/trunk/lib/msan/msan_chained_origin_depot.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/msan/msan_chained_origin_depot.h?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/msan/msan_chained_origin_depot.h (original)
+++ compiler-rt/trunk/lib/msan/msan_chained_origin_depot.h Thu Sep 4 05:36:14 2014
@@ -21,6 +21,9 @@ bool ChainedOriginDepotPut(u32 here_id,
// Retrieves a stored stack trace by the id.
u32 ChainedOriginDepotGet(u32 id, u32 *other);
+void ChainedOriginDepotLockAll();
+void ChainedOriginDepotUnlockAll();
+
} // namespace __msan
#endif // MSAN_CHAINED_ORIGIN_DEPOT_H
Modified: compiler-rt/trunk/lib/msan/msan_interceptors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/msan/msan_interceptors.cc?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/msan/msan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/msan/msan_interceptors.cc Thu Sep 4 05:36:14 2014
@@ -1148,6 +1148,24 @@ INTERCEPTOR(void *, shmat, int shmid, co
return p;
}
+static void BeforeFork() {
+ StackDepotLockAll();
+ ChainedOriginDepotLockAll();
+}
+
+static void AfterFork() {
+ ChainedOriginDepotUnlockAll();
+ StackDepotUnlockAll();
+}
+
+INTERCEPTOR(int, fork, void) {
+ ENSURE_MSAN_INITED();
+ BeforeFork();
+ int pid = REAL(fork)();
+ AfterFork();
+ return pid;
+}
+
struct MSanInterceptorContext {
bool in_interceptor_scope;
};
@@ -1532,6 +1550,7 @@ void InitializeInterceptors() {
INTERCEPT_FUNCTION(tzset);
INTERCEPT_FUNCTION(__cxa_atexit);
INTERCEPT_FUNCTION(shmat);
+ INTERCEPT_FUNCTION(fork);
inited = 1;
}
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.cc?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.cc Thu Sep 4 05:36:14 2014
@@ -128,6 +128,14 @@ const uptr *StackDepotGet(u32 id, uptr *
return desc.stack;
}
+void StackDepotLockAll() {
+ theDepot.LockAll();
+}
+
+void StackDepotUnlockAll() {
+ theDepot.UnlockAll();
+}
+
bool StackDepotReverseMap::IdDescPair::IdComparator(
const StackDepotReverseMap::IdDescPair &a,
const StackDepotReverseMap::IdDescPair &b) {
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.h?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepot.h Thu Sep 4 05:36:14 2014
@@ -40,6 +40,9 @@ StackDepotHandle StackDepotPut_WithHandl
// Retrieves a stored stack trace by the id.
const uptr *StackDepotGet(u32 id, uptr *size);
+void StackDepotLockAll();
+void StackDepotUnlockAll();
+
// Instantiating this class creates a snapshot of StackDepot which can be
// efficiently queried with StackDepotGet(). You can use it concurrently with
// StackDepot, but the snapshot is only guaranteed to contain those stack traces
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepotbase.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepotbase.h?rev=217140&r1=217139&r2=217140&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepotbase.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stackdepotbase.h Thu Sep 4 05:36:14 2014
@@ -32,6 +32,9 @@ class StackDepotBase {
StackDepotStats *GetStats() { return &stats; }
+ void LockAll();
+ void UnlockAll();
+
private:
static Node *find(Node *s, args_type args, u32 hash);
static Node *lock(atomic_uintptr_t *p);
@@ -153,5 +156,21 @@ StackDepotBase<Node, kReservedBits, kTab
return args_type();
}
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ lock(&tab[i]);
+ }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ atomic_uintptr_t *p = &tab[i];
+ uptr s = atomic_load(p, memory_order_relaxed);
+ unlock(p, (Node *)(s & ~1UL));
+ }
+}
+
} // namespace __sanitizer
#endif // SANITIZER_STACKDEPOTBASE_H
Added: compiler-rt/trunk/test/msan/fork.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/msan/fork.cc?rev=217140&view=auto
==============================================================================
--- compiler-rt/trunk/test/msan/fork.cc (added)
+++ compiler-rt/trunk/test/msan/fork.cc Thu Sep 4 05:36:14 2014
@@ -0,0 +1,123 @@
+// Test that chained origins are fork-safe.
+// Run a number of threads that create new chained origins, then fork
+// and verify that origin reads do not deadlock in the child process.
+
+// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -m64 -O3 %s -o %t
+// RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s
+
+// Fun fact: if test output is redirected to a file (as opposed to
+// being piped directly to FileCheck), we may lose some "done"s due to
+// a kernel bug:
+// https://lkml.org/lkml/2014/2/17/324
+
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <atomic>
+
+#include <sanitizer/msan_interface.h>
+
+std::atomic<bool> done;
+
+void copy_uninit_thread2() {
+ volatile int x;
+ volatile int v;
+ while (true) {
+ v = x;
+ x = v;
+ if (done.load())
+ return;
+ }
+}
+
+void copy_uninit_thread1(int level) {
+ if (!level)
+ copy_uninit_thread2();
+ else
+ copy_uninit_thread1(level - 1);
+}
+
+void *copy_uninit_thread(void *id) {
+ copy_uninit_thread1((long)id);
+ return 0;
+}
+
+// Run through stackdepot in the child process.
+// If any of the hash table cells are locked, this may deadlock.
+void child() {
+ volatile int x;
+ volatile int v;
+ for (int i = 0; i < 10000; ++i) {
+ v = x;
+ x = v;
+ }
+ write(2, "done\n", 5);
+}
+
+void test() {
+ done.store(false);
+ const int kThreads = 10;
+ pthread_t t[kThreads];
+ for (int i = 0; i < kThreads; ++i)
+ pthread_create(&t[i], NULL, copy_uninit_thread, (void*)(long)i);
+ usleep(100000);
+ pid_t pid = fork();
+ if (pid) {
+ // parent
+ done.store(true);
+ usleep(1000000);
+ kill(pid, SIGKILL);
+ } else {
+ // child
+ child();
+ }
+}
+
+int main() {
+ const int kChildren = 20;
+ for (int i = 0; i < kChildren; ++i) {
+ pid_t pid = fork();
+ if (pid) {
+ // parent
+ } else {
+ test();
+ exit(0);
+ }
+ }
+
+ for (int i = 0; i < kChildren; ++i) {
+ pid_t p;
+ while ((p = wait(NULL)) == -1) { }
+ }
+
+ return 0;
+}
+
+// Expect 20 (== kChildren) "done" messages.
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
+// CHECK: done
Propchange: compiler-rt/trunk/test/msan/fork.cc
------------------------------------------------------------------------------
svn:eol-style = LF
More information about the llvm-commits
mailing list