Skip to content

Commit 5ce04b4

Browse files
[ASan][Darwin][GCD] Add interceptor for dispatch_apply (#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
1 parent 81185f7 commit 5ce04b4

File tree

5 files changed

+107
-2
lines changed

5 files changed

+107
-2
lines changed

compiler-rt/lib/asan/asan_mac.cpp

Lines changed: 36 additions & 2 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
//
@@ -243,13 +245,31 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
243245
asan_dispatch_call_block_and_release);
244246
}
245247

246-
#if !defined(MISSING_BLOCKS_SUPPORT)
248+
extern "C" void asan_dispatch_apply_f_work(void *context, size_t iteration) {
249+
GET_STACK_TRACE_THREAD;
250+
asan_block_context_t *asan_ctxt = (asan_block_context_t *)context;
251+
asan_register_worker_thread(asan_ctxt->parent_tid, &stack);
252+
((void (*)(void *, size_t))asan_ctxt->func)(asan_ctxt->block, iteration);
253+
}
254+
255+
INTERCEPTOR(void, dispatch_apply_f, size_t iterations, dispatch_queue_t queue,
256+
void *ctxt, void (*work)(void *, size_t)) {
257+
GET_STACK_TRACE_THREAD;
258+
asan_block_context_t *asan_ctxt =
259+
alloc_asan_context(ctxt, (dispatch_function_t)work, &stack);
260+
REAL(dispatch_apply_f)(iterations, queue, (void *)asan_ctxt,
261+
asan_dispatch_apply_f_work);
262+
}
263+
264+
# if !defined(MISSING_BLOCKS_SUPPORT)
247265
extern "C" {
248266
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
249267
void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
250268
void(^work)(void));
251269
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
252270
void(^work)(void));
271+
void dispatch_apply(size_t iterations, dispatch_queue_t queue,
272+
void (^block)(size_t iteration));
253273
void dispatch_source_set_cancel_handler(dispatch_source_t ds,
254274
void(^work)(void));
255275
void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
@@ -332,6 +352,20 @@ INTERCEPTOR(void *, dispatch_mach_create_f, const char *label,
332352
});
333353
}
334354

335-
#endif
355+
INTERCEPTOR(void, dispatch_apply, size_t iterations, dispatch_queue_t queue,
356+
void (^block)(size_t iteration)) {
357+
ENABLE_FRAME_POINTER;
358+
int parent_tid = GetCurrentTidOrInvalid();
359+
360+
void (^asan_block)(size_t) = ^(size_t iteration) {
361+
GET_STACK_TRACE_THREAD;
362+
asan_register_worker_thread(parent_tid, &stack);
363+
block(iteration);
364+
};
365+
366+
REAL(dispatch_apply)(iterations, queue, asan_block);
367+
}
368+
369+
# endif
336370

337371
#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)