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 <linux/unistd.h>
18 #include <sched.h>
19 #include <semaphore.h>
20
21 #include "berberis/base/checks.h"
22 #include "berberis/base/tracing.h"
23 #include "berberis/guest_os_primitives/guest_signal.h"
24 #include "berberis/guest_os_primitives/guest_thread.h"
25 #include "berberis/guest_os_primitives/guest_thread_manager.h" // ResetCurrentGuestThreadAfterFork
26 #include "berberis/guest_os_primitives/scoped_pending_signals.h"
27 #include "berberis/guest_state/guest_addr.h"
28 #include "berberis/guest_state/guest_state_opaque.h"
29 #include "berberis/runtime/execute_guest.h"
30 #include "berberis/runtime_primitives/runtime_library.h"
31
32 #include "guest_signal_action.h"
33 #include "guest_thread_manager_impl.h"
34 #include "scoped_signal_blocker.h"
35
36 namespace berberis {
37
38 namespace {
39
CloneSyscall(long flags,long child_stack,long parent_tid,long new_tls,long child_tid)40 long CloneSyscall(long flags, long child_stack, long parent_tid, long new_tls, long child_tid) {
41 #if defined(__x86_64__) // sys_clone's last two arguments are flipped on x86-64.
42 return syscall(__NR_clone, flags, child_stack, parent_tid, child_tid, new_tls);
43 #else
44 return syscall(__NR_clone, flags, child_stack, parent_tid, new_tls, child_tid);
45 #endif
46 }
47
48 struct GuestThreadCloneInfo {
49 GuestThread* thread;
50 HostSigset mask;
51 sem_t sem;
52 };
53
RunClonedGuestThread(void * arg)54 int RunClonedGuestThread(void* arg) {
55 GuestThreadCloneInfo* info = static_cast<GuestThreadCloneInfo*>(arg);
56 GuestThread* thread = info->thread;
57
58 // Cannot use host pthread_key!
59 // TODO(b/280551726): Clear guest thread in exit syscall.
60 InsertCurrentThread(thread, false);
61
62 // ExecuteGuest requires pending signals enabled.
63 ScopedPendingSignalsEnabler scoped_pending_signals_enabler(thread);
64
65 // Host signals are blocked in parent before the clone,
66 // and remain blocked in child until this point.
67 RTSigprocmaskSyscallOrDie(SIG_SETMASK, &info->mask, nullptr);
68
69 // Notify parent that child is ready. Now parent can:
70 // - search for child in thread table
71 // - send child a signal
72 // - dispose info
73 CHECK_EQ(0, sem_post(&info->sem));
74 // TODO(b/77574158): Ensure caller has a chance to handle the notification.
75 sched_yield();
76
77 ExecuteGuest(thread->state());
78
79 LOG_ALWAYS_FATAL("cloned thread didn't exit");
80 return 0;
81 }
82
83 } // namespace
84
85 // go/berberis-guest-threads
CloneGuestThread(GuestThread * thread,int flags,GuestAddr guest_stack_top,GuestAddr parent_tid,GuestAddr new_tls,GuestAddr child_tid)86 pid_t CloneGuestThread(GuestThread* thread,
87 int flags,
88 GuestAddr guest_stack_top,
89 GuestAddr parent_tid,
90 GuestAddr new_tls,
91 GuestAddr child_tid) {
92 ThreadState& thread_state = *thread->state();
93 if (!(flags & CLONE_VM)) {
94 // Memory is *not* shared with the child.
95 // Run the child on the same host stack as the parent. Thus, can use host local variables.
96 // The child gets a copy of guest thread object.
97 // ATTENTION: Do not set new tls for the host - tls might be incompatible.
98 // TODO(b/280551726): Consider forcing new host tls to 0.
99 long pid = CloneSyscall(flags & ~CLONE_SETTLS, 0, parent_tid, 0, child_tid);
100 if (pid == 0) {
101 // Child, reset thread table.
102 ResetCurrentGuestThreadAfterFork(thread);
103 if (guest_stack_top) {
104 SetStackRegister(GetCPUState(thread_state), guest_stack_top);
105 // TODO(b/280551726): Reset stack attributes?
106 }
107 if ((flags & CLONE_SETTLS)) {
108 SetTlsAddr(thread_state, new_tls);
109 }
110 }
111 return pid;
112 }
113
114 // Memory is shared with the child.
115 // The child needs a distinct stack, both host and guest! Because of the distinct host stack,
116 // cannot use host local variables. For now, use clone function to pass parameters to the child.
117 // The child needs new instance of guest thread object.
118
119 GuestThreadCloneInfo info;
120
121 info.thread = GuestThread::CreateClone(thread, (flags & CLONE_SIGHAND) != 0);
122 if (info.thread == nullptr) {
123 return EAGAIN;
124 }
125
126 ThreadState& clone_thread_state = *info.thread->state();
127
128 if ((flags & CLONE_SETTLS)) {
129 SetTlsAddr(clone_thread_state, new_tls);
130 }
131
132 // Current insn addr is on SVC instruction, move to the next.
133 // TODO(b/280551726): Not needed if we can use raw syscall and continue current execution.
134 CPUState& clone_cpu = GetCPUState(clone_thread_state);
135 AdvanceInsnAddrBeyondSyscall(clone_cpu);
136 SetReturnValueRegister(clone_cpu, 0); // Syscall return value
137
138 if (guest_stack_top != kNullGuestAddr) {
139 SetStackRegister(GetCPUState(clone_thread_state), guest_stack_top);
140 SetLinkRegister(clone_cpu, kNullGuestAddr);
141 } else {
142 if (!(flags & CLONE_VFORK)) {
143 TRACE("CLONE_VM with NULL guest stack and not in CLONE_VFORK mode, returning EINVAL");
144 return EINVAL;
145 }
146 // See b/323981318 and b/156400255.
147 TRACE("CLONE_VFORK with CLONE_VM and NULL guest stack, will share guest stack with parent");
148 // GuestThread::CreateClone has already copied stack and link pointers to new thread.
149 }
150
151 // Thread must start with pending signals while it's executing runtime code.
152 SetPendingSignalsStatusAtomic(clone_thread_state, kPendingSignalsEnabled);
153 SetResidence(clone_thread_state, kOutsideGeneratedCode);
154
155 sem_init(&info.sem, 0, 0);
156
157 // ATTENTION: Don't set new tls for the host - tls might be incompatible.
158 // TODO(b/280551726): Consider forcing new host tls to 0.
159 long pid;
160 {
161 ScopedSignalBlocker signal_blocker;
162 info.mask = *signal_blocker.old_mask();
163 pid = clone(RunClonedGuestThread,
164 info.thread->GetHostStackTop(),
165 flags & ~CLONE_SETTLS,
166 &info,
167 parent_tid,
168 nullptr,
169 child_tid);
170 if (pid != -1) {
171 CHECK_EQ(0, sem_wait(&info.sem)); // Wait with blocked signals to avoid EINTR.
172 }
173 }
174
175 if (pid == -1) {
176 GuestThread::Destroy(info.thread);
177 }
178
179 sem_destroy(&info.sem);
180 return pid;
181 }
182
183 } // namespace berberis
184