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