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