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