158 lines · cpp
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-exception6//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 18using namespace __nsan;19 20NsanThread *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 31void NsanThread::SetThreadStackAndTls() {32 GetThreadStackAndTls(IsMainThread(), &stack_.bottom, &stack_.top, &tls_begin_,33 &tls_end_);34 int local;35 CHECK(AddrIsInStack((uptr)&local));36}37 38void 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 50void NsanThread::Init() {51 SetThreadStackAndTls();52 ClearShadowForThreadStackAndTLS();53 malloc_storage().Init();54}55 56void NsanThread::TSDDtor(void *tsd) {57 NsanThread *t = (NsanThread *)tsd;58 t->Destroy();59}60 61void NsanThread::Destroy() {62 malloc_storage().CommitBack();63 // We also clear the shadow on thread destruction because64 // some code may still be executing in later TSD destructors65 // 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 72thread_return_t NsanThread::ThreadStart() {73 if (!start_routine_) {74 // start_routine_ == 0 if we're on the main thread or on one of the75 // OS X libdispatch worker threads. But nobody is supposed to call76 // ThreadStart() for the worker threads.77 return 0;78 }79 80 return start_routine_(arg_);81}82 83NsanThread::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 FinishSwitchFiber88 // may be in process of overwriting stack_.top/bottom_. But in such case89 // 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 95uptr NsanThread::stack_top() { return GetStackBounds().top; }96 97uptr NsanThread::stack_bottom() { return GetStackBounds().bottom; }98 99bool NsanThread::AddrIsInStack(uptr addr) {100 const auto bounds = GetStackBounds();101 return addr >= bounds.bottom && addr < bounds.top;102}103 104void 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 111void 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 124static pthread_key_t tsd_key;125static bool tsd_key_inited;126 127void __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 133static THREADLOCAL NsanThread *nsan_current_thread;134 135NsanThread *__nsan::GetCurrentThread() { return nsan_current_thread; }136 137void __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 146void __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