#if SANITIZER_POSIX
+struct ThreadParam {
+ void *(*callback)(void *arg);
+ void *param;
+ atomic_uintptr_t tid;
+};
+
extern "C" void *__lsan_thread_start_func(void *arg) {
- atomic_uintptr_t *atomic_tid = (atomic_uintptr_t *)arg;
+ ThreadParam *p = (ThreadParam*)arg;
+ void* (*callback)(void *arg) = p->callback;
+ void *param = p->param;
// Wait until the last iteration to maximize the chance that we are the last
// destructor to run.
#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
}
#endif
int tid = 0;
- while ((tid = atomic_load(atomic_tid, memory_order_acquire)) == 0)
+ while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
internal_sched_yield();
ThreadStart(tid, GetTid());
- atomic_store(atomic_tid, 0, memory_order_release);
- auto self = GetThreadSelf();
- auto args = GetThreadArgRetval().GetArgs(self);
- void *retval = (*args.routine)(args.arg_retval);
- GetThreadArgRetval().Finish(self, retval);
- return retval;
+ atomic_store(&p->tid, 0, memory_order_release);
+ return callback(param);
}
INTERCEPTOR(int, pthread_create, void *th, void *attr,
AdjustStackSize(attr);
int detached = 0;
pthread_attr_getdetachstate(attr, &detached);
- atomic_uintptr_t atomic_tid = {};
- int result;
+ ThreadParam p;
+ p.callback = callback;
+ p.param = param;
+ atomic_store(&p.tid, 0, memory_order_relaxed);
+ int res;
{
// Ignore all allocations made by pthread_create: thread stack/TLS may be
// stored by pthread for future reuse even after thread destruction, and
// the linked list it's stored in doesn't even hold valid pointers to the
// objects, the latter are calculated by obscure pointer arithmetic.
ScopedInterceptorDisabler disabler;
- GetThreadArgRetval().Create(detached, {callback, param}, [&]() -> uptr {
- result =
- REAL(pthread_create)(th, attr, __lsan_thread_start_func, &atomic_tid);
- return result ? 0 : *(uptr *)(th);
- });
+ res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p);
}
- if (result == 0) {
+ if (res == 0) {
int tid = ThreadCreate(GetCurrentThreadId(), IsStateDetached(detached));
CHECK_NE(tid, kMainTid);
- atomic_store(&atomic_tid, tid, memory_order_release);
- while (atomic_load(&atomic_tid, memory_order_acquire) != 0)
+ atomic_store(&p.tid, tid, memory_order_release);
+ while (atomic_load(&p.tid, memory_order_acquire) != 0)
internal_sched_yield();
}
if (attr == &myattr)
pthread_attr_destroy(&myattr);
- return result;
+ return res;
}
INTERCEPTOR(int, pthread_join, void *thread, void **retval) {
- int result;
- GetThreadArgRetval().Join((uptr)thread, [&]() {
- result = REAL(pthread_join)(thread, retval);
- return !result;
- });
- return result;
+ return REAL(pthread_join)(thread, retval);
}
INTERCEPTOR(int, pthread_detach, void *thread) {
- int result;
- GetThreadArgRetval().Detach((uptr)thread, [&]() {
- result = REAL(pthread_detach)(thread);
- return !result;
- });
- return result;
+ return REAL(pthread_detach)(thread);
}
INTERCEPTOR(int, pthread_exit, void *retval) {
- GetThreadArgRetval().Finish(GetThreadSelf(), retval);
return REAL(pthread_exit)(retval);
}
# if SANITIZER_INTERCEPT_TRYJOIN
INTERCEPTOR(int, pthread_tryjoin_np, void *thread, void **ret) {
- int result;
- GetThreadArgRetval().Join((uptr)thread, [&]() {
- result = REAL(pthread_tryjoin_np)(thread, ret);
- return !result;
- });
- return result;
+ return REAL(pthread_tryjoin_np)(thread, ret);
}
# define LSAN_MAYBE_INTERCEPT_TRYJOIN INTERCEPT_FUNCTION(pthread_tryjoin_np)
# else
# if SANITIZER_INTERCEPT_TIMEDJOIN
INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,
const struct timespec *abstime) {
- int result;
- GetThreadArgRetval().Join((uptr)thread, [&]() {
- result = REAL(pthread_timedjoin_np)(thread, ret, abstime);
- return !result;
- });
- return result;
+ return REAL(pthread_timedjoin_np)(thread, ret, abstime);
}
# define LSAN_MAYBE_INTERCEPT_TIMEDJOIN \
INTERCEPT_FUNCTION(pthread_timedjoin_np)
namespace __lsan {
static ThreadRegistry *thread_registry;
-static ThreadArgRetval *thread_arg_retval;
static Mutex mu_for_thread_context;
static LowLevelAllocator allocator_for_thread_context;
}
void InitializeThreadRegistry() {
- static ALIGNED(alignof(
- ThreadRegistry)) char thread_registry_placeholder[sizeof(ThreadRegistry)];
+ static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)];
thread_registry =
new (thread_registry_placeholder) ThreadRegistry(CreateThreadContext);
-
- static ALIGNED(alignof(ThreadArgRetval)) char
- thread_arg_retval_placeholder[sizeof(ThreadArgRetval)];
- thread_arg_retval = new (thread_arg_retval_placeholder) ThreadArgRetval();
}
-ThreadArgRetval &GetThreadArgRetval() { return *thread_arg_retval; }
-
ThreadContextLsanBase::ThreadContextLsanBase(int tid)
: ThreadContextBase(tid) {}
InternalMmapVector<Range> *ranges) {}
void GetThreadExtraStackRangesLocked(InternalMmapVector<Range> *ranges) {}
-void LockThreadRegistry() {
- thread_registry->Lock();
- thread_arg_retval->Lock();
-}
+void LockThreadRegistry() { thread_registry->Lock(); }
-void UnlockThreadRegistry() {
- thread_arg_retval->Unlock();
- thread_registry->Unlock();
-}
+void UnlockThreadRegistry() { thread_registry->Unlock(); }
ThreadRegistry *GetLsanThreadRegistryLocked() {
thread_registry->CheckLocked();
}
void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) {
- GetThreadArgRetval().GetAllPtrsLocked(ptrs);
+ // This function can be used to treat memory reachable from `tctx` as live.
+ // This is useful for threads that have been created but not yet started.
+
+ // This is currently a no-op because the LSan `pthread_create()` interceptor
+ // blocks until the child thread starts which keeps the thread's `arg` pointer
+ // live.
}
} // namespace __lsan