Skip to content

Commit 21179c7

Browse files
Merge pull request #11409 from swiftlang/add_dispatch_apply
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)
2 parents 74cc2f4 + 87006c4 commit 21179c7

File tree

5 files changed

+119
-6
lines changed

5 files changed

+119
-6
lines changed

compiler-rt/lib/asan/asan_mac.cpp

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
103103
// dispatch_after()
104104
// dispatch_group_async_f()
105105
// dispatch_group_async()
106+
// dispatch_apply()
107+
// dispatch_apply_f()
106108
// TODO(glider): libdispatch API contains other functions that we don't support
107109
// yet.
108110
//
@@ -128,6 +130,7 @@ typedef void* dispatch_queue_t;
128130
typedef void* dispatch_source_t;
129131
typedef u64 dispatch_time_t;
130132
typedef void (*dispatch_function_t)(void *block);
133+
typedef void (*dispatch_apply_function_t)(void *, size_t);
131134
typedef void* (*worker_t)(void *block);
132135
typedef unsigned long dispatch_mach_reason;
133136
typedef void *dispatch_mach_msg_t;
@@ -147,7 +150,11 @@ typedef void (^dispatch_mach_handler_t)(dispatch_mach_reason reason,
147150
// A wrapper for the ObjC blocks used to support libdispatch.
148151
typedef struct {
149152
void *block;
150-
dispatch_function_t func;
153+
union {
154+
dispatch_function_t dispatch_func;
155+
dispatch_apply_function_t dispatch_apply_func;
156+
static_assert(sizeof(dispatch_func) == sizeof(dispatch_apply_func));
157+
};
151158
u32 parent_tid;
152159
} asan_block_context_t;
153160

@@ -175,8 +182,8 @@ void asan_dispatch_call_block_and_release(void *block) {
175182
block, (void*)pthread_self());
176183
asan_register_worker_thread(context->parent_tid, &stack);
177184
// Call the original dispatcher for the block.
178-
context->func(context->block);
179-
asan_free(context, &stack, FROM_MALLOC);
185+
context->dispatch_func(context->block);
186+
asan_free(context, &stack);
180187
}
181188

182189
} // namespace __asan
@@ -191,7 +198,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
191198
asan_block_context_t *asan_ctxt =
192199
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
193200
asan_ctxt->block = ctxt;
194-
asan_ctxt->func = func;
201+
asan_ctxt->dispatch_func = func;
195202
asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
196203
return asan_ctxt;
197204
}
@@ -243,13 +250,34 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
243250
asan_dispatch_call_block_and_release);
244251
}
245252

246-
#if !defined(MISSING_BLOCKS_SUPPORT)
253+
extern "C" void asan_dispatch_apply_f_work(void *context, size_t iteration) {
254+
GET_STACK_TRACE_THREAD;
255+
asan_block_context_t *asan_ctxt = (asan_block_context_t *)context;
256+
asan_register_worker_thread(asan_ctxt->parent_tid, &stack);
257+
asan_ctxt->dispatch_apply_func(asan_ctxt->block, iteration);
258+
}
259+
260+
INTERCEPTOR(void, dispatch_apply_f, size_t iterations, dispatch_queue_t queue,
261+
void *ctxt, dispatch_apply_function_t work) {
262+
GET_STACK_TRACE_THREAD;
263+
asan_block_context_t *asan_ctxt =
264+
(asan_block_context_t *)asan_malloc(sizeof(asan_block_context_t), &stack);
265+
asan_ctxt->block = ctxt;
266+
asan_ctxt->dispatch_apply_func = work;
267+
asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
268+
REAL(dispatch_apply_f)(iterations, queue, (void *)asan_ctxt,
269+
asan_dispatch_apply_f_work);
270+
}
271+
272+
# if !defined(MISSING_BLOCKS_SUPPORT)
247273
extern "C" {
248274
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
249275
void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
250276
void(^work)(void));
251277
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
252278
void(^work)(void));
279+
void dispatch_apply(size_t iterations, dispatch_queue_t queue,
280+
void (^block)(size_t iteration));
253281
void dispatch_source_set_cancel_handler(dispatch_source_t ds,
254282
void(^work)(void));
255283
void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
@@ -332,6 +360,20 @@ INTERCEPTOR(void *, dispatch_mach_create_f, const char *label,
332360
});
333361
}
334362

