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 "berberis/guest_os_primitives/guest_thread.h"
18
19 #include <sys/mman.h> // mprotect
20
21 #if defined(__BIONIC__)
22 #include "private/bionic_constants.h"
23 #include "private/bionic_tls.h"
24 #endif
25
26 #include "berberis/base/checks.h"
27 #include "berberis/base/mmap.h"
28 #include "berberis/base/tracing.h"
29 #include "berberis/guest_state/guest_addr.h" // ToGuestAddr
30 #include "berberis/guest_state/guest_state_opaque.h"
31 #include "berberis/runtime_primitives/host_stack.h"
32 #include "native_bridge_support/linker/static_tls_config.h"
33
34 #if defined(__BIONIC__)
35 #include "get_tls.h"
36 #endif
37
38 extern "C" void berberis_UnmapAndExit(void* ptr, size_t size, int status);
39
40 namespace berberis {
41
42 // Avoid depending on the whole intrinsics module just for this symbol.
43 // TODO(b/232598137): Maybe export an isolated header from intrinsics for this. Or
44 // alternatively export it from runtime_library.h.
45 namespace intrinsics {
46
47 void InitState();
48
49 } // namespace intrinsics
50
51 NativeBridgeStaticTlsConfig g_static_tls_config;
52
53 namespace {
54
55 const size_t kGuestThreadPageAlignedSize = AlignUpPageSize(sizeof(GuestThread));
56
57 } // namespace
58
59 // static
Create()60 GuestThread* GuestThread::Create() {
61 // ATTENTION: GuestThread is aligned on 16, as fp registers in CPUState are aligned on 16, for
62 // efficient handling with aligned SSE memory access instructions. Thus, avoid using 'new', as
63 // it might not honor alignment! See b/64554026.
64 //
65 // ATTENTION: Bionic allocates thread internal data together with thread stack.
66 // In case of user provided stack, thread internal data goes there.
67 void* thread_storage = Mmap(kGuestThreadPageAlignedSize);
68 if (thread_storage == MAP_FAILED) {
69 return nullptr;
70 }
71
72 GuestThread* thread = new (thread_storage) GuestThread;
73 CHECK(thread);
74
75 thread->state_ = CreateThreadState();
76 if (!thread->state_) {
77 TRACE("failed to allocate thread state");
78 Destroy(thread);
79 return nullptr;
80 }
81 SetGuestThread(*thread->state_, thread);
82
83 intrinsics::InitState();
84
85 return thread;
86 }
87
88 // static
CreateClone(const GuestThread * parent,bool share_signal_actions)89 GuestThread* GuestThread::CreateClone(const GuestThread* parent, bool share_signal_actions) {
90 GuestThread* thread = Create();
91 if (thread == nullptr) {
92 return nullptr;
93 }
94
95 // TODO(156271630): alloc host stack guard?
96 thread->host_stack_ = MmapOrDie(GetStackSizeForTranslation());
97 if (thread->host_stack_ == MAP_FAILED) {
98 TRACE("failed to allocate host stack!");
99 thread->host_stack_ = nullptr;
100 Destroy(thread);
101 return nullptr;
102 }
103
104 SetCPUState(*thread->state(), GetCPUState(*parent->state()));
105 SetTlsAddr(*thread->state(), GetTlsAddr(*parent->state()));
106
107 if (share_signal_actions) {
108 // New shared_ptr user.
109 thread->signal_actions_ = parent->signal_actions_;
110 } else {
111 thread->CloneSignalActionsTableFrom(parent->signal_actions_.get());
112 }
113
114 return thread;
115 }
116
117 // static
CreatePthread(void * stack,size_t stack_size,size_t guard_size)118 GuestThread* GuestThread::CreatePthread(void* stack, size_t stack_size, size_t guard_size) {
119 GuestThread* thread = Create();
120 if (thread == nullptr) {
121 return nullptr;
122 }
123
124 if (!thread->AllocStack(stack, stack_size, guard_size)) {
125 Destroy(thread);
126 return nullptr;
127 }
128
129 SetStackRegister(GetCPUState(*thread->state()), thread->stack_top_);
130
131 if (!thread->AllocShadowCallStack()) {
132 Destroy(thread);
133 return nullptr;
134 }
135
136 SetShadowCallStackPointer(GetCPUState(*thread->state()), thread->scs_base_);
137
138 // Static TLS must be in an independent mapping, because on creation of main thread its config
139 // is yet unknown. Loader sets main thread's static TLS explicitly later.
140 if (!thread->AllocStaticTls()) {
141 Destroy(thread);
142 return nullptr;
143 }
144
145 thread->SetDefaultSignalActionsTable();
146
147 return thread;
148 }
149
150 // static
CreateForTest(ThreadState * state)151 GuestThread* GuestThread::CreateForTest(ThreadState* state) {
152 void* thread_storage = Mmap(kGuestThreadPageAlignedSize);
153 if (thread_storage == MAP_FAILED) {
154 return nullptr;
155 }
156 GuestThread* thread = new (thread_storage) GuestThread;
157 thread->state_ = state;
158 return thread;
159 }
160
161 // static
Destroy(GuestThread * thread)162 void GuestThread::Destroy(GuestThread* thread) {
163 CHECK(thread);
164 // ATTENTION: Don't run guest code from here!
165 if (ArePendingSignalsPresent(*thread->state_)) {
166 TRACE("thread destroyed with pending signals, signals ignored!");
167 }
168 thread->signal_actions_.reset();
169
170 if (thread->host_stack_) {
171 // This happens only on cleanup after failed creation.
172 MunmapOrDie(thread->host_stack_, GetStackSizeForTranslation());
173 }
174 if (thread->mmap_size_) {
175 MunmapOrDie(thread->stack_, thread->mmap_size_);
176 }
177 #if defined(__BIONIC__)
178 if (thread->static_tls_ != nullptr) {
179 MunmapOrDie(thread->static_tls_, AlignUpPageSize(g_static_tls_config.size));
180 }
181 if (thread->scs_region_ != nullptr) {
182 MunmapOrDie(thread->scs_region_, SCS_GUARD_REGION_SIZE);
183 }
184 #endif // defined(__BIONIC__)
185 if (thread->state_) {
186 DestroyThreadState(thread->state_);
187 }
188 MunmapOrDie(thread, kGuestThreadPageAlignedSize);
189 }
190
191 // static
Exit(GuestThread * thread,int status)192 void GuestThread::Exit(GuestThread* thread, int status) {
193 // Destroy the thread without unmapping the host stack.
194 void* host_stack = thread->host_stack_;
195 thread->host_stack_ = nullptr;
196 Destroy(thread);
197
198 if (host_stack) {
199 berberis_UnmapAndExit(host_stack, GetStackSizeForTranslation(), status);
200 } else {
201 syscall(__NR_exit, status);
202 }
203 FATAL("thread didn't exit");
204 }
205
AllocStack(void * stack,size_t stack_size,size_t guard_size)206 bool GuestThread::AllocStack(void* stack, size_t stack_size, size_t guard_size) {
207 // Here is what bionic does, see bionic/pthread_create.cpp:
208 //
209 // For user-provided stack, it assumes guard_size is included in stack size.
210 //
211 // For new stack, it adds given guard and stack sizes to get actual stack size:
212 // |<- guard_size ->|<- stack_size -------------------->|
213 // | guard | stack | pthread_internal_t | tls | GUARD |
214 // |<- actual stack_size --------->|
215 // ^ stack_base ^ stack_top
216
217 if (stack) {
218 // User-provided stack.
219 stack_ = nullptr; // Do not unmap in Destroy!
220 mmap_size_ = 0;
221 guard_size_ = guard_size;
222 stack_size_ = stack_size;
223 stack_top_ = ToGuestAddr(stack) + stack_size_;
224 return true;
225 }
226
227 guard_size_ = AlignUpPageSize(guard_size);
228 mmap_size_ = guard_size_ + AlignUpPageSize(stack_size);
229 stack_size_ = mmap_size_;
230
231 stack_ = Mmap(mmap_size_);
232 if (stack_ == MAP_FAILED) {
233 TRACE("failed to allocate stack!");
234 stack_ = nullptr; // Do not unmap in Destroy!
235 return false;
236 }
237
238 if (mprotect(stack_, guard_size_, PROT_NONE) != 0) {
239 TRACE("failed to protect stack!");
240 return false;
241 }
242
243 stack_top_ = ToGuestAddr(stack_) + stack_size_ - 16;
244 return true;
245 }
246
AllocShadowCallStack()247 bool GuestThread::AllocShadowCallStack() {
248 #if defined(__BIONIC__) && defined(BERBERIS_GUEST_LP64) && !defined(BERBERIS_GUEST_ARCH_X86_64)
249 CHECK(IsAlignedPageSize(SCS_GUARD_REGION_SIZE));
250 CHECK(IsAlignedPageSize(SCS_SIZE));
251
252 scs_region_ = Mmap(SCS_GUARD_REGION_SIZE);
253 if (scs_region_ == MAP_FAILED) {
254 TRACE("failed to allocate shadow call stack!");
255 scs_region_ = nullptr; // do not unmap in Destroy!
256 return false;
257 }
258
259 GuestAddr scs_region_base = ToGuestAddr(scs_region_);
260 // TODO(b/138425729): use random offset!
261 scs_base_ = AlignUp(scs_region_base, SCS_SIZE);
262 GuestAddr scs_top = scs_base_ + SCS_SIZE;
263
264 if (mprotect(scs_region_, scs_base_ - scs_region_base, PROT_NONE) != 0 ||
265 mprotect(ToHostAddr<void>(scs_top),
266 scs_region_base + SCS_GUARD_REGION_SIZE - scs_top,
267 PROT_NONE) != 0) {
268 TRACE("failed to protect shadow call stack!");
269 return false;
270 }
271 #endif // defined(__BIONIC__) && defined(BERBERIS_GUEST_LP64) &&
272 // !defined(BERBERIS_GUEST_ARCH_X86_64)
273 return true;
274 }
275
AllocStaticTls()276 bool GuestThread::AllocStaticTls() {
277 // For the main thread, this function is called twice.
278
279 CHECK_EQ(nullptr, static_tls_);
280
281 #if defined(__BIONIC__)
282 if (g_static_tls_config.size > 0) {
283 static_tls_ = Mmap(AlignUpPageSize(g_static_tls_config.size));
284 if (static_tls_ == MAP_FAILED) {
285 TRACE("failed to allocate static tls!");
286 static_tls_ = nullptr; // Do not unmap in Destroy!
287 return false;
288 }
289 }
290 #endif // defined(__BIONIC__)
291
292 return true;
293 }
294
InitStaticTls()295 void GuestThread::InitStaticTls() {
296 #if defined(__BIONIC__)
297 if (static_tls_ == nullptr) {
298 // Leave the thread pointer unset when starting the main thread.
299 return;
300 }
301 // First initialize static TLS using the initialization image, then update
302 // some of the TLS slots. Reuse the host's pthread_internal_t and
303 // bionic_tls objects. We verify that these structures are safe to use with
304 // checks in berberis/android_api/libc/pthread_translation.h.
305 memcpy(static_tls_, g_static_tls_config.init_img, g_static_tls_config.size);
306 void** tls =
307 reinterpret_cast<void**>(reinterpret_cast<char*>(static_tls_) + g_static_tls_config.tpoff);
308 tls[g_static_tls_config.tls_slot_thread_id] = GetTls()[TLS_SLOT_THREAD_ID];
309 tls[g_static_tls_config.tls_slot_bionic_tls] = GetTls()[TLS_SLOT_BIONIC_TLS];
310 GetTls()[TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE] = GetThreadStateStorage(*state_);
311 SetTlsAddr(*state_, ToGuestAddr(tls));
312 #else
313 // For Glibc we provide stub which is only usable to distinguish different threads.
314 // This is the only thing that many applications need.
315 SetTlsAddr(*state_, GettidSyscall());
316 #endif
317 }
318
ConfigStaticTls(const NativeBridgeStaticTlsConfig * config)319 void GuestThread::ConfigStaticTls(const NativeBridgeStaticTlsConfig* config) {
320 // This function is called during Bionic linker initialization, before any
321 // guest constructor functions run. It should be safe to omit locking.
322 g_static_tls_config = *config;
323
324 // Reinitialize the main thread's static TLS.
325 CHECK_EQ(true, AllocStaticTls());
326 InitStaticTls();
327 }
328
GetHostStackTop() const329 void* GuestThread::GetHostStackTop() const {
330 CHECK(host_stack_);
331 auto top = reinterpret_cast<uintptr_t>(host_stack_) + GetStackSizeForTranslation();
332 return reinterpret_cast<void*>(top);
333 }
334
335 } // namespace berberis
336