Skip to content

Commit 4d2b942

Browse files
committed
[TSan] Support fiber API on macOS
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
1 parent fe8aabf commit 4d2b942

15 files changed

+89
-43
lines changed

compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,22 @@
2020
#include "tsan_interface_ann.h"
2121
#include "sanitizer_common/sanitizer_addrhashmap.h"
2222

23+
#include <errno.h>
2324
#include <libkern/OSAtomic.h>
2425
#include <objc/objc-sync.h>
26+
#include <sys/ucontext.h>
2527

2628
#if defined(__has_include) && __has_include(<xpc/xpc.h>)
2729
#include <xpc/xpc.h>
2830
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
2931

3032
typedef long long_t; // NOLINT
3133

34+
extern "C" {
35+
int getcontext(ucontext_t *ucp) __attribute__((returns_twice));
36+
int setcontext(const ucontext_t *ucp);
37+
}
38+
3239
namespace __tsan {
3340

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

363+
TSAN_INTERCEPTOR(int, swapcontext, ucontext_t *oucp, const ucontext_t *ucp) {
364+
{
365+
SCOPED_INTERCEPTOR_RAW(swapcontext, oucp, ucp);
366+
}
367+
// Bacause of swapcontext() semantics we have no option but to copy its
368+
// impementation here
369+
if (!oucp || !ucp) {
370+
errno = EINVAL;
371+
return -1;
372+
}
373+
ThreadState *thr = cur_thread();
374+
const int UCF_SWAPPED = 0x80000000;
375+
oucp->uc_onstack &= ~UCF_SWAPPED;
376+
thr->ignore_interceptors++;
377+
int ret = getcontext(oucp);
378+
if (!(oucp->uc_onstack & UCF_SWAPPED)) {
379+
thr->ignore_interceptors--;
380+
if (!ret) {
381+
oucp->uc_onstack |= UCF_SWAPPED;
382+
ret = setcontext(ucp);
383+
}
384+
}
385+
return ret;
386+
}
387+
356388
// On macOS, libc++ is always linked dynamically, so intercepting works the
357389
// usual way.
358390
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR

compiler-rt/lib/tsan/rtl/tsan_interface.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
125125
*addr = v;
126126
}
127127

128-
#if !SANITIZER_MAC && !SANITIZER_ANDROID
129128
SANITIZER_INTERFACE_ATTRIBUTE
130129
void *__tsan_get_current_fiber() {
131130
return cur_thread();
@@ -150,7 +149,6 @@ SANITIZER_INTERFACE_ATTRIBUTE
150149
void __tsan_set_fiber_name(void *fiber, const char *name) {
151150
ThreadSetName(static_cast<ThreadState *>(fiber), name);
152151
}
153-
#endif // !SANITIZER_MAC && !SANITIZER_ANDROID
154152
} // extern "C"
155153

