1 | #include "dfsan_thread.h" |
2 | |
3 | #include <pthread.h> |
4 | |
5 | #include "dfsan.h" |
6 | #include "sanitizer_common/sanitizer_tls_get_addr.h" |
7 | |
8 | namespace __dfsan { |
9 | |
10 | DFsanThread *DFsanThread::Create(thread_callback_t start_routine, void *arg, |
11 | bool track_origins) { |
12 | uptr PageSize = GetPageSizeCached(); |
13 | uptr size = RoundUpTo(size: sizeof(DFsanThread), boundary: PageSize); |
14 | DFsanThread *thread = (DFsanThread *)MmapOrDie(size, mem_type: __func__); |
15 | thread->start_routine_ = start_routine; |
16 | thread->arg_ = arg; |
17 | thread->track_origins_ = track_origins; |
18 | thread->destructor_iterations_ = GetPthreadDestructorIterations(); |
19 | |
20 | return thread; |
21 | } |
22 | |
23 | void DFsanThread::SetThreadStackAndTls() { |
24 | uptr tls_size = 0; |
25 | uptr stack_size = 0; |
26 | GetThreadStackAndTls(main: IsMainThread(), stk_addr: &stack_.bottom, stk_size: &stack_size, tls_addr: &tls_begin_, |
27 | tls_size: &tls_size); |
28 | stack_.top = stack_.bottom + stack_size; |
29 | tls_end_ = tls_begin_ + tls_size; |
30 | |
31 | int local; |
32 | CHECK(AddrIsInStack((uptr)&local)); |
33 | } |
34 | |
35 | void DFsanThread::ClearShadowForThreadStackAndTLS() { |
36 | dfsan_set_label(label: 0, addr: (void *)stack_.bottom, size: stack_.top - stack_.bottom); |
37 | if (tls_begin_ != tls_end_) |
38 | dfsan_set_label(label: 0, addr: (void *)tls_begin_, size: tls_end_ - tls_begin_); |
39 | DTLS *dtls = DTLS_Get(); |
40 | CHECK_NE(dtls, 0); |
41 | ForEachDVT(dtls, fn: [](const DTLS::DTV &dtv, int id) { |
42 | dfsan_set_label(label: 0, addr: (void *)(dtv.beg), size: dtv.size); |
43 | }); |
44 | } |
45 | |
46 | void DFsanThread::Init() { |
47 | SetThreadStackAndTls(); |
48 | ClearShadowForThreadStackAndTLS(); |
49 | } |
50 | |
51 | void DFsanThread::TSDDtor(void *tsd) { |
52 | DFsanThread *t = (DFsanThread *)tsd; |
53 | t->Destroy(); |
54 | } |
55 | |
56 | void DFsanThread::Destroy() { |
57 | malloc_storage().CommitBack(); |
58 | // We also clear the shadow on thread destruction because |
59 | // some code may still be executing in later TSD destructors |
60 | // and we don't want it to have any poisoned stack. |
61 | ClearShadowForThreadStackAndTLS(); |
62 | uptr size = RoundUpTo(size: sizeof(DFsanThread), boundary: GetPageSizeCached()); |
63 | UnmapOrDie(addr: this, size); |
64 | DTLS_Destroy(); |
65 | } |
66 | |
67 | thread_return_t DFsanThread::ThreadStart() { |
68 | if (!start_routine_) { |
69 | // start_routine_ == 0 if we're on the main thread or on one of the |
70 | // OS X libdispatch worker threads. But nobody is supposed to call |
71 | // ThreadStart() for the worker threads. |
72 | return 0; |
73 | } |
74 | |
75 | // The only argument is void* arg. |
76 | // |
77 | // We have never supported propagating the pointer arg as tainted, |
78 | // __dfsw_pthread_create/__dfso_pthread_create ignore the taint label. |
79 | // Note that the bytes pointed-to (probably the much more common case) |
80 | // can still have taint labels attached to them. |
81 | dfsan_clear_thread_local_state(); |
82 | |
83 | return start_routine_(arg_); |
84 | } |
85 | |
86 | DFsanThread::StackBounds DFsanThread::GetStackBounds() const { |
87 | return {.bottom: stack_.bottom, .top: stack_.top}; |
88 | } |
89 | |
90 | uptr DFsanThread::stack_top() { return GetStackBounds().top; } |
91 | |
92 | uptr DFsanThread::stack_bottom() { return GetStackBounds().bottom; } |
93 | |
94 | bool DFsanThread::AddrIsInStack(uptr addr) { |
95 | const auto bounds = GetStackBounds(); |
96 | return addr >= bounds.bottom && addr < bounds.top; |
97 | } |
98 | |
99 | static pthread_key_t tsd_key; |
100 | static bool tsd_key_inited = false; |
101 | |
102 | void DFsanTSDInit(void (*destructor)(void *tsd)) { |
103 | CHECK(!tsd_key_inited); |
104 | tsd_key_inited = true; |
105 | CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); |
106 | } |
107 | |
108 | static THREADLOCAL DFsanThread *dfsan_current_thread; |
109 | |
110 | DFsanThread *GetCurrentThread() { return dfsan_current_thread; } |
111 | |
112 | void SetCurrentThread(DFsanThread *t) { |
113 | // Make sure we do not reset the current DFsanThread. |
114 | CHECK_EQ(0, dfsan_current_thread); |
115 | dfsan_current_thread = t; |
116 | // Make sure that DFsanTSDDtor gets called at the end. |
117 | CHECK(tsd_key_inited); |
118 | pthread_setspecific(key: tsd_key, pointer: t); |
119 | } |
120 | |
121 | void DFsanTSDDtor(void *tsd) { |
122 | DFsanThread *t = (DFsanThread *)tsd; |
123 | if (t->destructor_iterations_ > 1) { |
124 | t->destructor_iterations_--; |
125 | CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); |
126 | return; |
127 | } |
128 | dfsan_current_thread = nullptr; |
129 | // Make sure that signal handler can not see a stale current thread pointer. |
130 | atomic_signal_fence(mo: memory_order_seq_cst); |
131 | DFsanThread::TSDDtor(tsd); |
132 | } |
133 | |
134 | } // namespace __dfsan |
135 | |