1 #include "dfsan_thread.h"
2 
3 #include <pthread.h>
4 
5 #include "dfsan.h"
6 
7 namespace __dfsan {
8 
9 DFsanThread *DFsanThread::Create(void *start_routine_trampoline,
10                                  thread_callback_t start_routine, void *arg) {
11   uptr PageSize = GetPageSizeCached();
12   uptr size = RoundUpTo(sizeof(DFsanThread), PageSize);
13   DFsanThread *thread = (DFsanThread *)MmapOrDie(size, __func__);
14   thread->start_routine_trampoline_ = start_routine_trampoline;
15   thread->start_routine_ = start_routine;
16   thread->arg_ = arg;
17   thread->destructor_iterations_ = GetPthreadDestructorIterations();
18 
19   return thread;
20 }
21 
22 void DFsanThread::SetThreadStackAndTls() {
23   uptr tls_size = 0;
24   uptr stack_size = 0;
25   uptr tls_begin;
26   GetThreadStackAndTls(IsMainThread(), &stack_.bottom, &stack_size, &tls_begin,
27                        &tls_size);
28   stack_.top = stack_.bottom + stack_size;
29 
30   int local;
31   CHECK(AddrIsInStack((uptr)&local));
32 }
33 
34 void DFsanThread::Init() { SetThreadStackAndTls(); }
35 
36 void DFsanThread::TSDDtor(void *tsd) {
37   DFsanThread *t = (DFsanThread *)tsd;
38   t->Destroy();
39 }
40 
41 void DFsanThread::Destroy() {
42   uptr size = RoundUpTo(sizeof(DFsanThread), GetPageSizeCached());
43   UnmapOrDie(this, size);
44 }
45 
46 thread_return_t DFsanThread::ThreadStart() {
47   Init();
48 
49   if (!start_routine_) {
50     // start_routine_ == 0 if we're on the main thread or on one of the
51     // OS X libdispatch worker threads. But nobody is supposed to call
52     // ThreadStart() for the worker threads.
53     return 0;
54   }
55 
56   CHECK(start_routine_trampoline_);
57 
58   typedef void *(*thread_callback_trampoline_t)(void *, void *, dfsan_label,
59                                                 dfsan_label *);
60 
61   dfsan_label ret_label;
62   return ((thread_callback_trampoline_t)
63               start_routine_trampoline_)((void *)start_routine_, arg_, 0,
64                                          &ret_label);
65 }
66 
67 DFsanThread::StackBounds DFsanThread::GetStackBounds() const {
68   return {stack_.bottom, stack_.top};
69 }
70 
71 uptr DFsanThread::stack_top() { return GetStackBounds().top; }
72 
73 uptr DFsanThread::stack_bottom() { return GetStackBounds().bottom; }
74 
75 bool DFsanThread::AddrIsInStack(uptr addr) {
76   const auto bounds = GetStackBounds();
77   return addr >= bounds.bottom && addr < bounds.top;
78 }
79 
80 static pthread_key_t tsd_key;
81 static bool tsd_key_inited = false;
82 
83 void DFsanTSDInit(void (*destructor)(void *tsd)) {
84   CHECK(!tsd_key_inited);
85   tsd_key_inited = true;
86   CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
87 }
88 
89 static THREADLOCAL DFsanThread *dfsan_current_thread;
90 
91 DFsanThread *GetCurrentThread() { return dfsan_current_thread; }
92 
93 void SetCurrentThread(DFsanThread *t) {
94   // Make sure we do not reset the current DFsanThread.
95   CHECK_EQ(0, dfsan_current_thread);
96   dfsan_current_thread = t;
97   // Make sure that DFsanTSDDtor gets called at the end.
98   CHECK(tsd_key_inited);
99   pthread_setspecific(tsd_key, t);
100 }
101 
102 void DFsanTSDDtor(void *tsd) {
103   DFsanThread *t = (DFsanThread *)tsd;
104   if (t->destructor_iterations_ > 1) {
105     t->destructor_iterations_--;
106     CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
107     return;
108   }
109   dfsan_current_thread = nullptr;
110   // Make sure that signal handler can not see a stale current thread pointer.
111   atomic_signal_fence(memory_order_seq_cst);
112   DFsanThread::TSDDtor(tsd);
113 }
114 
115 }  // namespace __dfsan
116