• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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