xref: /llvm-project/compiler-rt/lib/nsan/nsan_thread.cpp (revision f13b7d0b020d0d409322ed7544c16b224324083d)
1 //===- nsan_threads.cpp ---------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // Thread management.
9 //===----------------------------------------------------------------------===//
10 
11 #include "nsan_thread.h"
12 
13 #include <pthread.h>
14 
15 #include "nsan.h"
16 #include "sanitizer_common/sanitizer_tls_get_addr.h"
17 
18 using namespace __nsan;
19 
20 NsanThread *NsanThread::Create(thread_callback_t start_routine, void *arg) {
21   uptr PageSize = GetPageSizeCached();
22   uptr size = RoundUpTo(sizeof(NsanThread), PageSize);
23   NsanThread *thread = (NsanThread *)MmapOrDie(size, __func__);
24   thread->start_routine_ = start_routine;
25   thread->arg_ = arg;
26   thread->destructor_iterations_ = GetPthreadDestructorIterations();
27 
28   return thread;
29 }
30 
31 void NsanThread::SetThreadStackAndTls() {
32   GetThreadStackAndTls(IsMainThread(), &stack_.bottom, &stack_.top, &tls_begin_,
33                        &tls_end_);
34   int local;
35   CHECK(AddrIsInStack((uptr)&local));
36 }
37 
38 void NsanThread::ClearShadowForThreadStackAndTLS() {
39   __nsan_set_value_unknown((const u8 *)stack_.bottom,
40                            stack_.top - stack_.bottom);
41   if (tls_begin_ != tls_end_)
42     __nsan_set_value_unknown((const u8 *)tls_begin_, tls_end_ - tls_begin_);
43   DTLS *dtls = DTLS_Get();
44   CHECK_NE(dtls, 0);
45   ForEachDVT(dtls, [](const DTLS::DTV &dtv, int id) {
46     __nsan_set_value_unknown((const u8 *)dtv.beg, dtv.size);
47   });
48 }
49 
50 void NsanThread::Init() {
51   SetThreadStackAndTls();
52   ClearShadowForThreadStackAndTLS();
53   malloc_storage().Init();
54 }
55 
56 void NsanThread::TSDDtor(void *tsd) {
57   NsanThread *t = (NsanThread *)tsd;
58   t->Destroy();
59 }
60 
61 void NsanThread::Destroy() {
62   malloc_storage().CommitBack();
63   // We also clear the shadow on thread destruction because
64   // some code may still be executing in later TSD destructors
65   // and we don't want it to have any poisoned stack.
66   ClearShadowForThreadStackAndTLS();
67   uptr size = RoundUpTo(sizeof(NsanThread), GetPageSizeCached());
68   UnmapOrDie(this, size);
69   DTLS_Destroy();
70 }
71 
72 thread_return_t NsanThread::ThreadStart() {
73   if (!start_routine_) {
74     // start_routine_ == 0 if we're on the main thread or on one of the
75     // OS X libdispatch worker threads. But nobody is supposed to call
76     // ThreadStart() for the worker threads.
77     return 0;
78   }
79 
80   return start_routine_(arg_);
81 }
82 
83 NsanThread::StackBounds NsanThread::GetStackBounds() const {
84   if (!stack_switching_)
85     return {stack_.bottom, stack_.top};
86   const uptr cur_stack = GET_CURRENT_FRAME();
87   // Note: need to check next stack first, because FinishSwitchFiber
88   // may be in process of overwriting stack_.top/bottom_. But in such case
89   // we are already on the next stack.
90   if (cur_stack >= next_stack_.bottom && cur_stack < next_stack_.top)
91     return {next_stack_.bottom, next_stack_.top};
92   return {stack_.bottom, stack_.top};
93 }
94 
95 uptr NsanThread::stack_top() { return GetStackBounds().top; }
96 
97 uptr NsanThread::stack_bottom() { return GetStackBounds().bottom; }
98 
99 bool NsanThread::AddrIsInStack(uptr addr) {
100   const auto bounds = GetStackBounds();
101   return addr >= bounds.bottom && addr < bounds.top;
102 }
103 
104 void NsanThread::StartSwitchFiber(uptr bottom, uptr size) {
105   CHECK(!stack_switching_);
106   next_stack_.bottom = bottom;
107   next_stack_.top = bottom + size;
108   stack_switching_ = true;
109 }
110 
111 void NsanThread::FinishSwitchFiber(uptr *bottom_old, uptr *size_old) {
112   CHECK(stack_switching_);
113   if (bottom_old)
114     *bottom_old = stack_.bottom;
115   if (size_old)
116     *size_old = stack_.top - stack_.bottom;
117   stack_.bottom = next_stack_.bottom;
118   stack_.top = next_stack_.top;
119   stack_switching_ = false;
120   next_stack_.top = 0;
121   next_stack_.bottom = 0;
122 }
123 
124 static pthread_key_t tsd_key;
125 static bool tsd_key_inited;
126 
127 void __nsan::NsanTSDInit(void (*destructor)(void *tsd)) {
128   CHECK(!tsd_key_inited);
129   tsd_key_inited = true;
130   CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
131 }
132 
133 static THREADLOCAL NsanThread *nsan_current_thread;
134 
135 NsanThread *__nsan::GetCurrentThread() { return nsan_current_thread; }
136 
137 void __nsan::SetCurrentThread(NsanThread *t) {
138   // Make sure we do not reset the current NsanThread.
139   CHECK_EQ(0, nsan_current_thread);
140   nsan_current_thread = t;
141   // Make sure that NsanTSDDtor gets called at the end.
142   CHECK(tsd_key_inited);
143   pthread_setspecific(tsd_key, t);
144 }
145 
146 void __nsan::NsanTSDDtor(void *tsd) {
147   NsanThread *t = (NsanThread *)tsd;
148   if (t->destructor_iterations_ > 1) {
149     t->destructor_iterations_--;
150     CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
151     return;
152   }
153   nsan_current_thread = nullptr;
154   // Make sure that signal handler can not see a stale current thread pointer.
155   atomic_signal_fence(memory_order_seq_cst);
156   NsanThread::TSDDtor(tsd);
157 }
158