-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[ASan][Darwin][GCD] Add interceptor for dispatch_apply #149238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[ASan][Darwin][GCD] Add interceptor for dispatch_apply #149238
Conversation
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
@llvm/pr-subscribers-compiler-rt-sanitizer Author: None (thetruestblue) ChangesASan 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:
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
|
@DanBlackwell @padriff requesting review as well. |
92a25c0
to
5d72c09
Compare
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --extensions c,cpp,h -- 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 2ac23eec3..d0e9fef78 100644
--- a/compiler-rt/lib/asan/asan_mac.cpp
+++ b/compiler-rt/lib/asan/asan_mac.cpp
@@ -258,8 +258,8 @@ INTERCEPTOR(void, dispatch_apply_f, size_t iterations, dispatch_queue_t queue,
asan_block_context_t *asan_ctxt =
alloc_asan_context(ctxt, (dispatch_function_t)work, &stack);
- REAL(dispatch_apply_f)
- (iterations, queue, (void *)asan_ctxt, asan_dispatch_apply_f_block);
+ REAL(dispatch_apply_f)(iterations, queue, (void *)asan_ctxt,
+ asan_dispatch_apply_f_block);
}
# if !defined(MISSING_BLOCKS_SUPPORT)
@@ -281,10 +281,10 @@ void dispatch_apply(size_t iterations, 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,
|
5d72c09
to
181b63f
Compare
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