================ @@ -0,0 +1,398 @@ +//===-- tsan_deadlock_interceptors.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DeadlockSanitizer. +// +//===----------------------------------------------------------------------===// + +#include <pthread.h> + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_errno.h" +#include "sanitizer_common/sanitizer_glibc_version.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "tsan_deadlock_rtl.h" + +using namespace __tsan_deadlock; + +__attribute__((tls_model("initial-exec"))) static __thread volatile int initing; +static bool inited; + +static bool InitThread() { + if (initing) + return false; + if (cur_thread()) + return true; + initing = true; + if (!inited) { + inited = true; + Initialize(); + } + ThreadInit(&thr_tls); + initing = false; + return true; +} + +struct ThreadArg { + void *(*fn)(void *); + void *arg; +}; + +static void *ThreadTrampoline(void *arg) { + ThreadArg *targ = static_cast<ThreadArg *>(arg); + void *(*fn)(void *) = targ->fn; + void *fn_arg = targ->arg; + InternalFree(targ); + ThreadInit(&thr_tls); + void *retval = fn(fn_arg); + // Also called from the pthread_exit interceptor; guard with is_inited to + // stay idempotent. + if (thr_tls.is_inited) + ThreadDestroy(&thr_tls); + return retval; +} + +INTERCEPTOR(int, pthread_create, pthread_t *th, const pthread_attr_t *attr, + void *(*fn)(void *), void *arg) { + InitThread(); + ThreadArg *targ = static_cast<ThreadArg *>(InternalAlloc(sizeof(ThreadArg))); + targ->fn = fn; + targ->arg = arg; + return REAL(pthread_create)(th, attr, ThreadTrampoline, targ); +} + +INTERCEPTOR(int, pthread_join, pthread_t t, void **retval) { + InitThread(); + return REAL(pthread_join)(t, retval); +} + +INTERCEPTOR(void, pthread_exit, void *retval) { + if (thr_tls.is_inited) + ThreadDestroy(&thr_tls); + REAL(pthread_exit)(retval); +} + +INTERCEPTOR(int, pthread_mutex_init, pthread_mutex_t *m, + const pthread_mutexattr_t *attr) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_mutex_init)(m, attr); + if (res == 0) { + bool reentrant = false; + if (attr) { + int type = 0; + if (pthread_mutexattr_gettype(attr, &type) == 0) + reentrant = (type == PTHREAD_MUTEX_RECURSIVE); + } + MutexInit(cur_thread(), (uptr)m, reentrant, pc); + } + return res; +} + +INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_mutex_destroy)(m); + if (res == 0 || res == errno_EBUSY) + MutexDestroy(cur_thread(), (uptr)m, pc); + return res; +} + +INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeLock(cur_thread(), (uptr)m, true, pc); + int res = REAL(pthread_mutex_lock)(m); + MutexAfterLock(cur_thread(), (uptr)m, true, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_mutex_trylock)(m); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, true, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_mutex_timedlock, pthread_mutex_t *m, + const struct timespec *abstime) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_mutex_timedlock)(m, abstime); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, true, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeUnlock(cur_thread(), (uptr)m, true, pc); + return REAL(pthread_mutex_unlock)(m); +} + +INTERCEPTOR(int, pthread_spin_init, pthread_spinlock_t *m, int pshared) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_spin_init)(m, pshared); + if (res == 0) + MutexInit(cur_thread(), (uptr)m, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_spin_destroy, pthread_spinlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_spin_destroy)(m); + if (res == 0) + MutexDestroy(cur_thread(), (uptr)m, pc); + return res; +} + +INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeLock(cur_thread(), (uptr)m, true, pc); + int res = REAL(pthread_spin_lock)(m); + MutexAfterLock(cur_thread(), (uptr)m, true, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_spin_trylock, pthread_spinlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_spin_trylock)(m); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, true, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_spin_unlock, pthread_spinlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeUnlock(cur_thread(), (uptr)m, true, pc); + return REAL(pthread_spin_unlock)(m); +} + +INTERCEPTOR(int, pthread_rwlock_init, pthread_rwlock_t *m, + const pthread_rwlockattr_t *attr) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_rwlock_init)(m, attr); + if (res == 0) + MutexInit(cur_thread(), (uptr)m, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_destroy, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexDestroy(cur_thread(), (uptr)m, pc); + return REAL(pthread_rwlock_destroy)(m); +} + +INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeLock(cur_thread(), (uptr)m, false, pc); + int res = REAL(pthread_rwlock_rdlock)(m); + MutexAfterLock(cur_thread(), (uptr)m, false, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_tryrdlock, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_rwlock_tryrdlock)(m); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, false, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_timedrdlock, pthread_rwlock_t *m, + const timespec *abstime) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, false, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeLock(cur_thread(), (uptr)m, true, pc); + int res = REAL(pthread_rwlock_wrlock)(m); + MutexAfterLock(cur_thread(), (uptr)m, true, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_trywrlock, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_rwlock_trywrlock)(m); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, true, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_timedwrlock, pthread_rwlock_t *m, + const timespec *abstime) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); + if (res == 0) + MutexAfterLock(cur_thread(), (uptr)m, true, true, pc); + return res; +} + +INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeUnlock(cur_thread(), (uptr)m, false, pc); + return REAL(pthread_rwlock_unlock)(m); +} + +static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) { + 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 (pthread_cond_t *)cond; + void *newcond = InternalAlloc(sizeof(pthread_cond_t)); + internal_memset(newcond, 0, sizeof(pthread_cond_t)); + if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond, + memory_order_acq_rel)) + return (pthread_cond_t *)newcond; + InternalFree(newcond); + return (pthread_cond_t *)cond; +} + +INTERCEPTOR(int, pthread_cond_init, pthread_cond_t *c, + const pthread_condattr_t *a) { + InitThread(); + return REAL(pthread_cond_init)(init_cond(c, true), a); +} + +INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *c, pthread_mutex_t *m) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeUnlock(cur_thread(), (uptr)m, true, pc); + int res = REAL(pthread_cond_wait)(init_cond(c), m); + MutexBeforeLock(cur_thread(), (uptr)m, true, pc); + MutexAfterLock(cur_thread(), (uptr)m, true, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *c, pthread_mutex_t *m, + const timespec *abstime) { + uptr pc = GET_CURRENT_PC(); + InitThread(); + MutexBeforeUnlock(cur_thread(), (uptr)m, true, pc); + int res = REAL(pthread_cond_timedwait)(init_cond(c), m, abstime); + MutexBeforeLock(cur_thread(), (uptr)m, true, pc); + MutexAfterLock(cur_thread(), (uptr)m, true, false, pc); + return res; +} + +INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *c) { + InitThread(); + return REAL(pthread_cond_signal)(init_cond(c)); +} + +INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *c) { + InitThread(); + return REAL(pthread_cond_broadcast)(init_cond(c)); +} + +INTERCEPTOR(int, pthread_cond_destroy, pthread_cond_t *c) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + int res = REAL(pthread_cond_destroy)(cond); + if (common_flags()->legacy_pthread_cond) { + InternalFree(cond); + atomic_store((atomic_uintptr_t *)c, 0, memory_order_relaxed); + } + return res; +} + +INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { + InitThread(); + return REAL(realpath)(path, resolved_path); +} + +INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { + InitThread(); + return REAL(read)(fd, ptr, count); +} + +INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { + InitThread(); + return REAL(pread)(fd, ptr, count, offset); +} + +namespace __tsan_deadlock { + +void InitializeInterceptors() { + INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(pthread_exit); + + INTERCEPT_FUNCTION(pthread_mutex_init); + INTERCEPT_FUNCTION(pthread_mutex_destroy); + INTERCEPT_FUNCTION(pthread_mutex_lock); + INTERCEPT_FUNCTION(pthread_mutex_trylock); + INTERCEPT_FUNCTION(pthread_mutex_timedlock); + INTERCEPT_FUNCTION(pthread_mutex_unlock); + + INTERCEPT_FUNCTION(pthread_spin_init); + INTERCEPT_FUNCTION(pthread_spin_destroy); + INTERCEPT_FUNCTION(pthread_spin_lock); + INTERCEPT_FUNCTION(pthread_spin_trylock); + INTERCEPT_FUNCTION(pthread_spin_unlock); + + INTERCEPT_FUNCTION(pthread_rwlock_init); + INTERCEPT_FUNCTION(pthread_rwlock_destroy); + INTERCEPT_FUNCTION(pthread_rwlock_rdlock); + INTERCEPT_FUNCTION(pthread_rwlock_tryrdlock); + INTERCEPT_FUNCTION(pthread_rwlock_timedrdlock); + INTERCEPT_FUNCTION(pthread_rwlock_wrlock); + INTERCEPT_FUNCTION(pthread_rwlock_trywrlock); + INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock); + INTERCEPT_FUNCTION(pthread_rwlock_unlock); + + // See the comment in tsan_interceptors_posix.cpp. +#if SANITIZER_GLIBC && !__GLIBC_PREREQ(2, 36) && \ + (defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1 || \ + defined(__s390x__)) + 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 + INTERCEPT_FUNCTION(pthread_cond_init); + INTERCEPT_FUNCTION(pthread_cond_signal); + INTERCEPT_FUNCTION(pthread_cond_broadcast); + INTERCEPT_FUNCTION(pthread_cond_wait); + INTERCEPT_FUNCTION(pthread_cond_timedwait); + INTERCEPT_FUNCTION(pthread_cond_destroy); +#endif + + INTERCEPT_FUNCTION(realpath); ---------------- vitalybuka wrote:
why do we need these 3? https://github.com/llvm/llvm-project/pull/212748 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