156154
void __tsan_acquire(void *addr) {

compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ ThreadState *cur_thread() {
401401
return thr;
402402
}
403403

404+
void set_cur_thread(ThreadState *thr) {
405+
*get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
406+
}
407+
404408
void cur_thread_finalize() {
405409
__sanitizer_sigset_t emptyset;
406410
internal_sigfillset(&emptyset);

compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,37 +73,36 @@ static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
7373
// shadow memory is set up.
7474
static uptr main_thread_identity = 0;
7575
ALIGNED(64) static char main_thread_state[sizeof(ThreadState)];
76+
static ThreadState *main_thread_state_loc = (ThreadState *)main_thread_state;
7677

77-
ThreadState **cur_thread_location() {
78-
ThreadState **thread_identity = (ThreadState **)pthread_self();
79-
return ((uptr)thread_identity == main_thread_identity) ? nullptr
80-
: thread_identity;
78+
static ThreadState **cur_thread_location() {
79+
uptr thread_identity = (uptr)pthread_self();
80+
if (thread_identity == main_thread_identity || main_thread_identity == 0)
81+
return &main_thread_state_loc;
82+
return (ThreadState **)MemToShadow(thread_identity);
8183
}
8284

8385
ThreadState *cur_thread() {
84-
ThreadState **thr_state_loc = cur_thread_location();
85-
if (thr_state_loc == nullptr || main_thread_identity == 0) {
86-
return (ThreadState *)&main_thread_state;
87-
}
88-
ThreadState **fake_tls = (ThreadState **)MemToShadow((uptr)thr_state_loc);
89-
ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
90-
(uptr *)fake_tls, sizeof(ThreadState));
91-
return thr;
86+
return (ThreadState *)SignalSafeGetOrAllocate(
87+
(uptr *)cur_thread_location(), sizeof(ThreadState));
88+
}
89+
90+
void set_cur_thread(ThreadState *thr) {
91+
*cur_thread_location() = thr;
9292
}
9393

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

@@ -265,11 +264,11 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
265264
// The pointer to the ThreadState object is stored in the shadow memory
266265
// of the tls.
267266
uptr tls_end = tls_addr + tls_size;
268-
ThreadState **thr_state_loc = cur_thread_location();
269-
if (thr_state_loc == nullptr) {
267+
uptr thread_identity = (uptr)pthread_self();
268+
if (thread_identity == main_thread_identity) {
270269
MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, tls_size);
271270
} else {
272-
uptr thr_state_start = (uptr)thr_state_loc;
271+
uptr thr_state_start = thread_identity;
273272
uptr thr_state_end = thr_state_start + sizeof(uptr);
274273
CHECK_GE(thr_state_start, tls_addr);
275274
CHECK_LE(thr_state_start, tls_addr + tls_size);

compiler-rt/lib/tsan/rtl/tsan_rtl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ struct ThreadState {
464464
#if !SANITIZER_GO
465465
#if SANITIZER_MAC || SANITIZER_ANDROID
466466
ThreadState *cur_thread();
467+
void set_cur_thread(ThreadState *thr);
467468
void cur_thread_finalize();
468469
INLINE void cur_thread_init() { }
469470
#else

compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
405405
}
406406
}
407407

408-
#if !SANITIZER_MAC && !SANITIZER_ANDROID && !SANITIZER_GO
408+
#if !SANITIZER_GO
409409
void FiberSwitchImpl(ThreadState *from, ThreadState *to) {
410410
Processor *proc = from->proc();
411411
ProcUnwire(proc, from);

compiler-rt/test/lsan/TestCases/swapcontext.cc

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@
66
// RUN: %env_lsan_opts= not %run %t foo 2>&1 | FileCheck %s
77
// UNSUPPORTED: arm,powerpc64
88

9+
#include "sanitizer_common/sanitizer_ucontext.h"
910
#include <stdio.h>
10-
#if defined(__APPLE__)
11-
// Note: ucontext.h is deprecated on OSX, so this test may stop working
12-
// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now.
13-
#define _XOPEN_SOURCE 1
14-
#endif
15-
#include <ucontext.h>
1611
#include <unistd.h>
1712

1813
const int kStackSize = 1 << 20;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifdef __APPLE__
2+
// ucontext.h is deprecated on macOS, so tests that include it may stop working
3+
// someday. We define _XOPEN_SOURCE to keep using ucontext.h for now.
4+
#ifdef _STRUCT_UCONTEXT
5+
#error incomplete ucontext_t already defined, change #include order
6+
#endif
7+
#define _XOPEN_SOURCE 700
8+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
9+
#endif
10+
11+
#include <ucontext.h>

compiler-rt/test/tsan/fiber_asm.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
22
// REQUIRES: x86_64-target-arch
3-
// UNSUPPORTED: darwin
3+
// UNSUPPORTED: tvos, watchos
44
#include "test.h"
55

66
struct ucontext {
@@ -13,8 +13,8 @@ extern "C" {
1313
void ucontext_trampoline();
1414
}
1515

16-
__asm__(".global ucontext_do_switch\n"
17-
"ucontext_do_switch:\n\t"
16+
__asm__(".global " ASM_SYMBOL(ucontext_do_switch) "\n"
17+
ASM_SYMBOL(ucontext_do_switch) ":\n\t"
1818
"pushq %rbp\n\t"
1919
"pushq %r15\n\t"
2020
"pushq %r14\n\t"
@@ -31,8 +31,8 @@ __asm__(".global ucontext_do_switch\n"
3131
"popq %rbp\n\t"
3232
"retq");
3333

34-
__asm__(".global ucontext_trampoline\n"
35-
"ucontext_trampoline:\n\t"
34+
__asm__(".global " ASM_SYMBOL(ucontext_trampoline) "\n"
35+
ASM_SYMBOL(ucontext_trampoline) ":\n\t"
3636
".cfi_startproc\n\t"
3737
".cfi_undefined rip\n\t"
3838
"movq %r12, %rdi\n\t"

compiler-rt/test/tsan/fiber_from_thread.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2-
// UNSUPPORTED: darwin
2+
// UNSUPPORTED: tvos, watchos
3+
#include "sanitizer_common/sanitizer_ucontext.h"
34
#include "test.h"
4-
#include <ucontext.h>
55

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

compiler-rt/test/tsan/fiber_longjmp.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2-
// UNSUPPORTED: darwin
2+
// UNSUPPORTED: tvos, watchos
3+
#include "sanitizer_common/sanitizer_ucontext.h"
34
#include "test.h"
45
#include <setjmp.h>
5-
#include <ucontext.h>
66

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

compiler-rt/test/tsan/fiber_race.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s
2-
// UNSUPPORTED: darwin
2+
// UNSUPPORTED: tvos, watchos
3+
#include "sanitizer_common/sanitizer_ucontext.h"
34
#include "test.h"
4-
#include <ucontext.h>
55

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

compiler-rt/test/tsan/fiber_simple.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2-
// UNSUPPORTED: darwin
2+
// UNSUPPORTED: tvos, watchos
3+
#include "sanitizer_common/sanitizer_ucontext.h"
34
#include "test.h"
4-
#include <ucontext.h>
55

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

compiler-rt/test/tsan/fiber_two_threads.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2-
// UNSUPPORTED: darwin
2+
// UNSUPPORTED: tvos, watchos
3+
#include "sanitizer_common/sanitizer_ucontext.h"
34
#include "test.h"
4-
#include <ucontext.h>
55

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

compiler-rt/test/tsan/test.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,9 @@ void AnnotateRWLockReleased(const char *f, int l, void *m, long is_w);
8989
AnnotateRWLockAcquired(__FILE__, __LINE__, m, is_w)
9090
#define ANNOTATE_RWLOCK_RELEASED(m, is_w) \
9191
AnnotateRWLockReleased(__FILE__, __LINE__, m, is_w)
92+
93+
#ifdef __APPLE__
94+
#define ASM_SYMBOL(symbol) "_" #symbol
95+
#else
96+
#define ASM_SYMBOL(symbol) #symbol
97+
#endif

0 commit comments

Comments
 (0)