Skip to content

Conversation

thetruestblue
Copy link
Contributor

@thetruestblue thetruestblue commented Jul 17, 2025

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

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

rdar://139660648

ASan had a gap in covarage for wqthreads 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
@llvmbot
Copy link
Member

llvmbot commented Jul 17, 2025

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

Author: None (thetruestblue)

Changes

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 --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

@thetruestblue
Copy link
Contributor Author

@DanBlackwell @padriff requesting review as well.

@thetruestblue thetruestblue force-pushed the blueg/dispatch-apply-interceptor branch from 92a25c0 to 5d72c09 Compare July 17, 2025 16:24
Copy link

github-actions bot commented Jul 17, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions h,c,cpp -- compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c compiler-rt/lib/asan/asan_mac.cpp compiler-rt/lib/asan/tests/asan_mac_test.cpp compiler-rt/lib/asan/tests/asan_mac_test.h
View the diff from clang-format here.
diff --git a/compiler-rt/lib/asan/asan_mac.cpp b/compiler-rt/lib/asan/asan_mac.cpp
index 1f3c79eec..9f3cd27b7 100644
--- a/compiler-rt/lib/asan/asan_mac.cpp
+++ b/compiler-rt/lib/asan/asan_mac.cpp
@@ -280,10 +280,10 @@ dispatch_mach_t dispatch_mach_create(const char *label, dispatch_queue_t queue,
 #define GET_ASAN_BLOCK(work) \
   void (^asan_block)(void);  \
   int parent_tid = GetCurrentTidOrInvalid(); \
-  asan_block = ^(void) { \
-    GET_STACK_TRACE_THREAD; \
-    asan_register_worker_thread(parent_tid, &stack); \
-    work(); \
+  asan_block = ^(void) {                   \
+        GET_STACK_TRACE_THREAD;                          \
+        asan_register_worker_thread(parent_tid, &stack); \
+        work(); \
   }
 
 INTERCEPTOR(void, dispatch_async,

@thetruestblue thetruestblue force-pushed the blueg/dispatch-apply-interceptor branch from 5d72c09 to 181b63f Compare July 17, 2025 16:34
@thetruestblue
Copy link
Contributor Author

clang format is complaining about code that is not a part of my diff...

@fmayer
Copy link
Contributor

fmayer commented Jul 21, 2025

clang format is complaining about code that is not a part of my diff...

Are you using git clang-format?

Sorry, of course you meant the github action

@wrotki
Copy link
Contributor

wrotki commented Jul 21, 2025

I noticed that dispatch_apply_threadno.c is missing newline at the end, the formatter might complain about that too.

@wrotki
Copy link
Contributor

wrotki commented Jul 22, 2025

I noticed that dispatch_apply_threadno.c is missing newline at the end, the formatter might complain about that too.

👎 - you mean it's not the case? Github shows red circle with red minus sign at the end of this file - perhaps it means something else, never mind if it is so.

@thetruestblue
Copy link
Contributor Author

@wrotki -- clang formatter did not complain about it. You can see here the one place the formatter is mad, and its not complaining about no new line at end of file. The thumbs down was just to indicate that the formatter doesn't care.

Copy link
Collaborator

@yln yln left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

All I have are a nit for the test and Mariusz' naming suggestion.

@thetruestblue thetruestblue force-pushed the blueg/dispatch-apply-interceptor branch 2 times, most recently from b5f9998 to 769ee96 Compare July 23, 2025 16:00
@thetruestblue thetruestblue force-pushed the blueg/dispatch-apply-interceptor branch from 769ee96 to acf7469 Compare July 23, 2025 16:06
@thetruestblue
Copy link
Contributor Author

nits addressed. I'm not going to change the complaint about formatting in the block, as it's not apart of the diff and I don't want to confuse this commit with changes to that block structure.

@thetruestblue thetruestblue merged commit 5ce04b4 into llvm:main Jul 23, 2025
8 of 9 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jul 28, 2025
ASan had a gap in coverage for wqthreads blocks submitted by
dispatch_apply

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

rdar://139660648
@DanBlackwell
Copy link
Contributor

I think I have a warning from code introduced by this commit:

.../llvm-project/compiler-rt/lib/asan/asan_mac.cpp:252:4: warning: cast from 'dispatch_function_t' (aka 'void (*)(void *)') to 'void (*)(void *, size_t)' (aka 'void (*)(void *, unsigned long)') converts to incompatible function type [-Wcast-function-type-mismatch]
  252 |   ((void (*)(void *, size_t))asan_ctxt->func)(asan_ctxt->block, iteration);
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../llvm-project/compiler-rt/lib/asan/asan_mac.cpp:259:32: warning: cast from 'void (*)(void *, size_t)' (aka 'void (*)(void *, unsigned long)') to 'dispatch_function_t' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-mismatch]
  259 |       alloc_asan_context(ctxt, (dispatch_function_t)work, &stack);
      |                                ^~~~~~~~~~~~~~~~~~~~~~~~~

thetruestblue added a commit to swiftlang/llvm-project that referenced this pull request Sep 14, 2025
ASan had a gap in coverage for wqthreads blocks submitted by
dispatch_apply

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

rdar://139660648
(cherry picked from commit 5ce04b4)
thetruestblue added a commit to swiftlang/llvm-project that referenced this pull request Sep 14, 2025
Author: Dan Blackwell [email protected]
Date: Fri Aug 1 16:33:23 2025 +0100

[TSan] Fix asan_mac.cpp function pointer cast warnings (llvm#151517)

Fixes these compiler warnings:

    .../llvm-project/compiler-rt/lib/asan/asan_mac.cpp:252:4: warning: cast from 'dispatch_function_t' (aka 'void (*)(void *)') to 'void (*)(void *, size_t)' (aka 'void (*)(void *, unsigned long)') converts to incompatible function type [-Wcast-function-type-mismatch]
      252 |   ((void (*)(void *, size_t))asan_ctxt->func)(asan_ctxt->block, iteration);
          |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    .../llvm-project/compiler-rt/lib/asan/asan_mac.cpp:259:32: warning: cast from 'void (*)(void *, size_t)' (aka 'void (*)(void *, unsigned long)') to 'dispatch_function_t' (aka 'void (*)(void *)') converts to incompatible function type [-Wcast-function-type-mismatch]
      259 |       alloc_asan_context(ctxt, (dispatch_function_t)work, &stack);
          |                                ^~~~~~~~~~~~~~~~~~~~~~~~~
    
(cherry picked from commit e7e7494)

[ASan][Darwin][GCD] Add interceptor for dispatch_apply (llvm#149238)

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

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

rdar://139660648
(cherry picked from commit 5ce04b4)
thetruestblue added a commit to swiftlang/llvm-project that referenced this pull request Sep 15, 2025
ASan had a gap in coverage for wqthreads blocks submitted by
dispatch_apply

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

rdar://139660648
(cherry picked from commit 5ce04b4)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants