Skip to content

Commit

Permalink
[TSan] Support fiber API on macOS
Browse files Browse the repository at this point in the history
Committing on behalf of Yuri Per (yuri).

Reviewers: dvyukov, kubamracek, yln

Reviewed By: kubamracek

Authored By: yuri

Differential Revision: https://reviews.llvm.org/D58110

llvm-svn: 358802
  • Loading branch information
yln committed Apr 20, 2019
1 parent fe8aabf commit 4d2b942
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 43 deletions.
32 changes: 32 additions & 0 deletions compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc
Expand Up @@ -20,15 +20,22 @@
#include "tsan_interface_ann.h"
#include "sanitizer_common/sanitizer_addrhashmap.h"

#include <errno.h>
#include <libkern/OSAtomic.h>
#include <objc/objc-sync.h>
#include <sys/ucontext.h>

#if defined(__has_include) && __has_include(<xpc/xpc.h>)
#include <xpc/xpc.h>
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)

typedef long long_t; // NOLINT

extern "C" {
int getcontext(ucontext_t *ucp) __attribute__((returns_twice));
int setcontext(const ucontext_t *ucp);
}

namespace __tsan {

// The non-barrier versions of OSAtomic* functions are semantically mo_relaxed,
Expand Down Expand Up @@ -353,6 +360,31 @@ TSAN_INTERCEPTOR(int, objc_sync_exit, id obj) {
return result;
}

TSAN_INTERCEPTOR(int, swapcontext, ucontext_t *oucp, const ucontext_t *ucp) {
{
SCOPED_INTERCEPTOR_RAW(swapcontext, oucp, ucp);
}
// Bacause of swapcontext() semantics we have no option but to copy its
// impementation here
if (!oucp || !ucp) {
errno = EINVAL;
return -1;
}
ThreadState *thr = cur_thread();
const int UCF_SWAPPED = 0x80000000;
oucp->uc_onstack &= ~UCF_SWAPPED;
thr->ignore_interceptors++;
int ret = getcontext(oucp);
if (!(oucp->uc_onstack & UCF_SWAPPED)) {
thr->ignore_interceptors--;
if (!ret) {
oucp->uc_onstack |= UCF_SWAPPED;
ret = setcontext(ucp);
}
}
return ret;
}

// On macOS, libc++ is always linked dynamically, so intercepting works the
// usual way.
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
Expand Down
2 changes: 0 additions & 2 deletions compiler-rt/lib/tsan/rtl/tsan_interface.cc
Expand Up @@ -125,7 +125,6 @@ void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
*addr = v;
}

#if !SANITIZER_MAC && !SANITIZER_ANDROID
SANITIZER_INTERFACE_ATTRIBUTE
void *__tsan_get_current_fiber() {
return cur_thread();
Expand All @@ -150,7 +149,6 @@ SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_set_fiber_name(void *fiber, const char *name) {
ThreadSetName(static_cast<ThreadState *>(fiber), name);
}
#endif // !SANITIZER_MAC && !SANITIZER_ANDROID
} // extern "C"

