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