335-
#endif
363+
INTERCEPTOR(void, dispatch_apply, size_t iterations, dispatch_queue_t queue,
364+
void (^block)(size_t iteration)) {
365+
ENABLE_FRAME_POINTER;
366+
int parent_tid = GetCurrentTidOrInvalid();
367+
368+
void (^asan_block)(size_t) = ^(size_t iteration) {
369+
GET_STACK_TRACE_THREAD;
370+
asan_register_worker_thread(parent_tid, &stack);
371+
block(iteration);
372+
};
373+
374+
REAL(dispatch_apply)(iterations, queue, asan_block);
375+
}
376+
377+
# endif
336378

337379
#endif // SANITIZER_APPLE

compiler-rt/lib/asan/tests/asan_mac_test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ TEST(AddressSanitizerMac, GCDDispatchAfter) {
116116
EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
117117
}
118118

119+
TEST(AddressSanitizerMac, GCDDispatchApply) {
120+
// Make sure the whole ASan report is printed, i.e. that we don't die
121+
// on a CHECK.
122+
EXPECT_DEATH(TestGCDDispatchApply(), "Shadow byte legend");
123+
}
124+
119125
TEST(AddressSanitizerMac, GCDSourceEvent) {
120126
// Make sure the whole ASan report is printed, i.e. that we don't die
121127
// on a CHECK.

compiler-rt/lib/asan/tests/asan_mac_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern "C" {
99
void TestGCDReuseWqthreadsAsync();
1010
void TestGCDReuseWqthreadsSync();
1111
void TestGCDDispatchAfter();
12+
void TestGCDDispatchApply();
1213
void TestGCDInTSDDestructor();
1314
void TestGCDSourceEvent();
1415
void TestGCDSourceCancel();

compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,16 @@ void TestGCDDispatchAfter() {
148148
wait_forever();
149149
}
150150

151+
void TestGCDDispatchApply() {
152+
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
153+
__block char *buffer = (char *)malloc(4);
154+
dispatch_apply(8, queue, ^(size_t i) {
155+
access_memory(&buffer[i]);
156+
});
157+
158+
free(buffer); // not reached
159+
}
160+
151161
void worker_do_deallocate(void *ptr) {
152162
free(ptr);
153163
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Bugs caught within missing GCD dispatch blocks result in thread being reported as T-1
2+
// with an empty stack.
3+
// This tests that dispatch_apply blocks can capture valid thread number and stack.
4+
5+
// RUN: %clang_asan %s -o %t
6+
// RUN: not %run %t func 2>&1 | FileCheck %s --check-prefixes=CHECK-FUNC,CHECK
7+
// RUN: not %run %t block 2>&1 | FileCheck %s --check-prefixes=CHECK-BLOCK,CHECK
8+
9+
#include <dispatch/dispatch.h>
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
13+
__attribute__((noinline)) void access_memory_frame(char *x) { *x = 0; }
14+
15+
__attribute__((noinline)) void test_dispatch_apply() {
16+
char *x = (char *)malloc(4);
17+
dispatch_apply(8, dispatch_get_global_queue(0, 0), ^(size_t i) {
18+
access_memory_frame(&x[i]);
19+
});
20+
}
21+
22+
typedef struct {
23+
char *data;
24+
} Context;
25+
26+
void da_func(void *ctx, size_t i) {
27+
Context *c = (Context *)ctx;
28+
access_memory_frame(&c->data[i]);
29+
}
30+
31+
__attribute__((noinline)) void test_dispatch_apply_f() {
32+
Context *ctx = (Context *)malloc(sizeof(Context));
33+
ctx->data = (char *)malloc(4);
34+
dispatch_apply_f(8, dispatch_get_global_queue(0, 0), ctx, da_func);
35+
}
36+
37+
int main(int argc, const char *argv[]) {
38+
if (strcmp(argv[1], "func") == 0) {
39+
fprintf(stderr, "Test dispatch_apply with function\n");
40+
// CHECK-FUNC: dispatch_apply with function
41+
test_dispatch_apply_f();
42+
} else if (strcmp(argv[1], "block") == 0) {
43+
fprintf(stderr, "Test dispatch_apply with block\n");
44+
// CHECK-BLOCK: dispatch_apply with block
45+
test_dispatch_apply();
46+
} else {
47+
abort();
48+
}
49+
return 0;
50+
}
51+
52+
// CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
53+
// CHECK: #0 0x{{.*}} in {{.*}}access_memory_frame
54+
// CHECK-NOT: T-1

0 commit comments

Comments
 (0)