summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuba Brecka <kuba.brecka@gmail.com>2015-11-19 10:35:35 +0000
committerKuba Brecka <kuba.brecka@gmail.com>2015-11-19 10:35:35 +0000
commitefd92b3d16589f65bd762279697ab2add95ebd91 (patch)
tree356ae79f51e2b562abd2e8e2314c2c816a26943f
parentf278eb10b7dad7e081a29ff6d7f0f21fd3f37533 (diff)
downloadbcm5719-llvm-efd92b3d16589f65bd762279697ab2add95ebd91.tar.gz
bcm5719-llvm-efd92b3d16589f65bd762279697ab2add95ebd91.zip
[tsan] Handle dispatch_once on OS X
Reimplement dispatch_once in an interceptor to solve these issues that may produce false positives with TSan on OS X: 1) there is a racy load inside an inlined part of dispatch_once, 2) the fast path in dispatch_once doesn't perform an acquire load, so we don't properly synchronize the initialization and subsequent uses of whatever is initialized, 3) dispatch_once is already used in a lot of already-compiled code, so TSan doesn't see the inlined fast-path. This patch uses a trick to avoid ever taking the fast path (by never storing ~0 into the predicate), which means the interceptor will always be called even from already-compiled code. Within the interceptor, our own atomic reads and writes are not written into shadow cells, so the race in the inlined part is not reported (because the accesses are only loads). Differential Revision: http://reviews.llvm.org/D14811 llvm-svn: 253552
-rw-r--r--compiler-rt/lib/tsan/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.cc11
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.h12
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc72
-rw-r--r--compiler-rt/test/tsan/Darwin/gcd-once.mm55
5 files changed, 140 insertions, 11 deletions
diff --git a/compiler-rt/lib/tsan/CMakeLists.txt b/compiler-rt/lib/tsan/CMakeLists.txt
index de0fb439eaf..66ba30c031c 100644
--- a/compiler-rt/lib/tsan/CMakeLists.txt
+++ b/compiler-rt/lib/tsan/CMakeLists.txt
@@ -47,6 +47,7 @@ set(TSAN_CXX_SOURCES
if(APPLE)
list(APPEND TSAN_SOURCES
+ rtl/tsan_libdispatch_mac.cc
rtl/tsan_platform_mac.cc
rtl/tsan_platform_posix.cc)
elseif(UNIX)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
index 7aaa4c304bc..f389d6596e1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
@@ -264,17 +264,6 @@ ScopedInterceptor::~ScopedInterceptor() {
}
}
-#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
- SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- if (REAL(func) == 0) { \
- Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
- Die(); \
- } \
- if (thr->ignore_interceptors || thr->in_ignored_lib) \
- return REAL(func)(__VA_ARGS__); \
-/**/
-
-#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
#if SANITIZER_FREEBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
index 49b79a7c5f9..451e8645fc8 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
@@ -26,6 +26,18 @@ class ScopedInterceptor {
(void)pc; \
/**/
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ if (REAL(func) == 0) { \
+ Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+ Die(); \
+ } \
+ if (thr->ignore_interceptors || thr->in_ignored_lib) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+
#if SANITIZER_FREEBSD
#define __libc_free __free
#define __libc_malloc __malloc
diff --git a/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
new file mode 100644
index 00000000000..7f80a470288
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -0,0 +1,72 @@
+//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific libdispatch (GCD) support.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+namespace __tsan {
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+ dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
+ atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 &&
+ atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+ block();
+ Release(thr, pc, (uptr)a);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(thr, pc, (uptr)a);
+ }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+ void *context, dispatch_function_t function) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
+ WRAP(dispatch_once)(predicate, ^(void) {
+ function(context);
+ });
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
diff --git a/compiler-rt/test/tsan/Darwin/gcd-once.mm b/compiler-rt/test/tsan/Darwin/gcd-once.mm
new file mode 100644
index 00000000000..17757d20375
--- /dev/null
+++ b/compiler-rt/test/tsan/Darwin/gcd-once.mm
@@ -0,0 +1,55 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+static const long kNumThreads = 4;
+
+long global;
+long global2;
+
+static dispatch_once_t once_token;
+static dispatch_once_t once_token2;
+
+void f(void *) {
+ global2 = 42;
+ usleep(100000);
+}
+
+void *Thread(void *a) {
+ barrier_wait(&barrier);
+
+ dispatch_once(&once_token, ^{
+ global = 42;
+ usleep(100000);
+ });
+ long x = global;
+
+ dispatch_once_f(&once_token2, NULL, f);
+ long x2 = global2;
+
+ fprintf(stderr, "global = %ld\n", x);
+ fprintf(stderr, "global2 = %ld\n", x2);
+ return 0;
+}
+
+int main() {
+ fprintf(stderr, "Hello world.\n");
+ barrier_init(&barrier, kNumThreads);
+
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_create(&t[i], 0, Thread, 0);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ pthread_join(t[i], 0);
+ }
+
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
OpenPOWER on IntegriCloud