xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h (revision 297eecfb02bb25902531dbb5c3b9a88caf8adf29)
106c3fb27SDimitry Andric //===-- sanitizer_thread_arg_retval.h ---------------------------*- C++ -*-===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric //
906c3fb27SDimitry Andric // This file is shared between sanitizer tools.
1006c3fb27SDimitry Andric //
1106c3fb27SDimitry Andric // Tracks thread arguments and return value for leak checking.
1206c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
1306c3fb27SDimitry Andric 
1406c3fb27SDimitry Andric #ifndef SANITIZER_THREAD_ARG_RETVAL_H
1506c3fb27SDimitry Andric #define SANITIZER_THREAD_ARG_RETVAL_H
1606c3fb27SDimitry Andric 
1706c3fb27SDimitry Andric #include "sanitizer_common.h"
1806c3fb27SDimitry Andric #include "sanitizer_dense_map.h"
1906c3fb27SDimitry Andric #include "sanitizer_list.h"
2006c3fb27SDimitry Andric #include "sanitizer_mutex.h"
2106c3fb27SDimitry Andric 
2206c3fb27SDimitry Andric namespace __sanitizer {
2306c3fb27SDimitry Andric 
2406c3fb27SDimitry Andric // Primary goal of the class is to keep alive arg and retval pointer for leak
2506c3fb27SDimitry Andric // checking. However it can be used to pass those pointer into wrappers used by
2606c3fb27SDimitry Andric // interceptors. The difference from ThreadRegistry/ThreadList is that this
2706c3fb27SDimitry Andric // class keeps data up to the detach or join, as exited thread still can be
2806c3fb27SDimitry Andric // joined to retrive retval. ThreadRegistry/ThreadList can discard exited
2906c3fb27SDimitry Andric // threads immediately.
3006c3fb27SDimitry Andric class SANITIZER_MUTEX ThreadArgRetval {
3106c3fb27SDimitry Andric  public:
3206c3fb27SDimitry Andric   struct Args {
3306c3fb27SDimitry Andric     void* (*routine)(void*);
3406c3fb27SDimitry Andric     void* arg_retval;  // Either arg or retval.
3506c3fb27SDimitry Andric   };
Lock()3606c3fb27SDimitry Andric   void Lock() SANITIZER_ACQUIRE() { mtx_.Lock(); }
CheckLocked()3706c3fb27SDimitry Andric   void CheckLocked() const SANITIZER_CHECK_LOCKED() { mtx_.CheckLocked(); }
Unlock()3806c3fb27SDimitry Andric   void Unlock() SANITIZER_RELEASE() { mtx_.Unlock(); }
3906c3fb27SDimitry Andric 
4006c3fb27SDimitry Andric   // Wraps pthread_create or similar. We need to keep object locked, to
4106c3fb27SDimitry Andric   // prevent child thread from proceeding without thread handle.
4206c3fb27SDimitry Andric   template <typename CreateFn /* returns thread id on success, or 0 */>
Create(bool detached,const Args & args,const CreateFn & fn)4306c3fb27SDimitry Andric   void Create(bool detached, const Args& args, const CreateFn& fn) {
4406c3fb27SDimitry Andric     // No need to track detached threads with no args, but we will to do as it's
4506c3fb27SDimitry Andric     // not expensive and less edge-cases.
4606c3fb27SDimitry Andric     __sanitizer::Lock lock(&mtx_);
4706c3fb27SDimitry Andric     if (uptr thread = fn())
4806c3fb27SDimitry Andric       CreateLocked(thread, detached, args);
4906c3fb27SDimitry Andric   }
5006c3fb27SDimitry Andric 
5106c3fb27SDimitry Andric   // Returns thread arg and routine.
5206c3fb27SDimitry Andric   Args GetArgs(uptr thread) const;
5306c3fb27SDimitry Andric 
5406c3fb27SDimitry Andric   // Mark thread as done and stores retval or remove if detached. Should be
5506c3fb27SDimitry Andric   // called by the thread.
5606c3fb27SDimitry Andric   void Finish(uptr thread, void* retval);
5706c3fb27SDimitry Andric 
5806c3fb27SDimitry Andric   // Mark thread as detached or remove if done.
5906c3fb27SDimitry Andric   template <typename DetachFn /* returns true on success */>
Detach(uptr thread,const DetachFn & fn)6006c3fb27SDimitry Andric   void Detach(uptr thread, const DetachFn& fn) {
6106c3fb27SDimitry Andric     // Lock to prevent re-use of the thread between fn() and DetachLocked()
6206c3fb27SDimitry Andric     // calls.
6306c3fb27SDimitry Andric     __sanitizer::Lock lock(&mtx_);
6406c3fb27SDimitry Andric     if (fn())
6506c3fb27SDimitry Andric       DetachLocked(thread);
6606c3fb27SDimitry Andric   }
6706c3fb27SDimitry Andric 
6806c3fb27SDimitry Andric   // Joins the thread.
6906c3fb27SDimitry Andric   template <typename JoinFn /* returns true on success */>
Join(uptr thread,const JoinFn & fn)7006c3fb27SDimitry Andric   void Join(uptr thread, const JoinFn& fn) {
7106c3fb27SDimitry Andric     // Remember internal id of the thread to prevent re-use of the thread
7206c3fb27SDimitry Andric     // between fn() and AfterJoin() calls. Locking JoinFn, like in
7306c3fb27SDimitry Andric     // Detach(), implementation can cause deadlock.
7406c3fb27SDimitry Andric     auto gen = BeforeJoin(thread);
7506c3fb27SDimitry Andric     if (fn())
7606c3fb27SDimitry Andric       AfterJoin(thread, gen);
7706c3fb27SDimitry Andric   }
7806c3fb27SDimitry Andric 
7906c3fb27SDimitry Andric   // Returns all arg and retval which are considered alive.
8006c3fb27SDimitry Andric   void GetAllPtrsLocked(InternalMmapVector<uptr>* ptrs);
8106c3fb27SDimitry Andric 
size()8206c3fb27SDimitry Andric   uptr size() const {
8306c3fb27SDimitry Andric     __sanitizer::Lock lock(&mtx_);
8406c3fb27SDimitry Andric     return data_.size();
8506c3fb27SDimitry Andric   }
8606c3fb27SDimitry Andric 
8706c3fb27SDimitry Andric   // FIXME: Add fork support. Expected users of the class are sloppy with forks
8806c3fb27SDimitry Andric   // anyway. We likely should lock/unlock the object to avoid deadlocks, and
8906c3fb27SDimitry Andric   // erase all but the current threads, so we can detect leaked arg or retval in
9006c3fb27SDimitry Andric   // child process.
9106c3fb27SDimitry Andric 
9206c3fb27SDimitry Andric   // FIXME: Add cancelation support. Now if a thread was canceled, the class
9306c3fb27SDimitry Andric   // will keep pointers alive forever, missing leaks caused by cancelation.
9406c3fb27SDimitry Andric 
9506c3fb27SDimitry Andric  private:
96*297eecfbSDimitry Andric   static const u32 kInvalidGen = UINT32_MAX;
9706c3fb27SDimitry Andric   struct Data {
9806c3fb27SDimitry Andric     Args args;
9906c3fb27SDimitry Andric     u32 gen;  // Avoid collision if thread id re-used.
10006c3fb27SDimitry Andric     bool detached;
10106c3fb27SDimitry Andric     bool done;
10206c3fb27SDimitry Andric   };
10306c3fb27SDimitry Andric 
10406c3fb27SDimitry Andric   void CreateLocked(uptr thread, bool detached, const Args& args);
10506c3fb27SDimitry Andric   u32 BeforeJoin(uptr thread) const;
10606c3fb27SDimitry Andric   void AfterJoin(uptr thread, u32 gen);
10706c3fb27SDimitry Andric   void DetachLocked(uptr thread);
10806c3fb27SDimitry Andric 
10906c3fb27SDimitry Andric   mutable Mutex mtx_;
11006c3fb27SDimitry Andric 
11106c3fb27SDimitry Andric   DenseMap<uptr, Data> data_;
11206c3fb27SDimitry Andric   u32 gen_ = 0;
11306c3fb27SDimitry Andric };
11406c3fb27SDimitry Andric 
11506c3fb27SDimitry Andric }  // namespace __sanitizer
11606c3fb27SDimitry Andric 
11706c3fb27SDimitry Andric #endif  // SANITIZER_THREAD_ARG_RETVAL_H
118