1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <pthread.h>
18 #include <sys/types.h> // pid_t
19 #include <cstddef> // size_t
20
21 #include "berberis/base/checks.h"
22 #include "berberis/base/tracing.h"
23 #include "berberis/guest_os_primitives/guest_thread.h"
24 #include "berberis/guest_os_primitives/guest_thread_manager.h"
25 #include "berberis/instrument/guest_thread.h"
26 #include "berberis/runtime_primitives/code_pool.h" // ResetAllExecRegions
27 #include "guest_thread_manager_impl.h"
28 #include "guest_thread_map.h"
29 #include "scoped_signal_blocker.h"
30
31 namespace berberis {
32
33 // Manages thread local storage (TLS) for the current thread's GuestThread instance.
34 pthread_key_t g_guest_thread_key;
35
36 namespace {
37
GuestThreadDtor(void *)38 void GuestThreadDtor(void* /* arg */) {
39 // TLS cache was cleared by pthread_exit.
40 // TODO(b/280671643): Postpone detach to last pthread destructor iteration.
41 // On previous iterations, simply restore TLS cache and return.
42 DetachCurrentThread();
43 }
44
45 } // namespace
46
47 // Not thread safe, not async signals safe!
InitGuestThreadManager()48 void InitGuestThreadManager() {
49 // Here we don't need pthread_once, which is not reentrant due to spinlocks.
50 CHECK_EQ(0, pthread_key_create(&g_guest_thread_key, GuestThreadDtor));
51 }
52
GetCurrentGuestThread()53 GuestThread* GetCurrentGuestThread() {
54 bool attached;
55 return AttachCurrentThread(true, &attached);
56 }
57
ResetCurrentGuestThreadAfterFork(GuestThread * thread)58 void ResetCurrentGuestThreadAfterFork(GuestThread* thread) {
59 GuestThreadMap::GetInstance()->ResetThreadTable(GettidSyscall(), thread);
60 #if defined(__BIONIC__)
61 // Force (host) bionic to update cached tid if necessary
62 // 1. Bionic `clone` implementation resets cached `tid` before syscall
63 // so that it does not get accidentally propagate to the child.
64 // 2. pthread_lock/unlock implementations do not call `gettid()` they
65 // instead access cached value directly from TLS. Which leads to
66 // a situation where cached `tid` is updated in the middle of
67 // `dlopen` and it fails to unlock the mutex because the
68 // ownership check fails. Subsequent `dlsym` (or any other
69 // dl* call) stops on locked mutex.
70 //
71 // By calling `gettid()` here we force bionic to set the cached
72 // value to the correct one.
73 CHECK_NE(gettid(), -1);
74 #endif
75 ResetAllExecRegions();
76 }
77
GetGuestThreadAttr(pid_t tid,GuestAddr * stack_base,size_t * stack_size,size_t * guard_size,int * error)78 bool GetGuestThreadAttr(pid_t tid,
79 GuestAddr* stack_base,
80 size_t* stack_size,
81 size_t* guard_size,
82 int* error) {
83 GuestThread* thread = GuestThreadMap::GetInstance()->FindThread(tid);
84 if (thread) {
85 thread->GetAttr(stack_base, stack_size, guard_size);
86 return true;
87 }
88 *error = ESRCH;
89 return false;
90 }
91
ExitCurrentThread(int status)92 void ExitCurrentThread(int status) {
93 pid_t tid = GettidSyscall();
94
95 // The following code is not reentrant!
96 ScopedSignalBlocker signal_blocker;
97
98 // Remove thread from global table.
99 GuestThread* thread = GuestThreadMap::GetInstance()->RemoveThread(tid);
100 if (kInstrumentGuestThread) {
101 OnRemoveGuestThread(tid, thread);
102 }
103
104 TRACE("guest thread exited %d", tid);
105 GuestThread::Exit(thread, status);
106 }
107
108 // We assume translation cache is already modified. If any thread still runs
109 // a region that is already obsolete, we should force the thread to dispatcher
110 // to re-read from translation cache. We should also wait for that thread to
111 // acknowledge the dispatch, so code that called cache invalidation can be sure
112 // that obsolete code is never run after this point.
FlushGuestCodeCache()113 void FlushGuestCodeCache() {
114 // TODO(b/28081995): at the moment we don't know what range was flushed, so
115 // we have to force ALL guest threads to dispatcher. This is really, really,
116 // REALLY bad for performance.
117 // TODO(b/28081995): at the moment we don't wait for acknowledgment. This
118 // might cause subtle guest logic failures.
119 pid_t current_tid = GettidSyscall();
120 GuestThreadMap::GetInstance()->ForEachThread([current_tid](pid_t tid, GuestThread* thread) {
121 // ATTENTION: we probably don't want to force current thread to dispatcher
122 // and to wait for it to acknowledge :) Assume caller of this function
123 // (syscall emulation or trampoline) will force re-read from translation
124 // cache before continuing to guest code.
125 if (tid != current_tid) {
126 // Set thread's pending signals to present to force it to dispatcher.
127 // ATTENTION! this is the only place we access pending_signals_status
128 // from other thread!
129 uint8_t old_status = kPendingSignalsEnabled;
130 GetPendingSignalsStatusAtomic(*thread->state())
131 .compare_exchange_strong(old_status, kPendingSignalsPresent, std::memory_order_acq_rel);
132 }
133 });
134 }
135
136 // Common guest thread function attaches GuestThread lazily on first call and detaches in pthread
137 // key destructor (register_dtor = true).
138 //
139 // Guest signal handlers and guest pthread key destructors are special as they might be called when
140 // GuestThread is not yet attached or is already detached. Moreover, they cannot determine between
141 // latter cases. Thus, signal handlers and key destructors reuse GuestThread if it is attached,
142 // otherwise they attach AND detach themselves, so GuestThread attach state is preserved and
143 // GuestThread is never leaked (register_dtor = false).
144 //
145 // ATTENTION: When signal handler or key destructor attach GuestThread themselves, they might get
146 // GuestThread stack different from one used in thread function. It might confuse several
147 // (ill-formed?) apks, so we issue a warning.
148 //
149 // ATTENTION: Can be interrupted!
AttachCurrentThread(bool register_dtor,bool * attached)150 GuestThread* AttachCurrentThread(bool register_dtor, bool* attached) {
151 // The following code is not reentrant!
152 ScopedSignalBlocker signal_blocker;
153
154 pid_t tid = GettidSyscall();
155 GuestThread* thread = GuestThreadMap::GetInstance()->FindThread(tid);
156 if (thread) {
157 // Thread was already attached.
158 *attached = false;
159 return thread;
160 }
161
162 // Copy host stack size attributes.
163 size_t stack_size;
164 size_t guard_size;
165 pthread_attr_t attr;
166 CHECK_EQ(0, pthread_getattr_np(pthread_self(), &attr));
167 CHECK_EQ(0, pthread_attr_getstacksize(&attr, &stack_size));
168 CHECK_EQ(0, pthread_attr_getguardsize(&attr, &guard_size));
169 thread = GuestThread::CreatePthread(nullptr, stack_size, guard_size);
170 CHECK(thread);
171
172 InsertCurrentThread(thread, register_dtor);
173 thread->InitStaticTls();
174
175 // If thread is attached in HandleHostSignal we must run guest handler
176 // immediately because we detach guest thread before exit from HandleHostSignal.
177 // All non-reentrant code in runtime must be protected with ScopedPendingSignalsEnabler.
178 GetPendingSignalsStatusAtomic(*thread->state()) = kPendingSignalsDisabled;
179 // AttachCurrentThread is never called from generated code.
180 SetResidence(*thread->state(), kOutsideGeneratedCode);
181
182 *attached = true;
183 return thread;
184 }
185
InsertCurrentThread(GuestThread * thread,bool register_dtor)186 void InsertCurrentThread(GuestThread* thread, bool register_dtor) {
187 pid_t tid = GettidSyscall();
188
189 // The following code is not reentrant!
190 ScopedSignalBlocker signal_blocker;
191
192 // Thread should not be already in the table!
193 // If signal came after we checked tls cache or table but before we blocked signals, it should
194 // have attached AND detached the thread!
195 GuestThreadMap::GetInstance()->InsertThread(tid, thread);
196 if (register_dtor) {
197 CHECK_EQ(0, pthread_setspecific(g_guest_thread_key, thread));
198 }
199 if (kInstrumentGuestThread) {
200 OnInsertGuestThread(tid, thread);
201 }
202
203 TRACE("guest thread attached %d", tid);
204 }
205
206 // ATTENTION: Can be interrupted!
DetachCurrentThread()207 void DetachCurrentThread() {
208 pid_t tid = GettidSyscall();
209
210 // The following code is not reentrant!
211 ScopedSignalBlocker signal_blocker;
212
213 // Remove thread from global table.
214 GuestThread* thread = GuestThreadMap::GetInstance()->RemoveThread(tid);
215 if (kInstrumentGuestThread) {
216 OnRemoveGuestThread(tid, thread);
217 }
218
219 TRACE("guest thread detached %d", tid);
220 GuestThread::Destroy(thread);
221 }
222
223 } // namespace berberis
224