Skip to content

Add aligned_alloc to macOS tsan interceptors #79198

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

Closed
wants to merge 6 commits into from
Closed
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
12 changes: 11 additions & 1 deletion compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_errno.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
Expand Down Expand Up @@ -815,14 +816,23 @@ TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
#define TSAN_MAYBE_INTERCEPT_MEMALIGN
#endif

#if !SANITIZER_APPLE
// aligned_alloc was introduced in macOS 10.15
// Linking will fail when using an older SDK
#if !SANITIZER_APPLE || defined(__MAC_10_15)
// macOS 10.15 is greater than our minimal deployment target. To ensure we
// generate a weak reference so the TSan dylib continues to work on older
// systems, we need to forward declare the intercepted function as "weak
// imports".
SANITIZER_WEAK_IMPORT void *aligned_alloc(SIZE_T __alignment, SIZE_T __size);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to declare SANITIZER_WEAK_IMPORT for !APPLE ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, maybe @yln can help me answer it. This was following the pattern I found for libdispatch, I don't know if it is necessary or not.

// dispatch_async_and_wait() and friends were introduced in macOS 10.14.
// Linking of these interceptors fails when using an older SDK.
#if !SANITIZER_APPLE || defined(__MAC_10_14)
// macOS 10.14 is greater than our minimal deployment target. To ensure we
// generate a weak reference so the TSan dylib continues to work on older
// systems, we need to forward declare the intercepted functions as "weak
// imports". Note that this file is multi-platform, so we cannot include the
// actual header file (#include <dispatch/dispatch.h>).
SANITIZER_WEAK_IMPORT void dispatch_async_and_wait(
dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
SANITIZER_WEAK_IMPORT void dispatch_async_and_wait_f(
dispatch_queue_t queue, void *context, dispatch_function_t work);
SANITIZER_WEAK_IMPORT void dispatch_barrier_async_and_wait(
dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
SANITIZER_WEAK_IMPORT void dispatch_barrier_async_and_wait_f(
dispatch_queue_t queue, void *context, dispatch_function_t work);
DISPATCH_INTERCEPT_SYNC_F(dispatch_async_and_wait_f, false)
DISPATCH_INTERCEPT_SYNC_B(dispatch_async_and_wait, false)
DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_async_and_wait_f, true)
DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_async_and_wait, true)
#endif

TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
if (in_symbolizer())
return InternalAlloc(sz, nullptr, align);
SCOPED_INTERCEPTOR_RAW(aligned_alloc, align, sz);
return user_aligned_alloc(thr, pc, align, sz);
}
#endif

#if !SANITIZER_APPLE
TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
if (in_symbolizer())
return InternalAlloc(sz, nullptr, GetPageSizeCached());
Expand Down
65 changes: 65 additions & 0 deletions compiler-rt/test/tsan/free_race_aligned_alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %clang_tsan -O1 %s -o %t -undefined dynamic_lookup
// RUN: %deflake %run %t | FileCheck %s

#include "test.h"

#include <stdlib.h>

#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500
# define MACOS_VERSION_AT_LEAST_10_15 1
#else
# define MACOS_VERSION_AT_LEAST_10_15 0
#endif

#if defined(__cplusplus) && (__cplusplus >= 201703L) && \
(!defined(__apple__) || MACOS_VERSION_AT_LEAST_10_15)
# define HAVE_ALIGNED_ALLOC 1
#else
# define HAVE_ALIGNED_ALLOC 0
#endif

int *mem;
pthread_mutex_t mtx;

void *Thread1(void *x) {
pthread_mutex_lock(&mtx);
free(mem);
pthread_mutex_unlock(&mtx);
barrier_wait(&barrier);
return NULL;
}

__attribute__((noinline)) void *Thread2(void *x) {
barrier_wait(&barrier);
pthread_mutex_lock(&mtx);
mem[0] = 42;
pthread_mutex_unlock(&mtx);
return NULL;
}

int main() {

barrier_init(&barrier, 2);
#if HAVE_ALIGNED_ALLOC
mem = (int *)aligned_alloc(8, 8);
#else
mem = (int *)malloc(8);
#endif
pthread_mutex_init(&mtx, 0);
pthread_t t;
pthread_create(&t, NULL, Thread1, NULL);
Thread2(0);
pthread_join(t, NULL);
pthread_mutex_destroy(&mtx);
return 0;
}

// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
// CHECK: Write of size 4 at {{.*}} by main thread{{.*}}:
// CHECK: #0 Thread2
// CHECK: #1 main
// CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}:
// CHECK: #0 free
// CHECK: #{{(1|2)}} Thread1
// CHECK: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2