summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Samsonov <samsonov@google.com>2014-04-16 23:06:46 +0000
committerAlexey Samsonov <samsonov@google.com>2014-04-16 23:06:46 +0000
commit1e1ecda447b441c2af5a4668623af6a10f8d247d (patch)
tree076ea4fcd178a406247f1b479fa10484bc57e4af /lib
parent4f2694e0c34b46b5531ba74b5eff84217db2509f (diff)
Move pthread_cond_* interceptors from sanitizer_common with all the ugly hacks to TSan
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@206423 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc140
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h1
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc124
-rw-r--r--lib/tsan/rtl/tsan_platform.h4
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc13
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc14
8 files changed, 154 insertions, 166 deletions
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 2b0ba4264..a4e6445de 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -2648,145 +2648,6 @@ INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
#define INIT_PTHREAD_MUTEX_UNLOCK
#endif
-#if SANITIZER_INTERCEPT_PTHREAD_COND
-// Problem:
-// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
-// pthread_cond_t has different size in the different versions.
-// If call new REAL functions for old pthread_cond_t, they will corrupt memory
-// after pthread_cond_t (old cond is smaller).
-// If we call old REAL functions for new pthread_cond_t, we will lose some
-// functionality (e.g. old functions do not support waiting against
-// CLOCK_REALTIME).
-// Proper handling would require to have 2 versions of interceptors as well.
-// But this is messy, in particular requires linker scripts when sanitizer
-// runtime is linked into a shared library.
-// Instead we assume we don't have dynamic libraries built against old
-// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag
-// that allows to work with old libraries (but this mode does not support
-// some features, e.g. pthread_condattr_getpshared).
-static void *init_cond(void *c, bool force = false) {
- // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
- // So we allocate additional memory on the side large enough to hold
- // any pthread_cond_t object. Always call new REAL functions, but pass
- // the aux object to them.
- // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
- // first word of pthread_cond_t to zero.
- // It's all relevant only for linux.
- if (!common_flags()->legacy_pthread_cond)
- return c;
- atomic_uintptr_t *p = (atomic_uintptr_t*)c;
- uptr cond = atomic_load(p, memory_order_acquire);
- if (!force && cond != 0)
- return (void*)cond;
- void *newcond = WRAP(malloc)(pthread_cond_t_sz);
- internal_memset(newcond, 0, pthread_cond_t_sz);
- if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
- memory_order_acq_rel))
- return newcond;
- WRAP(free)(newcond);
- return (void*)cond;
-}
-
-struct CondMutexUnlockCtx {
- void *ctx;
- void *m;
-};
-
-static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
- COMMON_INTERCEPTOR_MUTEX_LOCK(arg->ctx, arg->m);
-}
-
-namespace __sanitizer {
-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);
-} // namespace __sanitizer
-
-INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
- void *cond = init_cond(c, true);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, cond, a);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr));
- return REAL(pthread_cond_init)(cond, a);
-}
-
-INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
- void *cond = init_cond(c);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, cond, m);
- COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
- CondMutexUnlockCtx arg = {ctx, m};
- // This ensures that we handle mutex lock even in case of pthread_cancel.
- // See test/tsan/cond_cancel.cc.
- int res = __sanitizer::call_pthread_cancel_with_cleanup(
- (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
- cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
- if (res == errno_EOWNERDEAD)
- COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
- COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
- return res;
-}
-
-INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
- void *cond = init_cond(c);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait, cond, m, abstime);
- COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
- CondMutexUnlockCtx arg = {ctx, m};
- // This ensures that we handle mutex lock even in case of pthread_cancel.
- // See test/tsan/cond_cancel.cc.
- int res = __sanitizer::call_pthread_cancel_with_cleanup(
- REAL(pthread_cond_timedwait), cond, m, abstime,
- (void(*)(void *arg))cond_mutex_unlock, &arg);
- if (res == errno_EOWNERDEAD)
- COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
- COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
- return res;
-}
-
-INTERCEPTOR(int, pthread_cond_signal, void *c) {
- void *cond = init_cond(c);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, cond);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
- return REAL(pthread_cond_signal)(cond);
-}
-
-INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
- void *cond = init_cond(c);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, cond);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr));
- return REAL(pthread_cond_broadcast)(cond);
-}
-
-INTERCEPTOR(int, pthread_cond_destroy, void *c) {
- void *cond = init_cond(c);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy, cond);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr));
- int res = REAL(pthread_cond_destroy)(cond);
- if (common_flags()->legacy_pthread_cond) {
- // Free our aux cond and zero the pointer to not leave dangling pointers.
- WRAP(free)(cond);
- atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
- }
- return res;
-}
-
-#define INIT_PTHREAD_COND \
- INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2"); \
- INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2"); \
- INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); \
- INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2"); \
- INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); \
- INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2")
-#else
-#define INIT_PTHREAD_COND
-#endif
-
#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R
static void write_mntent(void *ctx, __sanitizer_mntent *mnt) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt));
@@ -3934,7 +3795,6 @@ INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p,
INIT__EXIT; \
INIT_PTHREAD_MUTEX_LOCK; \
INIT_PTHREAD_MUTEX_UNLOCK; \
- INIT_PTHREAD_COND; \
INIT_GETMNTENT; \
INIT_GETMNTENT_R; \
INIT_STATFS; \
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 5020775ec..40eabfd76 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -527,18 +527,6 @@ void SetIndirectCallWrapper(uptr wrapper) {
indirect_call_wrapper = wrapper;
}
-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) {
- // pthread_cleanup_push/pop are hardcore macros mess.
- // We can't intercept nor call them w/o including pthread.h.
- int res;
- pthread_cleanup_push(cleanup, arg);
- res = fn(c, m, abstime);
- pthread_cleanup_pop(0);
- return res;
-}
-
} // namespace __sanitizer
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 14201dd92..747513de5 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -302,18 +302,6 @@ MacosVersion GetMacosVersion() {
return result;
}
-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) {
- // pthread_cleanup_push/pop are hardcore macros mess.
- // We can't intercept nor call them w/o including pthread.h.
- int res;
- pthread_cleanup_push(cleanup, arg);
- res = fn(c, m, abstime);
- pthread_cleanup_pop(0);
- return res;
-}
-
} // namespace __sanitizer
#endif // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 2ea6ddd98..57cfacce7 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -181,7 +181,6 @@
#define SANITIZER_INTERCEPT__EXIT SI_LINUX
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TLS_GET_ADDR SI_LINUX_NOT_ANDROID
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index fb45a3930..76cfd31d3 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -213,6 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() {
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
+#define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
@@ -915,6 +916,122 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
return res;
}
+// Problem:
+// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
+// pthread_cond_t has different size in the different versions.
+// If call new REAL functions for old pthread_cond_t, they will corrupt memory
+// after pthread_cond_t (old cond is smaller).
+// If we call old REAL functions for new pthread_cond_t, we will lose some
+// functionality (e.g. old functions do not support waiting against
+// CLOCK_REALTIME).
+// Proper handling would require to have 2 versions of interceptors as well.
+// But this is messy, in particular requires linker scripts when sanitizer
+// runtime is linked into a shared library.
+// Instead we assume we don't have dynamic libraries built against old
+// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag
+// that allows to work with old libraries (but this mode does not support
+// some features, e.g. pthread_condattr_getpshared).
+static void *init_cond(void *c, bool force = false) {
+ // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
+ // So we allocate additional memory on the side large enough to hold
+ // any pthread_cond_t object. Always call new REAL functions, but pass
+ // the aux object to them.
+ // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
+ // first word of pthread_cond_t to zero.
+ // It's all relevant only for linux.
+ if (!common_flags()->legacy_pthread_cond)
+ return c;
+ atomic_uintptr_t *p = (atomic_uintptr_t*)c;
+ uptr cond = atomic_load(p, memory_order_acquire);
+ if (!force && cond != 0)
+ return (void*)cond;
+ void *newcond = WRAP(malloc)(pthread_cond_t_sz);
+ internal_memset(newcond, 0, pthread_cond_t_sz);
+ if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
+ memory_order_acq_rel))
+ return newcond;
+ WRAP(free)(newcond);
+ return (void*)cond;
+}
+
+struct CondMutexUnlockCtx {
+ ThreadState *thr;
+ uptr pc;
+ void *m;
+};
+
+static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
+ MutexLock(arg->thr, arg->pc, (uptr)arg->m);
+}
+
+INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+ void *cond = init_cond(c, true);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, cond, a);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ return REAL(pthread_cond_init)(cond, a);
+}
+
+INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
+ MutexUnlock(thr, pc, (uptr)m);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ CondMutexUnlockCtx arg = {thr, pc, m};
+ // This ensures that we handle mutex lock even in case of pthread_cancel.
+ // See test/tsan/cond_cancel.cc.
+ int res = call_pthread_cancel_with_cleanup(
+ (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
+ cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
+ MutexUnlock(thr, pc, (uptr)m);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ CondMutexUnlockCtx arg = {thr, pc, m};
+ // This ensures that we handle mutex lock even in case of pthread_cancel.
+ // See test/tsan/cond_cancel.cc.
+ int res = call_pthread_cancel_with_cleanup(
+ REAL(pthread_cond_timedwait), cond, m, abstime,
+ (void(*)(void *arg))cond_mutex_unlock, &arg);
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_cond_signal, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_signal)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_broadcast)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ int res = REAL(pthread_cond_destroy)(cond);
+ if (common_flags()->legacy_pthread_cond) {
+ // Free our aux cond and zero the pointer to not leave dangling pointers.
+ WRAP(free)(cond);
+ atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
+ }
+ return res;
+}
+
TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
int res = REAL(pthread_mutex_init)(m, a);
@@ -2209,6 +2326,13 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_join);
TSAN_INTERCEPT(pthread_detach);
+ TSAN_INTERCEPT_VER(pthread_cond_init, "GLIBC_2.3.2");
+ TSAN_INTERCEPT_VER(pthread_cond_signal, "GLIBC_2.3.2");
+ TSAN_INTERCEPT_VER(pthread_cond_broadcast, "GLIBC_2.3.2");
+ TSAN_INTERCEPT_VER(pthread_cond_wait, "GLIBC_2.3.2");
+ TSAN_INTERCEPT_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
+ TSAN_INTERCEPT_VER(pthread_cond_destroy, "GLIBC_2.3.2");
+
TSAN_INTERCEPT(pthread_mutex_init);
TSAN_INTERCEPT(pthread_mutex_destroy);
TSAN_INTERCEPT(pthread_mutex_trylock);
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index e66b51c3d..1657a2b86 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -165,6 +165,10 @@ bool IsGlobalVar(uptr addr);
int ExtractResolvFDs(void *state, int *fds, int nfd);
int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
+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);
+
} // namespace __tsan
#else // defined(__LP64__) || defined(_WIN64)
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index 0a9e4ed0f..3c3a58b0d 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -358,8 +358,19 @@ int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
}
return res;
}
-#endif
+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) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
+}
+#endif
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 50dd037cd..a545884b2 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -91,6 +91,20 @@ void FinalizePlatform() {
fflush(0);
}
+#ifndef TSAN_GO
+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) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
+}
+#endif
+
} // namespace __tsan
#endif // SANITIZER_MAC