Skip to content

Commit 267f96a

Browse files
committed
[ASan][Darwin][GCD] Add interceptor for dispatch_apply
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
1 parent 94382c8 commit 267f96a

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

compiler-rt/lib/asan/asan_mac.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
103103
// dispatch_after()
104104
// dispatch_group_async_f()
105105
// dispatch_group_async()
106+
// dispatch_apply()
106107
// TODO(glider): libdispatch API contains other functions that we don't support
107108
// yet.
108109
//
@@ -255,6 +256,8 @@ void dispatch_source_set_cancel_handler(dispatch_source_t ds,
255256
void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
256257
dispatch_mach_t dispatch_mach_create(const char *label, dispatch_queue_t queue,
257258
dispatch_mach_handler_t handler);
259+
void dispatch_apply(size_t iterations, dispatch_queue_t queue,
260+
void (^block)(size_t iteration));
258261
}
259262

260263
#define GET_ASAN_BLOCK(work) \
@@ -332,6 +335,20 @@ INTERCEPTOR(void *, dispatch_mach_create_f, const char *label,
332335
});
333336
}
334337

335-
#endif
338+
INTERCEPTOR(void, dispatch_apply, size_t iterations, dispatch_queue_t queue,
339+
void (^block)(size_t iteration)) {
340+
ENABLE_FRAME_POINTER;
341+
int parent_tid = GetCurrentTidOrInvalid();
342+
343+
void (^asan_block)(size_t) = ^(size_t iteration) {
344+
GET_STACK_TRACE_THREAD;
345+
asan_register_worker_thread(parent_tid, &stack);
346+
block(iteration);
347+
};
348+
349+
REAL(dispatch_apply)(iterations, queue, asan_block);
350+
}
351+
352+
# endif
336353

337354
#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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 2>&1 | FileCheck %s
7+
8+
#include <dispatch/dispatch.h>
9+
#include <stdlib.h>
10+
11+
__attribute__((noinline)) void access_memory_frame(char *x) { *x = 0; }
12+
13+
__attribute__((noinline)) void test_dispatch_apply() {
14+
char *x = (char *)malloc(4);
15+
dispatch_apply(8, dispatch_get_global_queue(0, 0), ^(size_t i) {
16+
access_memory_frame(&x[i]);
17+
});
18+
}
19+
20+
int main(int argc, const char *argv[]) {
21+
test_dispatch_apply();
22+
return 0;
23+
}
24+
25+
// CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
26+
// CHECK: #0 0x{{.*}} in {{.*}}access_memory_frame
27+
// CHECK-NOT: T-1

0 commit comments

Comments
 (0)