Skip to content

[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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions compiler-rt/lib/asan/asan_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
// dispatch_after()
// dispatch_group_async_f()
// dispatch_group_async()
// dispatch_apply()
// dispatch_apply_f()
// TODO(glider): libdispatch API contains other functions that we don't support
// yet.
//
Expand Down Expand Up @@ -243,7 +245,24 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
asan_dispatch_call_block_and_release);
}

#if !defined(MISSING_BLOCKS_SUPPORT)
extern "C" void asan_dispatch_apply_f_block(void *context, size_t iteration) {
GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = (asan_block_context_t *)context;
asan_register_worker_thread(asan_ctxt->parent_tid, &stack);
((void (*)(void *, size_t))asan_ctxt->func)(asan_ctxt->block, iteration);
}

INTERCEPTOR(void, dispatch_apply_f, size_t iterations, dispatch_queue_t queue,
void *ctxt, void (*work)(void *, size_t)) {
GET_STACK_TRACE_THREAD;
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);
}

# if !defined(MISSING_BLOCKS_SUPPORT)
extern "C" {
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
Expand All @@ -255,6 +274,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) \
Expand Down Expand Up @@ -332,6 +353,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
6 changes: 6 additions & 0 deletions compiler-rt/lib/asan/tests/asan_mac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/asan/tests/asan_mac_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern "C" {
void TestGCDReuseWqthreadsAsync();
void TestGCDReuseWqthreadsSync();
void TestGCDDispatchAfter();
void TestGCDDispatchApply();
void TestGCDInTSDDestructor();
void TestGCDSourceEvent();
void TestGCDSourceCancel();
Expand Down
10 changes: 10 additions & 0 deletions compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
49 changes: 49 additions & 0 deletions compiler-rt/test/asan/TestCases/Darwin/dispatch_apply_threadno.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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 -DDISPATCH_APPLY_F %s -o %t
// RUN: not %run %t 2>&1 | FileCheck %s

// 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]);
});
}

typedef struct {
char *data;
} Context;

void da_func(void *ctx, size_t i) {
Context *c = (Context *)ctx;
access_memory_frame(&c->data[i]);
}

__attribute__((noinline)) void test_dispatch_apply_f() {
Context *ctx = (Context *)malloc(sizeof(Context));
ctx->data = (char *)malloc(4);
dispatch_apply_f(8, dispatch_get_global_queue(0, 0), ctx, da_func);
}

int main(int argc, const char *argv[]) {
#if DISPATCH_APPLY_F
test_dispatch_apply_f();
#else
test_dispatch_apply();
#endif
return 0;
}

// CHECK: ERROR: AddressSanitizer: heap-buffer-overflow
// CHECK: #0 0x{{.*}} in {{.*}}access_memory_frame
// CHECK-NOT: T-1
Loading