[compiler-rt] [ASan][Darwin][GCD] Add interceptor for dispatch_apply (PR #149238)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 16 20:05:53 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (thetruestblue)

<details>
<summary>Changes</summary>

ASan had a gap in coverage for wqthreads blocks submitted by dispatch_apply

This adds interceptor for dispatch_apply and adds a test that a failure in a dispatch apply block contains thread and stack info.

rdar://139660648

---
Full diff: https://github.com/llvm/llvm-project/pull/149238.diff


5 Files Affected:

- (modified) compiler-rt/lib/asan/asan_mac.cpp (+18-1) 
- (modified) compiler-rt/lib/asan/tests/asan_mac_test.cpp (+6) 
- (modified) compiler-rt/lib/asan/tests/asan_mac_test.h (+1) 
- (modified) compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm (+10) 
- (added) compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c (+27) 


``````````diff
diff --git a/compiler-rt/lib/asan/asan_mac.cpp b/compiler-rt/lib/asan/asan_mac.cpp
index be513a03ed5cd..30c81ec64f024 100644
--- a/compiler-rt/lib/asan/asan_mac.cpp
+++ b/compiler-rt/lib/asan/asan_mac.cpp
@@ -103,6 +103,7 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
 //   dispatch_after()
 //   dispatch_group_async_f()
 //   dispatch_group_async()
+//   dispatch_apply()
 // TODO(glider): libdispatch API contains other functions that we don't support
 // yet.
 //
@@ -255,6 +256,8 @@ void dispatch_source_set_cancel_handler(dispatch_source_t ds,
 void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
 dispatch_mach_t dispatch_mach_create(const char *label, dispatch_queue_t queue,
                                      dispatch_mach_handler_t handler);
+void dispatch_apply(size_t iterations, dispatch_queue_t queue,
+                    void (^block)(size_t iteration));
 }
 
 #define GET_ASAN_BLOCK(work) \
@@ -332,6 +335,20 @@ INTERCEPTOR(void *, dispatch_mach_create_f, const char *label,
       });
 }
 
-#endif
+INTERCEPTOR(void, dispatch_apply, size_t iterations, dispatch_queue_t queue,
+            void (^block)(size_t iteration)) {
+  ENABLE_FRAME_POINTER;
+  int parent_tid = GetCurrentTidOrInvalid();
+
+  void (^asan_block)(size_t) = ^(size_t iteration) {
+    GET_STACK_TRACE_THREAD;
+    asan_register_worker_thread(parent_tid, &stack);
+    block(iteration);
+  };
+
+  REAL(dispatch_apply)(iterations, queue, asan_block);
+}
+
+#  endif
 
 #endif  // SANITIZER_APPLE
diff --git a/compiler-rt/lib/asan/tests/asan_mac_test.cpp b/compiler-rt/lib/asan/tests/asan_mac_test.cpp
index bd36089991deb..4b21f12f81eac 100644
--- a/compiler-rt/lib/asan/tests/asan_mac_test.cpp
+++ b/compiler-rt/lib/asan/tests/asan_mac_test.cpp
@@ -116,6 +116,12 @@ TEST(AddressSanitizerMac, GCDDispatchAfter) {
   EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
 }
 
+TEST(AddressSanitizerMac, GCDDispatchApply) {
+  // Make sure the whole ASan report is printed, i.e. that we don't die
+  // on a CHECK.
+  EXPECT_DEATH(TestGCDDispatchApply(), "Shadow byte legend");
+}
+
 TEST(AddressSanitizerMac, GCDSourceEvent) {
   // Make sure the whole ASan report is printed, i.e. that we don't die
   // on a CHECK.
diff --git a/compiler-rt/lib/asan/tests/asan_mac_test.h b/compiler-rt/lib/asan/tests/asan_mac_test.h
index 441547a5a3dcb..ec71546a3989b 100644
--- a/compiler-rt/lib/asan/tests/asan_mac_test.h
+++ b/compiler-rt/lib/asan/tests/asan_mac_test.h
@@ -9,6 +9,7 @@ extern "C" {
   void TestGCDReuseWqthreadsAsync();
   void TestGCDReuseWqthreadsSync();
   void TestGCDDispatchAfter();
+  void TestGCDDispatchApply();
   void TestGCDInTSDDestructor();
   void TestGCDSourceEvent();
   void TestGCDSourceCancel();
diff --git a/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm b/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm
index 3f8fa26d95b8d..ddb50f894639d 100644
--- a/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm
+++ b/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm
@@ -148,6 +148,16 @@ void TestGCDDispatchAfter() {
   wait_forever();
 }
 
+void TestGCDDispatchApply() {
+  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+  __block char *buffer = (char *)malloc(4);
+  dispatch_apply(8, queue, ^(size_t i) {
+    access_memory(&buffer[i]);
+  });
+
+  free(buffer);  // not reached
+}
+
 void worker_do_deallocate(void *ptr) {
   free(ptr);
 }
diff --git a/compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c b/compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c
new file mode 100644
index 0000000000000..8dfd0942bf656
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c
@@ -0,0 +1,27 @@
+// Bugs caught within missing GCD dispatch blocks result in thread being reported as T-1
+// with an empty stack.
+// This tests that dispatch_apply blocks can capture valid thread number and stack.
+
+// RUN: %clang_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+#include <dispatch/dispatch.h>
+#include <stdlib.h>
+
+__attribute__((noinline)) void access_memory_frame(char *x) { *x = 0; }
+
+__attribute__((noinline)) void test_dispatch_apply() {
+  char *x = (char *)malloc(4);
+  dispatch_apply(8, dispatch_get_global_queue(0, 0), ^(size_t i) {
+    access_memory_frame(&x[i]);
+  });
+}
+
+int main(int argc, const char *argv[]) {
+  test_dispatch_apply();
+  return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK: #0 0x{{.*}} in {{.*}}access_memory_frame
+// CHECK-NOT: T-1
\ No newline at end of file

``````````

</details>


https://github.com/llvm/llvm-project/pull/149238


More information about the llvm-commits mailing list