Dynamic interceptors for dispatch_async and dispatch_after.


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@162202 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h
index 71cc2cc..0a18818 100644
--- a/lib/asan/asan_intercepted_functions.h
+++ b/lib/asan/asan_intercepted_functions.h
@@ -198,6 +198,12 @@
 DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy,
                              CFAllocatorRef alloc, CFStringRef str);
 DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr);
+#if MAC_INTERPOSE_FUNCTIONS
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async,
+                             dispatch_queue_t dq, void (^work)(void));
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after,
+                             dispatch_queue_t dq, void (^work)(void));
+#endif  // MAC_INTERPOSE_FUNCTIONS
 #endif  // __APPLE__
 }  // extern "C"
 #endif
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index bda4e22..0118da8 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -241,6 +241,8 @@
     pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
 }  // extern "C"
 
+// For use by only those functions that allocated the context via
+// alloc_asan_context().
 extern "C"
 void asan_dispatch_call_block_and_release(void *block) {
   GET_STACK_TRACE_HERE(kStackTraceMax);
@@ -312,6 +314,66 @@
                                 asan_dispatch_call_block_and_release);
 }
 
+#if MAC_INTERPOSE_FUNCTIONS
+// dispatch_async and TODO tailcall the corresponding dispatch_*_f functions.
+// When wrapping functions with mach_override, they are intercepted
+// automatically. But with dylib interposition this does not work, because the
+// calls within the same library are not interposed.
+// Therefore we need to re-implement dispatch_async and friends.
+
+// See dispatch/dispatch.h.
+#define DISPATCH_TIME_FOREVER (~0ull)
+typedef void (^dispatch_block_t)(void);
+
+// See
+// http://www.opensource.apple.com/source/libdispatch/libdispatch-228.18/src/init.c
+// for the implementation of _dispatch_call_block_copy_and_release().
+static void _dispatch_call_block_and_release(void *block) {
+  void (^b)(void) = (dispatch_block_t)block;
+  b();
+  _Block_release(b);
+}
+
+// See
+// http://www.opensource.apple.com/source/libdispatch/libdispatch-228.18/src/internal.h
+#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l))
+
+// See
+// http://www.opensource.apple.com/source/libdispatch/libdispatch-228.18/src/init.c
+static dispatch_block_t _dispatch_Block_copy(dispatch_block_t db) {
+  dispatch_block_t rval;
+  if (fastpath(db)) { 
+    while (!fastpath(rval = Block_copy(db))) {
+      sleep(1);
+    }
+    return rval;
+  }
+  CHECK(0 && "NULL was passed where a block should have been");
+  return (dispatch_block_t)NULL;  // Unreachable.
+}
+
+// See
+// http://www.opensource.apple.com/source/libdispatch/libdispatch-228.18/src/queue.c
+// for the implementation of dispatch_async(), dispatch_sync(),
+// dispatch_after().
+INTERCEPTOR(void, dispatch_async,
+            dispatch_queue_t dq, dispatch_block_t work) {
+  WRAP(dispatch_async_f)(dq, _dispatch_Block_copy(work),
+                         _dispatch_call_block_and_release);
+}
+
+INTERCEPTOR(void, dispatch_after,
+            dispatch_time_t when, dispatch_queue_t queue,
+            dispatch_block_t work) {
+  if (when == DISPATCH_TIME_FOREVER) {
+    CHECK(0 && "dispatch_after() called with 'when' == infinity");
+    return;  // Unreachable.
+  }
+  WRAP(dispatch_after_f)(when, queue, _dispatch_Block_copy(work),
+                         _dispatch_call_block_and_release);
+}
+#endif
+
 INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
                                           dispatch_queue_t dq, void *ctxt,
                                           dispatch_function_t func) {
diff --git a/lib/asan/dynamic/asan_interceptors_dynamic.cc b/lib/asan/dynamic/asan_interceptors_dynamic.cc
index 787446d..009d503 100644
--- a/lib/asan/dynamic/asan_interceptors_dynamic.cc
+++ b/lib/asan/dynamic/asan_interceptors_dynamic.cc
@@ -19,6 +19,11 @@
 
 namespace __asan {
 
+#if !MAC_INTERPOSE_FUNCTIONS
+# error \
+  Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS
+#endif
+
 #define INTERPOSE_FUNCTION(function) \
     { reinterpret_cast<const uptr>(WRAP(function)), \
       reinterpret_cast<const uptr>(function) }
@@ -87,6 +92,9 @@
   INTERPOSE_FUNCTION(dispatch_barrier_async_f),
   INTERPOSE_FUNCTION(dispatch_group_async_f),
 
+  INTERPOSE_FUNCTION(dispatch_async),
+  INTERPOSE_FUNCTION(dispatch_after),
+
   INTERPOSE_FUNCTION(__CFInitialize),
   INTERPOSE_FUNCTION(CFStringCreateCopy),
   INTERPOSE_FUNCTION(free),