diff options
author | Alexey Samsonov <samsonov@google.com> | 2014-04-16 23:06:46 +0000 |
---|---|---|
committer | Alexey Samsonov <samsonov@google.com> | 2014-04-16 23:06:46 +0000 |
commit | 1e1ecda447b441c2af5a4668623af6a10f8d247d (patch) | |
tree | 076ea4fcd178a406247f1b479fa10484bc57e4af /lib | |
parent | 4f2694e0c34b46b5531ba74b5eff84217db2509f (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.inc | 140 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_linux_libcdep.cc | 12 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_mac.cc | 12 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_platform_interceptors.h | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_interceptors.cc | 124 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_platform.h | 4 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_platform_linux.cc | 13 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_platform_mac.cc | 14 |
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 |