void __tsan_acquire(void *addr) {
Expand Down
4 changes: 4 additions & 0 deletions compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
Expand Up @@ -401,6 +401,10 @@ ThreadState *cur_thread() {
return thr;
}

void set_cur_thread(ThreadState *thr) {
*get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
}

void cur_thread_finalize() {
__sanitizer_sigset_t emptyset;
internal_sigfillset(&emptyset);
Expand Down
37 changes: 18 additions & 19 deletions compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc
Expand Up @@ -73,37 +73,36 @@ static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
// shadow memory is set up.
static uptr main_thread_identity = 0;
ALIGNED(64) static char main_thread_state[sizeof(ThreadState)];
static ThreadState *main_thread_state_loc = (ThreadState *)main_thread_state;

ThreadState **cur_thread_location() {
ThreadState **thread_identity = (ThreadState **)pthread_self();
return ((uptr)thread_identity == main_thread_identity) ? nullptr
: thread_identity;
static ThreadState **cur_thread_location() {
uptr thread_identity = (uptr)pthread_self();
if (thread_identity == main_thread_identity || main_thread_identity == 0)
return &main_thread_state_loc;
return (ThreadState **)MemToShadow(thread_identity);
}

ThreadState *cur_thread() {
ThreadState **thr_state_loc = cur_thread_location();
if (thr_state_loc == nullptr || main_thread_identity == 0) {
return (ThreadState *)&main_thread_state;
}
ThreadState **fake_tls = (ThreadState **)MemToShadow((uptr)thr_state_loc);
ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
(uptr *)fake_tls, sizeof(ThreadState));
return thr;
return (ThreadState *)SignalSafeGetOrAllocate(
(uptr *)cur_thread_location(), sizeof(ThreadState));
}

void set_cur_thread(ThreadState *thr) {
*cur_thread_location() = thr;
}

// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
// munmap first and then clear `fake_tls`; if we receive a signal in between,
// handler will try to access the unmapped ThreadState.
void cur_thread_finalize() {
ThreadState **thr_state_loc = cur_thread_location();
if (thr_state_loc == nullptr) {
if (thr_state_loc == &main_thread_state_loc) {
// Calling dispatch_main() or xpc_main() actually invokes pthread_exit to
// exit the main thread. Let's keep the main thread's ThreadState.
return;
}
ThreadState **fake_tls = (ThreadState **)MemToShadow((uptr)thr_state_loc);
internal_munmap(*fake_tls, sizeof(ThreadState));
*fake_tls = nullptr;
internal_munmap(*thr_state_loc, sizeof(ThreadState));
*thr_state_loc = nullptr;
}
#endif

Expand Down Expand Up @@ -265,11 +264,11 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
// The pointer to the ThreadState object is stored in the shadow memory
// of the tls.
uptr tls_end = tls_addr + tls_size;
ThreadState **thr_state_loc = cur_thread_location();
if (thr_state_loc == nullptr) {
uptr thread_identity = (uptr)pthread_self();
if (thread_identity == main_thread_identity) {
MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, tls_size);
} else {
uptr thr_state_start = (uptr)thr_state_loc;
uptr thr_state_start = thread_identity;
uptr thr_state_end = thr_state_start + sizeof(uptr);
CHECK_GE(thr_state_start, tls_addr);
CHECK_LE(thr_state_start, tls_addr + tls_size);
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/tsan/rtl/tsan_rtl.h
Expand Up @@ -464,6 +464,7 @@ struct ThreadState {
#if !SANITIZER_GO
#if SANITIZER_MAC || SANITIZER_ANDROID
ThreadState *cur_thread();
void set_cur_thread(ThreadState *thr);
void cur_thread_finalize();
INLINE void cur_thread_init() { }
#else
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
Expand Up @@ -405,7 +405,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
}
}

#if !SANITIZER_MAC && !SANITIZER_ANDROID && !SANITIZER_GO
#if !SANITIZER_GO
void FiberSwitchImpl(ThreadState *from, ThreadState *to) {
Processor *proc = from->proc();
ProcUnwire(proc, from);
Expand Down
7 changes: 1 addition & 6 deletions compiler-rt/test/lsan/TestCases/swapcontext.cc
Expand Up @@ -6,13 +6,8 @@
// RUN: %env_lsan_opts= not %run %t foo 2>&1 | FileCheck %s
// UNSUPPORTED: arm,powerpc64

#include "sanitizer_common/sanitizer_ucontext.h"
#include <stdio.h>
#if defined(__APPLE__)
// Note: ucontext.h is deprecated on OSX, so this test may stop working
// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now.
#define _XOPEN_SOURCE 1
#endif
#include <ucontext.h>
#include <unistd.h>

const int kStackSize = 1 << 20;
Expand Down
11 changes: 11 additions & 0 deletions compiler-rt/test/sanitizer_common/sanitizer_ucontext.h
@@ -0,0 +1,11 @@
#ifdef __APPLE__
// ucontext.h is deprecated on macOS, so tests that include it may stop working
// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now.
#ifdef _STRUCT_UCONTEXT
#error incomplete ucontext_t already defined, change #include order
#endif
#define _XOPEN_SOURCE 700
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif

#include <ucontext.h>
10 changes: 5 additions & 5 deletions compiler-rt/test/tsan/fiber_asm.cc
@@ -1,6 +1,6 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// REQUIRES: x86_64-target-arch
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "test.h"

struct ucontext {
Expand All @@ -13,8 +13,8 @@ extern "C" {
void ucontext_trampoline();
}

__asm__(".global ucontext_do_switch\n"
"ucontext_do_switch:\n\t"
__asm__(".global " ASM_SYMBOL(ucontext_do_switch) "\n"
ASM_SYMBOL(ucontext_do_switch) ":\n\t"
"pushq %rbp\n\t"
"pushq %r15\n\t"
"pushq %r14\n\t"
Expand All @@ -31,8 +31,8 @@ __asm__(".global ucontext_do_switch\n"
"popq %rbp\n\t"
"retq");

__asm__(".global ucontext_trampoline\n"
"ucontext_trampoline:\n\t"
__asm__(".global " ASM_SYMBOL(ucontext_trampoline) "\n"
ASM_SYMBOL(ucontext_trampoline) ":\n\t"
".cfi_startproc\n\t"
".cfi_undefined rip\n\t"
"movq %r12, %rdi\n\t"
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/fiber_from_thread.cc
@@ -1,7 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "sanitizer_common/sanitizer_ucontext.h"
#include "test.h"
#include <ucontext.h>

char stack[64 * 1024] __attribute__((aligned(16)));

Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/fiber_longjmp.cc
@@ -1,8 +1,8 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "sanitizer_common/sanitizer_ucontext.h"
#include "test.h"
#include <setjmp.h>
#include <ucontext.h>

char stack[64 * 1024] __attribute__((aligned(16)));

Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/fiber_race.cc
@@ -1,7 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "sanitizer_common/sanitizer_ucontext.h"
#include "test.h"
#include <ucontext.h>

char stack[64 * 1024] __attribute__((aligned(16)));

Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/fiber_simple.cc
@@ -1,7 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "sanitizer_common/sanitizer_ucontext.h"
#include "test.h"
#include <ucontext.h>

char stack[64 * 1024] __attribute__((aligned(16)));

Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/fiber_two_threads.cc
@@ -1,7 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: darwin
// UNSUPPORTED: tvos, watchos
#include "sanitizer_common/sanitizer_ucontext.h"
#include "test.h"
#include <ucontext.h>

char stack[64 * 1024] __attribute__((aligned(16)));

Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/test/tsan/test.h
Expand Up @@ -89,3 +89,9 @@ void AnnotateRWLockReleased(const char *f, int l, void *m, long is_w);
AnnotateRWLockAcquired(__FILE__, __LINE__, m, is_w)
#define ANNOTATE_RWLOCK_RELEASED(m, is_w) \
AnnotateRWLockReleased(__FILE__, __LINE__, m, is_w)

#ifdef __APPLE__
#define ASM_SYMBOL(symbol) "_" #symbol
#else
#define ASM_SYMBOL(symbol) #symbol
#endif

0 comments on commit 4d2b942

Please sign in to comment.