1 //===- nsan_threads.cpp ---------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
11 #include "nsan_thread.h"
16 #include "sanitizer_common/sanitizer_tls_get_addr.h"
18 using namespace __nsan
;
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
;
26 thread
->destructor_iterations_
= GetPthreadDestructorIterations();
31 void NsanThread::SetThreadStackAndTls() {
32 GetThreadStackAndTls(IsMainThread(), &stack_
.bottom
, &stack_
.top
, &tls_begin_
,
35 CHECK(AddrIsInStack((uptr
)&local
));
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();
45 ForEachDVT(dtls
, [](const DTLS::DTV
&dtv
, int id
) {
46 __nsan_set_value_unknown((const u8
*)dtv
.beg
, dtv
.size
);
50 void NsanThread::Init() {
51 SetThreadStackAndTls();
52 ClearShadowForThreadStackAndTLS();
53 malloc_storage().Init();
56 void NsanThread::TSDDtor(void *tsd
) {
57 NsanThread
*t
= (NsanThread
*)tsd
;
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
);
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.
80 return start_routine_(arg_
);
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
};
95 uptr
NsanThread::stack_top() { return GetStackBounds().top
; }
97 uptr
NsanThread::stack_bottom() { return GetStackBounds().bottom
; }
99 bool NsanThread::AddrIsInStack(uptr addr
) {
100 const auto bounds
= GetStackBounds();
101 return addr
>= bounds
.bottom
&& addr
< bounds
.top
;
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;
111 void NsanThread::FinishSwitchFiber(uptr
*bottom_old
, uptr
*size_old
) {
112 CHECK(stack_switching_
);
114 *bottom_old
= stack_
.bottom
;
116 *size_old
= stack_
.top
- stack_
.bottom
;
117 stack_
.bottom
= next_stack_
.bottom
;
118 stack_
.top
= next_stack_
.top
;
119 stack_switching_
= false;
121 next_stack_
.bottom
= 0;
124 static pthread_key_t tsd_key
;
125 static bool tsd_key_inited
;
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
));
133 static THREADLOCAL NsanThread
*nsan_current_thread
;
135 NsanThread
*__nsan::GetCurrentThread() { return nsan_current_thread
; }
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
);
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
));
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
);