summaryrefslogtreecommitdiff
path: root/libsanitizer/tsan
diff options
context:
space:
mode:
Diffstat (limited to 'libsanitizer/tsan')
-rw-r--r--libsanitizer/tsan/Makefile.am5
-rw-r--r--libsanitizer/tsan/Makefile.in21
-rw-r--r--libsanitizer/tsan/tsan_clock.cc2
-rw-r--r--libsanitizer/tsan/tsan_defs.h2
-rw-r--r--libsanitizer/tsan/tsan_flags.cc10
-rw-r--r--libsanitizer/tsan/tsan_interceptors.cc197
-rw-r--r--libsanitizer/tsan/tsan_interceptors.h12
-rw-r--r--libsanitizer/tsan/tsan_libdispatch_mac.cc70
-rw-r--r--libsanitizer/tsan/tsan_malloc_mac.cc67
-rw-r--r--libsanitizer/tsan/tsan_mman.h1
-rw-r--r--libsanitizer/tsan/tsan_new_delete.cc8
-rw-r--r--libsanitizer/tsan/tsan_platform.h4
-rw-r--r--libsanitizer/tsan/tsan_platform_linux.cc93
-rw-r--r--libsanitizer/tsan/tsan_platform_mac.cc123
-rw-r--r--libsanitizer/tsan/tsan_platform_posix.cc122
-rw-r--r--libsanitizer/tsan/tsan_report.cc19
-rw-r--r--libsanitizer/tsan/tsan_rtl.cc3
-rw-r--r--libsanitizer/tsan/tsan_rtl.h10
-rw-r--r--libsanitizer/tsan/tsan_rtl_aarch64.S204
-rw-r--r--libsanitizer/tsan/tsan_rtl_thread.cc8
20 files changed, 790 insertions, 191 deletions
diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am
index dc22db5d4460..6776923363a3 100644
--- a/libsanitizer/tsan/Makefile.am
+++ b/libsanitizer/tsan/Makefile.am
@@ -21,6 +21,8 @@ tsan_files = \
tsan_interface_atomic.cc \
tsan_interface.cc \
tsan_interface_java.cc \
+ tsan_libdispatch_mac.cc \
+ tsan_malloc_mac.cc \
tsan_md5.cc \
tsan_mman.cc \
tsan_mutex.cc \
@@ -28,6 +30,7 @@ tsan_files = \
tsan_new_delete.cc \
tsan_platform_linux.cc \
tsan_platform_mac.cc \
+ tsan_platform_posix.cc \
tsan_platform_windows.cc \
tsan_report.cc \
tsan_rtl.cc \
@@ -41,7 +44,7 @@ tsan_files = \
tsan_sync.cc
libtsan_la_SOURCES = $(tsan_files)
-EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S
+EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
libtsan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
if LIBBACKTRACE_SUPPORTED
diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in
index 6d5d2efbf577..14a4202e3277 100644
--- a/libsanitizer/tsan/Makefile.in
+++ b/libsanitizer/tsan/Makefile.in
@@ -107,12 +107,14 @@ am__DEPENDENCIES_1 =
am__objects_1 = tsan_clock.lo tsan_fd.lo tsan_flags.lo \
tsan_ignoreset.lo tsan_interceptors.lo tsan_interface_ann.lo \
tsan_interface_atomic.lo tsan_interface.lo \
- tsan_interface_java.lo tsan_md5.lo tsan_mman.lo tsan_mutex.lo \
+ tsan_interface_java.lo tsan_libdispatch_mac.lo \
+ tsan_malloc_mac.lo tsan_md5.lo tsan_mman.lo tsan_mutex.lo \
tsan_mutexset.lo tsan_new_delete.lo tsan_platform_linux.lo \
- tsan_platform_mac.lo tsan_platform_windows.lo tsan_report.lo \
- tsan_rtl.lo tsan_rtl_mutex.lo tsan_rtl_report.lo \
- tsan_rtl_thread.lo tsan_stack_trace.lo tsan_stat.lo \
- tsan_suppressions.lo tsan_symbolize.lo tsan_sync.lo
+ tsan_platform_mac.lo tsan_platform_posix.lo \
+ tsan_platform_windows.lo tsan_report.lo tsan_rtl.lo \
+ tsan_rtl_mutex.lo tsan_rtl_report.lo tsan_rtl_thread.lo \
+ tsan_stack_trace.lo tsan_stat.lo tsan_suppressions.lo \
+ tsan_symbolize.lo tsan_sync.lo
am_libtsan_la_OBJECTS = $(am__objects_1)
libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
@@ -318,6 +320,8 @@ tsan_files = \
tsan_interface_atomic.cc \
tsan_interface.cc \
tsan_interface_java.cc \
+ tsan_libdispatch_mac.cc \
+ tsan_malloc_mac.cc \
tsan_md5.cc \
tsan_mman.cc \
tsan_mutex.cc \
@@ -325,6 +329,7 @@ tsan_files = \
tsan_new_delete.cc \
tsan_platform_linux.cc \
tsan_platform_mac.cc \
+ tsan_platform_posix.cc \
tsan_platform_windows.cc \
tsan_report.cc \
tsan_rtl.cc \
@@ -338,7 +343,7 @@ tsan_files = \
tsan_sync.cc
libtsan_la_SOURCES = $(tsan_files)
-EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S
+EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
libtsan_la_LIBADD = \
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
$(top_builddir)/interception/libinterception.la \
@@ -473,6 +478,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_libdispatch_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_malloc_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
@@ -480,9 +487,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_aarch64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@
diff --git a/libsanitizer/tsan/tsan_clock.cc b/libsanitizer/tsan/tsan_clock.cc
index d925c9cfd83d..037afc83fc6c 100644
--- a/libsanitizer/tsan/tsan_clock.cc
+++ b/libsanitizer/tsan/tsan_clock.cc
@@ -88,8 +88,6 @@
namespace __tsan {
-const unsigned kInvalidTid = (unsigned)-1;
-
ThreadClock::ThreadClock(unsigned tid, unsigned reused)
: tid_(tid)
, reused_(reused + 1) { // 0 has special meaning
diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h
index 21b68356e7bc..259b30bee5db 100644
--- a/libsanitizer/tsan/tsan_defs.h
+++ b/libsanitizer/tsan/tsan_defs.h
@@ -81,6 +81,8 @@ const bool kCollectHistory = false;
const bool kCollectHistory = true;
#endif
+const unsigned kInvalidTid = (unsigned)-1;
+
// The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes.
diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc
index 3e89375187db..4cec0ac09f41 100644
--- a/libsanitizer/tsan/tsan_flags.cc
+++ b/libsanitizer/tsan/tsan_flags.cc
@@ -59,9 +59,13 @@ void InitializeFlags(Flags *f, const char *env) {
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.allow_addr2line = true;
-#ifndef SANITIZER_GO
- cf.detect_deadlocks = true;
-#endif
+ if (kGoMode) {
+ // Does not work as expected for Go: runtime handles SIGABRT and crashes.
+ cf.abort_on_error = false;
+ // Go does not have mutexes.
+ } else {
+ cf.detect_deadlocks = true;
+ }
cf.print_suppressions = false;
cf.stack_trace_format = " #%n %f %S %M";
cf.exitcode = 66;
diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc
index 16465b96b6c6..90c1d7bf2436 100644
--- a/libsanitizer/tsan/tsan_interceptors.cc
+++ b/libsanitizer/tsan/tsan_interceptors.cc
@@ -26,16 +26,28 @@
#include "tsan_mman.h"
#include "tsan_fd.h"
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
using namespace __tsan; // NOLINT
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
#define __errno_location __error
-#define __libc_realloc __realloc
-#define __libc_calloc __calloc
#define stdout __stdoutp
#define stderr __stderrp
#endif
+#if SANITIZER_FREEBSD
+#define __libc_realloc __realloc
+#define __libc_calloc __calloc
+#elif SANITIZER_MAC
+#define __libc_malloc REAL(malloc)
+#define __libc_realloc REAL(realloc)
+#define __libc_calloc REAL(calloc)
+#define __libc_free REAL(free)
+#endif
+
#if SANITIZER_LINUX || SANITIZER_FREEBSD
#define PTHREAD_CREATE_DETACHED 1
#elif SANITIZER_MAC
@@ -78,12 +90,13 @@ extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
extern "C" int pthread_setspecific(unsigned key, const void *v);
DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
-extern "C" int pthread_yield();
extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset);
// REAL(sigfillset) defined in common interceptors.
DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set)
DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
extern "C" void *pthread_self();
extern "C" void _exit(int status);
extern "C" int *__errno_location();
@@ -100,7 +113,9 @@ const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int EINVAL = 22;
const int EBUSY = 16;
const int EOWNERDEAD = 130;
+#if !SANITIZER_MAC
const int EPOLL_CTL_ADD = 1;
+#endif
const int SIGILL = 4;
const int SIGABRT = 6;
const int SIGFPE = 8;
@@ -115,7 +130,9 @@ const int SIGBUS = 7;
const int SIGSYS = 31;
#endif
void *const MAP_FAILED = (void*)-1;
+#if !SANITIZER_MAC
const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
+#endif
const int MAP_FIXED = 0x10;
typedef long long_t; // NOLINT
@@ -245,17 +262,6 @@ ScopedInterceptor::~ScopedInterceptor() {
}
}
-#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
- SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- if (REAL(func) == 0) { \
- Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
- Die(); \
- } \
- if (thr->ignore_interceptors || thr->in_ignored_lib) \
- return REAL(func)(__VA_ARGS__); \
-/**/
-
-#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
#if SANITIZER_FREEBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
@@ -370,6 +376,7 @@ static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
return res;
}
+#if !SANITIZER_MAC
static void on_exit_wrapper(int status, void *arg) {
ThreadState *thr = cur_thread();
uptr pc = 0;
@@ -394,6 +401,7 @@ TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
ThreadIgnoreEnd(thr, pc);
return res;
}
+#endif
// Cleanup old bufs.
static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
@@ -430,8 +438,12 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
static void LongJmp(ThreadState *thr, uptr *env) {
#if SANITIZER_FREEBSD
uptr mangled_sp = env[2];
-#else
+#elif defined(SANITIZER_LINUX)
+# ifdef __aarch64__
+ uptr mangled_sp = env[13];
+# else
uptr mangled_sp = env[6];
+# endif
#endif // SANITIZER_FREEBSD
// Find the saved buf by mangled_sp.
for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
@@ -517,6 +529,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) {
REAL(siglongjmp)(env, val);
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
if (cur_thread()->in_symbolizer)
return __libc_malloc(size);
@@ -583,6 +596,7 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
return user_alloc_usable_size(p);
}
+#endif
TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
SCOPED_TSAN_INTERCEPTOR(strlen, s);
@@ -607,13 +621,18 @@ TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
MemoryAccessRange(thr, pc, (uptr)dst, size, true);
MemoryAccessRange(thr, pc, (uptr)src, size, false);
}
- return internal_memcpy(dst, src, size);
+ // On OS X, calling internal_memcpy here will cause memory corruptions,
+ // because memcpy and memmove are actually aliases of the same implementation.
+ // We need to use internal_memmove here.
+ return internal_memmove(dst, src, size);
}
TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
- MemoryAccessRange(thr, pc, (uptr)dst, n, true);
- MemoryAccessRange(thr, pc, (uptr)src, n, false);
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
+ MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+ MemoryAccessRange(thr, pc, (uptr)src, n, false);
+ }
return REAL(memmove)(dst, src, n);
}
@@ -626,6 +645,7 @@ TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
char *res = REAL(strchrnul)(s, c);
@@ -633,6 +653,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
READ_STRING(thr, pc, s, len);
return res;
}
+#endif
TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strrchr, s, c);
@@ -676,8 +697,8 @@ static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
return true;
}
-TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
- int flags, int fd, unsigned off) {
+TSAN_INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
+ int fd, OFF_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED;
@@ -690,9 +711,9 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
return res;
}
-#if !SANITIZER_FREEBSD
-TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
- int flags, int fd, u64 off) {
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
+ int fd, OFF64_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED;
@@ -720,7 +741,7 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
return res;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
@@ -730,6 +751,7 @@ TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
#define TSAN_MAYBE_INTERCEPT_MEMALIGN
#endif
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
@@ -739,8 +761,9 @@ TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(valloc, sz);
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
+#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached());
@@ -751,11 +774,13 @@ TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
#define TSAN_MAYBE_INTERCEPT_PVALLOC
#endif
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
*memptr = user_alloc(thr, pc, sz, align);
return 0;
}
+#endif
// Used in thread-safe function static initialization.
extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
@@ -785,6 +810,19 @@ extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) {
atomic_store(g, 0, memory_order_relaxed);
}
+namespace __tsan {
+void DestroyThreadState() {
+ ThreadState *thr = cur_thread();
+ ThreadFinish(thr);
+ ThreadSignalContext *sctx = thr->signal_ctx;
+ if (sctx) {
+ thr->signal_ctx = 0;
+ UnmapOrDie(sctx, sizeof(*sctx));
+ }
+ cur_thread_finalize();
+}
+} // namespace __tsan
+
static void thread_finalize(void *v) {
uptr iter = (uptr)v;
if (iter > 1) {
@@ -794,15 +832,7 @@ static void thread_finalize(void *v) {
}
return;
}
- {
- ThreadState *thr = cur_thread();
- ThreadFinish(thr);
- ThreadSignalContext *sctx = thr->signal_ctx;
- if (sctx) {
- thr->signal_ctx = 0;
- UnmapOrDie(sctx, sizeof(*sctx));
- }
- }
+ DestroyThreadState();
}
@@ -829,7 +859,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
}
ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
- pthread_yield();
+ internal_sched_yield();
ThreadStart(thr, tid, GetTid());
atomic_store(&p->tid, 0, memory_order_release);
}
@@ -889,7 +919,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
// before the new thread got a chance to acquire from it in ThreadStart.
atomic_store(&p.tid, tid, memory_order_release);
while (atomic_load(&p.tid, memory_order_acquire) != 0)
- pthread_yield();
+ internal_sched_yield();
}
if (attr == &myattr)
pthread_attr_destroy(&myattr);
@@ -1092,6 +1122,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
int res = REAL(pthread_mutex_timedlock)(m, abstime);
@@ -1100,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
}
return res;
}
+#endif
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
int res = REAL(pthread_spin_init)(m, pshared);
@@ -1143,6 +1176,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
int res = REAL(pthread_spin_unlock)(m);
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
@@ -1180,6 +1214,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
@@ -1188,6 +1223,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
@@ -1207,6 +1243,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
@@ -1215,6 +1252,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
@@ -1223,6 +1261,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
@@ -1248,12 +1287,17 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
if (o == 0 || f == 0)
return EINVAL;
- atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
+ atomic_uint32_t *a;
+ if (!SANITIZER_MAC)
+ a = static_cast<atomic_uint32_t*>(o);
+ else // On OS X, pthread_once_t has a header with a long-sized signature.
+ a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t)));
u32 v = atomic_load(a, memory_order_acquire);
if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
memory_order_relaxed)) {
@@ -1263,7 +1307,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
atomic_store(a, 2, memory_order_release);
} else {
while (v != 2) {
- pthread_yield();
+ internal_sched_yield();
v = atomic_load(a, memory_order_acquire);
}
if (!thr->in_ignored_lib)
@@ -1272,7 +1316,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
return 0;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1284,7 +1328,7 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
#endif
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(stat)(path, buf);
@@ -1295,7 +1339,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#endif
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1306,7 +1350,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT___XSTAT64
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1317,7 +1361,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT_STAT64
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1329,7 +1373,7 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
#endif
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
READ_STRING(thr, pc, path, 0);
return REAL(lstat)(path, buf);
@@ -1340,7 +1384,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#endif
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1351,7 +1395,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT___LXSTAT64
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0);
@@ -1362,7 +1406,7 @@ TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT_LSTAT64
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
if (fd > 0)
@@ -1375,7 +1419,7 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
#endif
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
@@ -1388,7 +1432,7 @@ TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
#endif
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
if (fd > 0)
@@ -1400,7 +1444,7 @@ TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
#define TSAN_MAYBE_INTERCEPT___FXSTAT64
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
if (fd > 0)
@@ -1421,7 +1465,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
return fd;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
READ_STRING(thr, pc, name, 0);
@@ -1444,7 +1488,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
return fd;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
READ_STRING(thr, pc, name, 0);
@@ -1474,6 +1518,7 @@ TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
return newfd2;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
int newfd2 = REAL(dup3)(oldfd, newfd, flags);
@@ -1481,8 +1526,9 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
FdDup(thr, pc, oldfd, newfd2, false);
return newfd2;
}
+#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
int fd = REAL(eventfd)(initval, flags);
@@ -1495,7 +1541,7 @@ TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
#define TSAN_MAYBE_INTERCEPT_EVENTFD
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
if (fd >= 0)
@@ -1510,7 +1556,7 @@ TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
#define TSAN_MAYBE_INTERCEPT_SIGNALFD
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init, int fake) {
SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
int fd = REAL(inotify_init)(fake);
@@ -1523,7 +1569,7 @@ TSAN_INTERCEPTOR(int, inotify_init, int fake) {
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
int fd = REAL(inotify_init1)(flags);
@@ -1577,7 +1623,7 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) {
return res;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create, int size) {
SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
int fd = REAL(epoll_create)(size);
@@ -1590,7 +1636,7 @@ TSAN_INTERCEPTOR(int, epoll_create, int size) {
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
int fd = REAL(epoll_create1)(flags);
@@ -1610,7 +1656,7 @@ TSAN_INTERCEPTOR(int, close, int fd) {
return REAL(close)(fd);
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0)
@@ -1623,7 +1669,7 @@ TSAN_INTERCEPTOR(int, __close, int fd) {
#endif
// glibc guts
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
int fds[64];
@@ -1647,6 +1693,7 @@ TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
int res = REAL(pipe2)(pipefd, flags);
@@ -1654,6 +1701,7 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
return res;
}
+#endif
TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
@@ -1704,7 +1752,7 @@ TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
return res;
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
void *res = REAL(tmpfile64)(fake);
@@ -1771,7 +1819,7 @@ TSAN_INTERCEPTOR(int, closedir, void *dirp) {
return REAL(closedir)(dirp);
}
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
if (epfd >= 0)
@@ -1788,7 +1836,7 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL
#endif
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
if (epfd >= 0)
@@ -2087,6 +2135,7 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
return WRAP(fork)(fake);
}
+#if !SANITIZER_MAC
typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
void *data);
struct dl_iterate_phdr_data {
@@ -2130,6 +2179,7 @@ TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
return res;
}
+#endif
static int OnExit(ThreadState *thr) {
int status = Finalize(thr);
@@ -2143,6 +2193,7 @@ struct TsanInterceptorContext {
const uptr pc;
};
+#if !SANITIZER_MAC
static void HandleRecvmsg(ThreadState *thr, uptr pc,
__sanitizer_msghdr *msg) {
int fds[64];
@@ -2150,6 +2201,7 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
for (int i = 0; i < cnt; i++)
FdEventCreate(thr, pc, fds[i]);
}
+#endif
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getaddrinfo() and fopen())
@@ -2264,9 +2316,11 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+#if !SANITIZER_MAC
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, msg)
+#endif
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
if (TsanThread *t = GetCurrentThread()) { \
@@ -2298,6 +2352,7 @@ struct ScopedSyscall {
}
};
+#if !SANITIZER_MAC
static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
TSAN_SYSCALL();
MemoryAccessRange(thr, pc, p, s, write);
@@ -2351,6 +2406,7 @@ static void syscall_post_fork(uptr pc, int pid) {
ForkParentAfter(thr, pc);
}
}
+#endif
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
@@ -2401,24 +2457,29 @@ static void finalize(void *arg) {
Die();
}
+#if !SANITIZER_MAC
static void unreachable() {
Report("FATAL: ThreadSanitizer: unreachable called\n");
Die();
}
+#endif
void InitializeInterceptors() {
+#if !SANITIZER_MAC
// We need to setup it early, because functions like dlsym() can call it.
REAL(memset) = internal_memset;
REAL(memcpy) = internal_memcpy;
+#endif
// Instruct libc malloc to consume less memory.
-#if !SANITIZER_FREEBSD
+#if SANITIZER_LINUX
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
#endif
InitializeCommonInterceptors();
+#if !SANITIZER_MAC
// We can not use TSAN_INTERCEPT to get setjmp addr,
// because it does &setjmp and setjmp is not present in some versions of libc.
using __interception::GetRealFunctionAddress;
@@ -2426,6 +2487,7 @@ void InitializeInterceptors() {
GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0);
GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
+#endif
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
@@ -2565,9 +2627,12 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(__cxa_atexit);
TSAN_INTERCEPT(_exit);
+#if !SANITIZER_MAC
// Need to setup it, because interceptors check that the function is resolved.
// But atexit is emitted directly into the module, so can't be resolved.
REAL(atexit) = (int(*)(void(*)()))unreachable;
+#endif
+
if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
Printf("ThreadSanitizer: failed to setup atexit callback\n");
Die();
diff --git a/libsanitizer/tsan/tsan_interceptors.h b/libsanitizer/tsan/tsan_interceptors.h
index 4a246c647a06..ed68eb96bf81 100644
--- a/libsanitizer/tsan/tsan_interceptors.h
+++ b/libsanitizer/tsan/tsan_interceptors.h
@@ -24,6 +24,18 @@ class ScopedInterceptor {
(void)pc; \
/**/
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ if (REAL(func) == 0) { \
+ Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+ Die(); \
+ } \
+ if (thr->ignore_interceptors || thr->in_ignored_lib) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+
#if SANITIZER_FREEBSD
#define __libc_free __free
#define __libc_malloc __malloc
diff --git a/libsanitizer/tsan/tsan_libdispatch_mac.cc b/libsanitizer/tsan/tsan_libdispatch_mac.cc
new file mode 100644
index 000000000000..5b39665d5d20
--- /dev/null
+++ b/libsanitizer/tsan/tsan_libdispatch_mac.cc
@@ -0,0 +1,70 @@
+//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific libdispatch (GCD) support.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+namespace __tsan {
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+ dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
+ atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 &&
+ atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+ block();
+ Release(thr, pc, (uptr)a);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(thr, pc, (uptr)a);
+ }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+ void *context, dispatch_function_t function) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
+ WRAP(dispatch_once)(predicate, ^(void) {
+ function(context);
+ });
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
diff --git a/libsanitizer/tsan/tsan_malloc_mac.cc b/libsanitizer/tsan/tsan_malloc_mac.cc
new file mode 100644
index 000000000000..97090773ca76
--- /dev/null
+++ b/libsanitizer/tsan/tsan_malloc_mac.cc
@@ -0,0 +1,67 @@
+//===-- tsan_malloc_mac.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "tsan_interceptors.h"
+#include "tsan_stack_trace.h"
+
+using namespace __tsan;
+#define COMMON_MALLOC_ZONE_NAME "tsan"
+#define COMMON_MALLOC_ENTER()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited)
+#define COMMON_MALLOC_FORCE_LOCK()
+#define COMMON_MALLOC_FORCE_UNLOCK()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ void *p = \
+ user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment)
+#define COMMON_MALLOC_MALLOC(size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(malloc)(size); \
+ SCOPED_INTERCEPTOR_RAW(malloc, size); \
+ void *p = user_alloc(thr, pc, size)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(realloc)(ptr, size); \
+ SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
+ void *p = user_realloc(thr, pc, ptr, size)
+#define COMMON_MALLOC_CALLOC(count, size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(calloc)(count, size); \
+ SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
+ void *p = user_calloc(thr, pc, size, count)
+#define COMMON_MALLOC_VALLOC(size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(valloc)(size); \
+ SCOPED_INTERCEPTOR_RAW(valloc, size); \
+ void *p = user_alloc(thr, pc, size, GetPageSizeCached())
+#define COMMON_MALLOC_FREE(ptr) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(free)(ptr); \
+ SCOPED_INTERCEPTOR_RAW(free, ptr); \
+ user_free(thr, pc, ptr)
+#define COMMON_MALLOC_SIZE(ptr) \
+ uptr size = user_alloc_usable_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats)
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
+#define COMMON_MALLOC_IGNORE_INVALID_FREE false
+#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("free_common(%p) -- attempting to free unallocated memory.\n", ptr);
+#define COMMON_MALLOC_NAMESPACE __tsan
+
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+#endif
diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h
index da7cf211940a..a7c91fae5e16 100644
--- a/libsanitizer/tsan/tsan_mman.h
+++ b/libsanitizer/tsan/tsan_mman.h
@@ -18,6 +18,7 @@ namespace __tsan {
const uptr kDefaultAlignment = 16;
void InitializeAllocator();
+void ReplaceSystemMalloc();
void AllocatorThreadStart(ThreadState *thr);
void AllocatorThreadFinish(ThreadState *thr);
void AllocatorPrintStats();
diff --git a/libsanitizer/tsan/tsan_new_delete.cc b/libsanitizer/tsan/tsan_new_delete.cc
index ad4ed449751b..ba8474e07141 100644
--- a/libsanitizer/tsan/tsan_new_delete.cc
+++ b/libsanitizer/tsan/tsan_new_delete.cc
@@ -9,6 +9,7 @@
//
// Interceptors for operators new and delete.
//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "tsan_interceptors.h"
@@ -18,6 +19,13 @@ namespace std {
struct nothrow_t {};
} // namespace std
+DECLARE_REAL(void *, malloc, uptr size)
+DECLARE_REAL(void, free, void *ptr)
+#if SANITIZER_MAC
+#define __libc_malloc REAL(malloc)
+#define __libc_free REAL(free)
+#endif
+
#define OPERATOR_NEW_BODY(mangled_name) \
if (cur_thread()->in_symbolizer) \
return __libc_malloc(size); \
diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h
index 07d11e572ff2..f34f577ec030 100644
--- a/libsanitizer/tsan/tsan_platform.h
+++ b/libsanitizer/tsan/tsan_platform.h
@@ -338,6 +338,8 @@ uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
}
void InitializePlatform();
+void CheckAndProtect();
+void InitializeShadowMemoryPlatform();
void FlushShadowMemory();
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
@@ -351,6 +353,8 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime,
void(*cleanup)(void *arg), void *arg);
+void DestroyThreadState();
+
} // namespace __tsan
#endif // TSAN_PLATFORM_H
diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc
index f409a8b221c0..a2e89f22da6f 100644
--- a/libsanitizer/tsan/tsan_platform_linux.cc
+++ b/libsanitizer/tsan/tsan_platform_linux.cc
@@ -130,17 +130,6 @@ void FlushShadowMemory() {
}
#ifndef SANITIZER_GO
-static void ProtectRange(uptr beg, uptr end) {
- CHECK_LE(beg, end);
- if (beg == end)
- return;
- if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
- Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
- Printf("FATAL: Make sure you are not using unlimited stack\n");
- Die();
- }
-}
-
// Mark shadow for .rodata sections with the special kShadowRodata marker.
// Accesses to .rodata can't race, so this saves time, memory and trace space.
static void MapRodata() {
@@ -198,58 +187,7 @@ static void MapRodata() {
internal_close(fd);
}
-void InitializeShadowMemory() {
- // Map memory shadow.
- uptr shadow =
- (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
- if (shadow != kShadowBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie (%p, %p).\n", shadow, kShadowBeg);
- Die();
- }
- // This memory range is used for thread stacks and large user mmaps.
- // Frequently a thread uses only a small part of stack and similarly
- // a program uses a small part of large mmap. On some programs
- // we see 20% memory usage reduction without huge pages for this range.
- // FIXME: don't use constants here.
-#if defined(__x86_64__)
- const uptr kMadviseRangeBeg = 0x7f0000000000ull;
- const uptr kMadviseRangeSize = 0x010000000000ull;
-#elif defined(__mips64)
- const uptr kMadviseRangeBeg = 0xff00000000ull;
- const uptr kMadviseRangeSize = 0x0100000000ull;
-#elif defined(__aarch64__)
- const uptr kMadviseRangeBeg = 0x7e00000000ull;
- const uptr kMadviseRangeSize = 0x0100000000ull;
-#endif
- NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
- kMadviseRangeSize * kShadowMultiplier);
- // Meta shadow is compressing and we don't flush it,
- // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
- // On one program it reduces memory consumption from 5GB to 2.5GB.
- NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
- if (common_flags()->use_madv_dontdump)
- DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
- DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
- kShadowBeg, kShadowEnd,
- (kShadowEnd - kShadowBeg) >> 30);
-
- // Map meta shadow.
- uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
- uptr meta =
- (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
- if (meta != kMetaShadowBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
- Die();
- }
- if (common_flags()->use_madv_dontdump)
- DontDumpShadowMemory(meta, meta_size);
- DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
- meta, meta + meta_size, meta_size >> 30);
-
+void InitializeShadowMemoryPlatform() {
MapRodata();
}
@@ -293,31 +231,6 @@ static void InitDataSeg() {
CHECK_LT((uptr)&g_data_start, g_data_end);
}
-static void CheckAndProtect() {
- // Ensure that the binary is indeed compiled with -pie.
- MemoryMappingLayout proc_maps(true);
- uptr p, end;
- while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) {
- if (IsAppMem(p))
- continue;
- if (p >= kHeapMemEnd &&
- p < HeapEnd())
- continue;
- if (p >= kVdsoBeg) // vdso
- break;
- Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
- Die();
- }
-
- ProtectRange(kLoAppMemEnd, kShadowBeg);
- ProtectRange(kShadowEnd, kMetaShadowBeg);
- ProtectRange(kMetaShadowEnd, kTraceMemBeg);
- // Memory for traces is mapped lazily in MapThreadTrace.
- // Protect the whole range for now, so that user does not map something here.
- ProtectRange(kTraceMemBeg, kTraceMemEnd);
- ProtectRange(kTraceMemEnd, kHeapMemBeg);
- ProtectRange(HeapEnd(), kHiAppMemBeg);
-}
#endif // #ifndef SANITIZER_GO
void InitializePlatform() {
@@ -416,6 +329,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
}
#endif
+#ifndef SANITIZER_GO
+void ReplaceSystemMalloc() { }
+#endif
+
} // namespace __tsan
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
diff --git a/libsanitizer/tsan/tsan_platform_mac.cc b/libsanitizer/tsan/tsan_platform_mac.cc
index a2ae26f5341e..e1405ffeabf1 100644
--- a/libsanitizer/tsan/tsan_platform_mac.cc
+++ b/libsanitizer/tsan/tsan_platform_mac.cc
@@ -13,8 +13,10 @@
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC
+#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
@@ -38,6 +40,62 @@
namespace __tsan {
+#ifndef SANITIZER_GO
+static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
+ atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
+ void *val = (void *)atomic_load_relaxed(a);
+ atomic_signal_fence(memory_order_acquire); // Turns the previous load into
+ // acquire wrt signals.
+ if (UNLIKELY(val == nullptr)) {
+ val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ CHECK(val);
+ void *cmp = nullptr;
+ if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val,
+ memory_order_acq_rel)) {
+ internal_munmap(val, size);
+ val = cmp;
+ }
+ }
+ return val;
+}
+
+// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
+// problematic, because there are several places where interceptors are called
+// when TLVs are not accessible (early process startup, thread cleanup, ...).
+// The following provides a "poor man's TLV" implementation, where we use the
+// shadow memory of the pointer returned by pthread_self() to store a pointer to
+// the ThreadState object. The main thread's ThreadState pointer is stored
+// separately in a static variable, because we need to access it even before the
+// shadow memory is set up.
+static uptr main_thread_identity = 0;
+static ThreadState *main_thread_state = nullptr;
+
+ThreadState *cur_thread() {
+ ThreadState **fake_tls;
+ uptr thread_identity = (uptr)pthread_self();
+ if (thread_identity == main_thread_identity || main_thread_identity == 0) {
+ fake_tls = &main_thread_state;
+ } else {
+ fake_tls = (ThreadState **)MemToShadow(thread_identity);
+ }
+ ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
+ (uptr *)fake_tls, sizeof(ThreadState));
+ return 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() {
+ uptr thread_identity = (uptr)pthread_self();
+ CHECK_NE(thread_identity, main_thread_identity);
+ ThreadState **fake_tls = (ThreadState **)MemToShadow(thread_identity);
+ internal_munmap(*fake_tls, sizeof(ThreadState));
+ *fake_tls = nullptr;
+}
+#endif
+
uptr GetShadowMemoryConsumption() {
return 0;
}
@@ -49,28 +107,57 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
}
#ifndef SANITIZER_GO
-void InitializeShadowMemory() {
- uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
- kShadowEnd - kShadowBeg);
- if (shadow != kShadowBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie.\n");
- Die();
+void InitializeShadowMemoryPlatform() { }
+
+// On OS X, GCD worker threads are created without a call to pthread_create. We
+// need to properly register these threads with ThreadCreate and ThreadStart.
+// These threads don't have a parent thread, as they are created "spuriously".
+// We're using a libpthread API that notifies us about a newly created thread.
+// The `thread == pthread_self()` check indicates this is actually a worker
+// thread. If it's just a regular thread, this hook is called on the parent
+// thread.
+typedef void (*pthread_introspection_hook_t)(unsigned int event,
+ pthread_t thread, void *addr,
+ size_t size);
+extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
+ pthread_introspection_hook_t hook);
+static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
+static const uptr PTHREAD_INTROSPECTION_THREAD_DESTROY = 4;
+static pthread_introspection_hook_t prev_pthread_introspection_hook;
+static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
+ void *addr, size_t size) {
+ if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
+ if (thread == pthread_self()) {
+ // The current thread is a newly created GCD worker thread.
+ ThreadState *parent_thread_state = nullptr; // No parent.
+ int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
+ CHECK_NE(tid, 0);
+ ThreadState *thr = cur_thread();
+ ThreadStart(thr, tid, GetTid());
+ }
+ } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
+ ThreadState *thr = cur_thread();
+ if (thr->tctx && thr->tctx->parent_tid == kInvalidTid) {
+ DestroyThreadState();
+ }
}
- if (common_flags()->use_madv_dontdump)
- DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
- DPrintf("kShadow %zx-%zx (%zuGB)\n",
- kShadowBeg, kShadowEnd,
- (kShadowEnd - kShadowBeg) >> 30);
- DPrintf("kAppMem %zx-%zx (%zuGB)\n",
- kAppMemBeg, kAppMemEnd,
- (kAppMemEnd - kAppMemBeg) >> 30);
+
+ if (prev_pthread_introspection_hook != nullptr)
+ prev_pthread_introspection_hook(event, thread, addr, size);
}
#endif
void InitializePlatform() {
DisableCoreDumperIfNecessary();
+#ifndef SANITIZER_GO
+ CheckAndProtect();
+
+ CHECK_EQ(main_thread_identity, 0);
+ main_thread_identity = (uptr)pthread_self();
+
+ prev_pthread_introspection_hook =
+ pthread_introspection_hook_install(&my_pthread_introspection_hook);
+#endif
}
#ifndef SANITIZER_GO
@@ -89,6 +176,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
}
#endif
+bool IsGlobalVar(uptr addr) {
+ return false;
+}
+
} // namespace __tsan
#endif // SANITIZER_MAC
diff --git a/libsanitizer/tsan/tsan_platform_posix.cc b/libsanitizer/tsan/tsan_platform_posix.cc
new file mode 100644
index 000000000000..5e3d12e9496e
--- /dev/null
+++ b/libsanitizer/tsan/tsan_platform_posix.cc
@@ -0,0 +1,122 @@
+//===-- tsan_platform_posix.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// POSIX-specific code.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_POSIX
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+#ifndef SANITIZER_GO
+void InitializeShadowMemory() {
+ // Map memory shadow.
+ uptr shadow =
+ (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
+ if (shadow != kShadowBeg) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie (%p, %p).\n", shadow, kShadowBeg);
+ Die();
+ }
+ // This memory range is used for thread stacks and large user mmaps.
+ // Frequently a thread uses only a small part of stack and similarly
+ // a program uses a small part of large mmap. On some programs
+ // we see 20% memory usage reduction without huge pages for this range.
+ // FIXME: don't use constants here.
+#if defined(__x86_64__)
+ const uptr kMadviseRangeBeg = 0x7f0000000000ull;
+ const uptr kMadviseRangeSize = 0x010000000000ull;
+#elif defined(__mips64)
+ const uptr kMadviseRangeBeg = 0xff00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#elif defined(__aarch64__)
+ const uptr kMadviseRangeBeg = 0x7e00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#endif
+ NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
+ kMadviseRangeSize * kShadowMultiplier);
+ // Meta shadow is compressing and we don't flush it,
+ // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
+ // On one program it reduces memory consumption from 5GB to 2.5GB.
+ NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
+ DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
+ kShadowBeg, kShadowEnd,
+ (kShadowEnd - kShadowBeg) >> 30);
+
+ // Map meta shadow.
+ uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
+ uptr meta =
+ (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
+ if (meta != kMetaShadowBeg) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
+ Die();
+ }
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(meta, meta_size);
+ DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
+ meta, meta + meta_size, meta_size >> 30);
+
+ InitializeShadowMemoryPlatform();
+}
+
+static void ProtectRange(uptr beg, uptr end) {
+ CHECK_LE(beg, end);
+ if (beg == end)
+ return;
+ if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
+ Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
+ Printf("FATAL: Make sure you are not using unlimited stack\n");
+ Die();
+ }
+}
+
+void CheckAndProtect() {
+ // Ensure that the binary is indeed compiled with -pie.
+ MemoryMappingLayout proc_maps(true);
+ uptr p, end, prot;
+ while (proc_maps.Next(&p, &end, 0, 0, 0, &prot)) {
+ if (IsAppMem(p))
+ continue;
+ if (p >= kHeapMemEnd &&
+ p < HeapEnd())
+ continue;
+ if (prot == 0) // Zero page or mprotected.
+ continue;
+ if (p >= kVdsoBeg) // vdso
+ break;
+ Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
+ Die();
+ }
+
+ ProtectRange(kLoAppMemEnd, kShadowBeg);
+ ProtectRange(kShadowEnd, kMetaShadowBeg);
+ ProtectRange(kMetaShadowEnd, kTraceMemBeg);
+ // Memory for traces is mapped lazily in MapThreadTrace.
+ // Protect the whole range for now, so that user does not map something here.
+ ProtectRange(kTraceMemBeg, kTraceMemEnd);
+ ProtectRange(kTraceMemEnd, kHeapMemBeg);
+ ProtectRange(HeapEnd(), kHiAppMemBeg);
+}
+#endif
+
+} // namespace __tsan
+
+#endif // SANITIZER_POSIX
diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc
index a84738dff6e8..119b1ec1da9c 100644
--- a/libsanitizer/tsan/tsan_report.cc
+++ b/libsanitizer/tsan/tsan_report.cc
@@ -109,6 +109,12 @@ static const char *ReportTypeString(ReportType typ) {
return "";
}
+#if SANITIZER_MAC
+static const char *const kInterposedFunctionPrefix = "wrap_";
+#else
+static const char *const kInterposedFunctionPrefix = "__interceptor_";
+#endif
+
void PrintStack(const ReportStack *ent) {
if (ent == 0 || ent->frames == 0) {
Printf(" [failed to restore the stack]\n\n");
@@ -119,7 +125,7 @@ void PrintStack(const ReportStack *ent) {
InternalScopedString res(2 * GetPageSizeCached());
RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix, "__interceptor_");
+ common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
Printf("%s\n", res.data());
}
Printf("\n");
@@ -163,9 +169,14 @@ static void PrintLocation(const ReportLocation *loc) {
Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
const DataInfo &global = loc->global;
- Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
- global.name, global.size, global.start,
- StripModuleName(global.module), global.module_offset);
+ if (global.size != 0)
+ Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
+ global.name, global.size, global.start,
+ StripModuleName(global.module), global.module_offset);
+ else
+ Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name,
+ global.start, StripModuleName(global.module),
+ global.module_offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc
index e7bdfb51884f..4fceca6f41f9 100644
--- a/libsanitizer/tsan/tsan_rtl.cc
+++ b/libsanitizer/tsan/tsan_rtl.cc
@@ -42,7 +42,7 @@ extern "C" void __tsan_resume() {
namespace __tsan {
-#ifndef SANITIZER_GO
+#if !defined(SANITIZER_GO) && !SANITIZER_MAC
THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
#endif
static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
@@ -323,6 +323,7 @@ void Initialize(ThreadState *thr) {
CheckVMASize();
#ifndef SANITIZER_GO
InitializeAllocator();
+ ReplaceSystemMalloc();
#endif
InitializeInterceptors();
CheckShadowMapping();
diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h
index 34b009b15d78..12587dd203ef 100644
--- a/libsanitizer/tsan/tsan_rtl.h
+++ b/libsanitizer/tsan/tsan_rtl.h
@@ -408,12 +408,18 @@ struct ThreadState {
};
#ifndef SANITIZER_GO
+#if SANITIZER_MAC
+ThreadState *cur_thread();
+void cur_thread_finalize();
+#else
__attribute__((tls_model("initial-exec")))
extern THREADLOCAL char cur_thread_placeholder[];
INLINE ThreadState *cur_thread() {
return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
}
-#endif
+INLINE void cur_thread_finalize() { }
+#endif // SANITIZER_MAC
+#endif // SANITIZER_GO
class ThreadContext : public ThreadContextBase {
public:
@@ -707,7 +713,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c);
// The trick is that the call preserves all registers and the compiler
// does not treat it as a call.
// If it does not work for you, use normal call.
-#if !SANITIZER_DEBUG && defined(__x86_64__)
+#if !SANITIZER_DEBUG && defined(__x86_64__) && !SANITIZER_MAC
// The caller may not create the stack frame for itself at all,
// so we create a reserve stack frame for it (1024b must be enough).
#define HACKY_CALL(f) \
diff --git a/libsanitizer/tsan/tsan_rtl_aarch64.S b/libsanitizer/tsan/tsan_rtl_aarch64.S
new file mode 100644
index 000000000000..20bf00827e9a
--- /dev/null
+++ b/libsanitizer/tsan/tsan_rtl_aarch64.S
@@ -0,0 +1,204 @@
+#include "sanitizer_common/sanitizer_asm.h"
+.hidden __tsan_setjmp
+.comm _ZN14__interception11real_setjmpE,8,8
+.type setjmp, @function
+setjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf
+ str x19, [sp, 16]
+ CFI_OFFSET (19, -16)
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // restore env parameter
+ mov x0, x19
+ ldr x19, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (19)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc setjmp
+ adrp x1, :got:_ZN14__interception11real_setjmpE
+ ldr x1, [x1, #:got_lo12:_ZN14__interception11real_setjmpE]
+ ldr x1, [x1]
+ br x1
+
+ CFI_ENDPROC
+.size setjmp, .-setjmp
+
+.comm _ZN14__interception12real__setjmpE,8,8
+.globl _setjmp
+.type _setjmp, @function
+_setjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf
+ str x19, [sp, 16]
+ CFI_OFFSET (19, -16)
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // Restore jmp_buf parameter
+ mov x0, x19
+ ldr x19, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (19)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc setjmp
+ adrp x1, :got:_ZN14__interception12real__setjmpE
+ ldr x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE]
+ ldr x1, [x1]
+ br x1
+
+ CFI_ENDPROC
+.size _setjmp, .-_setjmp
+
+.comm _ZN14__interception14real_sigsetjmpE,8,8
+.globl sigsetjmp
+.type sigsetjmp, @function
+sigsetjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf and savesigs
+ stp x19, x20, [sp, 16]
+ CFI_OFFSET (19, -16)
+ CFI_OFFSET (20, -8)
+ mov w20, w1
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // restore env parameter
+ mov w1, w20
+ mov x0, x19
+ ldp x19, x20, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (29)
+ CFI_RESTORE (19)
+ CFI_RESTORE (20)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc sigsetjmp
+ adrp x2, :got:_ZN14__interception14real_sigsetjmpE
+ ldr x2, [x2, #:got_lo12:_ZN14__interception14real_sigsetjmpE]
+ ldr x2, [x2]
+ br x2
+ CFI_ENDPROC
+.size sigsetjmp, .-sigsetjmp
+
+.comm _ZN14__interception16real___sigsetjmpE,8,8
+.globl __sigsetjmp
+.type __sigsetjmp, @function
+__sigsetjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf and savesigs
+ stp x19, x20, [sp, 16]
+ CFI_OFFSET (19, -16)
+ CFI_OFFSET (20, -8)
+ mov w20, w1
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ mov w1, w20
+ mov x0, x19
+ ldp x19, x20, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (29)
+ CFI_RESTORE (19)
+ CFI_RESTORE (20)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc __sigsetjmp
+ adrp x2, :got:_ZN14__interception16real___sigsetjmpE
+ ldr x2, [x2, #:got_lo12:_ZN14__interception16real___sigsetjmpE]
+ ldr x2, [x2]
+ br x2
+ CFI_ENDPROC
+.size __sigsetjmp, .-__sigsetjmp
+
+#if defined(__linux__)
+/* We do not need executable stack. */
+.section .note.GNU-stack,"",@progbits
+#endif
diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc
index 9d7e2d3028e9..3939c77d41c3 100644
--- a/libsanitizer/tsan/tsan_rtl_thread.cc
+++ b/libsanitizer/tsan/tsan_rtl_thread.cc
@@ -53,6 +53,8 @@ void ThreadContext::OnCreated(void *arg) {
if (tid == 0)
return;
OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
+ if (!args->thr) // GCD workers don't have a parent thread.
+ return;
args->thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
@@ -229,8 +231,10 @@ int ThreadCount(ThreadState *thr) {
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
StatInc(thr, StatThreadCreate);
OnCreatedArgs args = { thr, pc };
- int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args);
- DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid);
+ u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
+ int tid =
+ ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
+ DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
return tid;
}