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 <atomic>
18 #include <csignal>
19 #include <memory>
20 #include <mutex>
21
22 #if defined(__BIONIC__)
23 #include <platform/bionic/reserved_signals.h>
24 #endif
25
26 #include "berberis/base/checks.h"
27 #include "berberis/base/config_globals.h"
28 #include "berberis/base/forever_alloc.h"
29 #include "berberis/base/tracing.h"
30 #include "berberis/guest_os_primitives/guest_signal.h"
31 #include "berberis/guest_os_primitives/guest_thread.h"
32 #include "berberis/guest_os_primitives/guest_thread_manager.h"
33 #include "berberis/guest_os_primitives/syscall_numbers.h"
34 #include "berberis/guest_state/guest_state_opaque.h"
35 #include "berberis/runtime_primitives/recovery_code.h"
36
37 #include "guest_signal_action.h"
38 #include "guest_thread_manager_impl.h" // AttachCurrentThread, DetachCurrentThread
39 #include "scoped_signal_blocker.h"
40
41 // Glibc didn't define this macro for i386 and x86_64 at the moment of adding
42 // its use below. This condition still stands though.
43 #ifndef SI_FROMKERNEL
44 #define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0)
45 #endif
46
47 namespace berberis {
48
49 namespace {
50
51 // Execution cannot proceed until the next pending signals check for _kernel_ sent
52 // synchronious signals: the faulty instruction will be executed again, leading
53 // to the infinite recursion. So crash immediately to simplify debugging.
54 //
55 // Note that a _user_ sent signal which is typically synchronious, such as SIGSEGV,
56 // can continue until pending signals check.
IsPendingSignalWithoutRecoveryCodeFatal(siginfo_t * info)57 bool IsPendingSignalWithoutRecoveryCodeFatal(siginfo_t* info) {
58 switch (info->si_signo) {
59 case SIGSEGV:
60 case SIGBUS:
61 case SIGILL:
62 case SIGFPE:
63 return SI_FROMKERNEL(info);
64 default:
65 return false;
66 }
67 }
68
69 // Technically guest threads may work with different signal action tables, so it's possible to
70 // optimize by using different mutexes. But it's rather an exotic corner case, so we keep it simple.
GetSignalActionsGuardMutex()71 std::mutex* GetSignalActionsGuardMutex() {
72 static auto* g_mutex = NewForever<std::mutex>();
73 return g_mutex;
74 }
75
FindSignalHandler(const GuestSignalActionsTable & signal_actions,int signal)76 const Guest_sigaction* FindSignalHandler(const GuestSignalActionsTable& signal_actions,
77 int signal) {
78 CHECK_GT(signal, 0);
79 CHECK_LE(signal, Guest__KERNEL__NSIG);
80 std::lock_guard<std::mutex> lock(*GetSignalActionsGuardMutex());
81 return &signal_actions.at(signal - 1).GetClaimedGuestAction();
82 }
83
GetHostRegIP(const ucontext_t * ucontext)84 uintptr_t GetHostRegIP(const ucontext_t* ucontext) {
85 #if defined(__i386__)
86 return ucontext->uc_mcontext.gregs[REG_EIP];
87 #elif defined(__x86_64__)
88 return ucontext->uc_mcontext.gregs[REG_RIP];
89 #elif defined(__riscv)
90 return ucontext->uc_mcontext.__gregs[REG_PC];
91 #elif defined(__aarch64__)
92 return ucontext->uc_mcontext.pc;
93 #else
94 #error "Unknown host arch"
95 #endif
96 }
97
SetHostRegIP(ucontext * ucontext,uintptr_t addr)98 void SetHostRegIP(ucontext* ucontext, uintptr_t addr) {
99 #if defined(__i386__)
100 ucontext->uc_mcontext.gregs[REG_EIP] = addr;
101 #elif defined(__x86_64__)
102 ucontext->uc_mcontext.gregs[REG_RIP] = addr;
103 #elif defined(__riscv)
104 ucontext->uc_mcontext.__gregs[REG_PC] = addr;
105 #elif defined(__aarch64__)
106 ucontext->uc_mcontext.pc = addr;
107 #else
108 #error "Unknown host arch"
109 #endif
110 }
111
112 // Can be interrupted by another HandleHostSignal!
HandleHostSignal(int sig,siginfo_t * info,void * context)113 void HandleHostSignal(int sig, siginfo_t* info, void* context) {
114 TRACE("handle host signal %d", sig);
115
116 bool attached;
117 GuestThread* thread = AttachCurrentThread(false, &attached);
118
119 // If pending signals are enabled, just add this signal to currently pending.
120 // If pending signals are disabled, run handlers for currently pending signals
121 // and for this signal now. While running the handlers, enable nested signals
122 // to be pending.
123 bool prev_pending_signals_enabled = thread->TestAndEnablePendingSignals();
124 thread->SetSignalFromHost(*info);
125 if (!prev_pending_signals_enabled) {
126 CHECK_EQ(GetResidence(*thread->state()), kOutsideGeneratedCode);
127 thread->ProcessAndDisablePendingSignals();
128 if (attached) {
129 DetachCurrentThread();
130 }
131 } else {
132 // We can't make signals pendings as we need to detach the thread!
133 CHECK(!attached);
134
135 // Run recovery code to restore precise context and exit generated code.
136 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
137 uintptr_t addr = GetHostRegIP(ucontext);
138 uintptr_t recovery_addr = FindRecoveryCode(addr, thread->state());
139
140 if (recovery_addr) {
141 if (!IsConfigFlagSet(kAccurateSigsegv)) {
142 // We often get asynchronious signals at instructions with recovery code.
143 // This is okay when the recovery is accurate, but highly fragile with inaccurate recovery.
144 if (!IsPendingSignalWithoutRecoveryCodeFatal(info)) {
145 TRACE("Skipping imprecise context recovery for non-fatal signal");
146 TRACE("Guest signal handler suspended, continue");
147 return;
148 }
149 TRACE(
150 "Imprecise context at recovery, only guest pc is in sync."
151 " Other registers may be stale.");
152 }
153 SetHostRegIP(ucontext, recovery_addr);
154 TRACE("guest signal handler suspended, run recovery for host pc %p at host pc %p",
155 reinterpret_cast<void*>(addr),
156 reinterpret_cast<void*>(recovery_addr));
157 } else {
158 // Failed to find recovery code.
159 // Translated code should be arranged to continue till
160 // the next pending signals check unless it's fatal.
161 if (IsPendingSignalWithoutRecoveryCodeFatal(info)) {
162 LOG_ALWAYS_FATAL("Cannot process signal %d", sig);
163 }
164 TRACE("guest signal handler suspended, continue");
165 }
166 }
167 }
168
IsReservedSignal(int signal)169 bool IsReservedSignal(int signal) {
170 switch (signal) {
171 // Disallow guest action for SIGABRT to simplify debugging (b/32167022).
172 case SIGABRT:
173 #if defined(__BIONIC__)
174 // Disallow overwriting the host profiler handler from guest code. Otherwise
175 // guest __libc_init_profiling_handlers() would install its own handler, which
176 // is not yet supported for guest code (at least need a proxy for
177 // heapprofd_client.so) and fundamentally cannot be supported for host code.
178 // TODO(b/167966989): Instead intercept __libc_init_profiling_handlers.
179 case BIONIC_SIGNAL_PROFILER:
180 #endif
181 return true;
182 }
183 return false;
184 }
185
186 } // namespace
187
SetDefaultSignalActionsTable()188 void GuestThread::SetDefaultSignalActionsTable() {
189 static auto* g_signal_actions = NewForever<GuestSignalActionsTable>();
190 // We need to initialize shared_ptr, but we don't want to attempt to delete the default
191 // signal actions when guest thread terminates. Hence we specify a void deleter.
192 signal_actions_ = std::shared_ptr<GuestSignalActionsTable>(g_signal_actions, [](auto) {});
193 }
194
CloneSignalActionsTableFrom(GuestSignalActionsTable * from_table)195 void GuestThread::CloneSignalActionsTableFrom(GuestSignalActionsTable* from_table) {
196 // Need lock to make sure from_table isn't changed concurrently.
197 std::lock_guard<std::mutex> lock(*GetSignalActionsGuardMutex());
198 signal_actions_ = std::make_shared<GuestSignalActionsTable>(*from_table);
199 }
200
201 // Can be interrupted by another SetSignal!
SetSignalFromHost(const siginfo_t & host_info)202 void GuestThread::SetSignalFromHost(const siginfo_t& host_info) {
203 siginfo_t* guest_info = pending_signals_.AllocSignal();
204
205 // Convert host siginfo to guest.
206 *guest_info = host_info;
207 switch (host_info.si_signo) {
208 case SIGILL:
209 case SIGFPE: {
210 guest_info->si_addr = ToHostAddr<void>(GetInsnAddr(GetCPUState(*state_)));
211 break;
212 }
213 case SIGSYS: {
214 guest_info->si_syscall = ToGuestSyscallNumber(host_info.si_syscall);
215 break;
216 }
217 }
218
219 // This is never interrupted by code that clears queue or status,
220 // so the order in which to set them is not important.
221 pending_signals_.EnqueueSignal(guest_info);
222 // Check that pending signals are not disabled and mark them as present.
223 uint8_t old_status = GetPendingSignalsStatusAtomic(*state_).exchange(kPendingSignalsPresent,
224 std::memory_order_relaxed);
225 CHECK_NE(kPendingSignalsDisabled, old_status);
226 }
227
SigAltStack(const stack_t * ss,stack_t * old_ss,int * error)228 bool GuestThread::SigAltStack(const stack_t* ss, stack_t* old_ss, int* error) {
229 // The following code is not reentrant!
230 ScopedSignalBlocker signal_blocker;
231
232 if (old_ss) {
233 if (sig_alt_stack_) {
234 old_ss->ss_sp = sig_alt_stack_;
235 old_ss->ss_size = sig_alt_stack_size_;
236 old_ss->ss_flags = IsOnSigAltStack() ? SS_ONSTACK : 0;
237 } else {
238 old_ss->ss_sp = nullptr;
239 old_ss->ss_size = 0;
240 old_ss->ss_flags = SS_DISABLE;
241 }
242 }
243 if (ss) {
244 if (sig_alt_stack_ && IsOnSigAltStack()) {
245 *error = EPERM;
246 return false;
247 }
248 if (ss->ss_flags == SS_DISABLE) {
249 sig_alt_stack_ = nullptr;
250 sig_alt_stack_size_ = 0;
251 return true;
252 }
253 if (ss->ss_flags != 0) {
254 *error = EINVAL;
255 return false;
256 }
257 if (ss->ss_size < GetGuest_MINSIGSTKSZ()) {
258 *error = ENOMEM;
259 return false;
260 }
261 sig_alt_stack_ = ss->ss_sp;
262 sig_alt_stack_size_ = ss->ss_size;
263 }
264 return true;
265 }
266
SwitchToSigAltStack()267 void GuestThread::SwitchToSigAltStack() {
268 if (sig_alt_stack_ && !IsOnSigAltStack()) {
269 // TODO(b/289563835): Try removing `- 16` while ensuring app compatibility.
270 // Reliable context on why we use `- 16` here seems to be lost.
271 SetStackRegister(GetCPUState(*state_), ToGuestAddr(sig_alt_stack_) + sig_alt_stack_size_ - 16);
272 }
273 }
274
IsOnSigAltStack() const275 bool GuestThread::IsOnSigAltStack() const {
276 CHECK_NE(sig_alt_stack_, nullptr);
277 const char* ss_start = static_cast<const char*>(sig_alt_stack_);
278 const char* ss_curr = ToHostAddr<const char>(GetStackRegister(GetCPUState(*state_)));
279 return ss_curr >= ss_start && ss_curr < ss_start + sig_alt_stack_size_;
280 }
281
ProcessPendingSignals()282 void GuestThread::ProcessPendingSignals() {
283 for (;;) {
284 // Process pending signals while present.
285 uint8_t status = GetPendingSignalsStatusAtomic(*state_).load(std::memory_order_acquire);
286 CHECK_NE(kPendingSignalsDisabled, status);
287 if (status == kPendingSignalsEnabled) {
288 return;
289 }
290 ProcessPendingSignalsImpl();
291 }
292 }
293
ProcessAndDisablePendingSignals()294 bool GuestThread::ProcessAndDisablePendingSignals() {
295 for (;;) {
296 // If pending signals are not present, cas should disable them.
297 // Otherwise, process pending signals and try again.
298 uint8_t old_status = kPendingSignalsEnabled;
299 if (GetPendingSignalsStatusAtomic(*state_).compare_exchange_weak(
300 old_status, kPendingSignalsDisabled, std::memory_order_acq_rel)) {
301 return true;
302 }
303 if (old_status == kPendingSignalsDisabled) {
304 return false;
305 }
306 ProcessPendingSignalsImpl();
307 }
308 }
309
TestAndEnablePendingSignals()310 bool GuestThread::TestAndEnablePendingSignals() {
311 // If pending signals are disabled, cas should mark them enabled.
312 // Otherwise, pending signals are already enabled.
313 uint8_t old_status = kPendingSignalsDisabled;
314 return !GetPendingSignalsStatusAtomic(*state_).compare_exchange_strong(
315 old_status, kPendingSignalsEnabled, std::memory_order_acq_rel);
316 }
317
318 // Return if another iteration is needed.
319 // ATTENTION: Can be interrupted by SetSignal!
ProcessPendingSignalsImpl()320 void GuestThread::ProcessPendingSignalsImpl() {
321 // Clear pending signals status and queue.
322 // ATTENTION: It is important to change status before the queue!
323 // Otherwise if interrupted by SetSignal, we might end up with
324 // no pending signals status but with non-empty queue!
325 GetPendingSignalsStatusAtomic(*state_).store(kPendingSignalsEnabled, std::memory_order_relaxed);
326
327 siginfo_t* signal_info;
328 while ((signal_info = pending_signals_.DequeueSignalUnsafe())) {
329 const Guest_sigaction* sa = FindSignalHandler(*signal_actions_.get(), signal_info->si_signo);
330 ProcessGuestSignal(this, sa, signal_info);
331 pending_signals_.FreeSignal(signal_info);
332 }
333 }
334
SetGuestSignalHandler(int signal,const Guest_sigaction * act,Guest_sigaction * old_act,int * error)335 bool SetGuestSignalHandler(int signal,
336 const Guest_sigaction* act,
337 Guest_sigaction* old_act,
338 int* error) {
339 #if defined(__riscv)
340 TRACE("ATTENTION: SetGuestSignalHandler is unimplemented - skipping it without raising an error");
341 return true;
342 #endif
343 if (signal < 1 || signal > Guest__KERNEL__NSIG) {
344 *error = EINVAL;
345 return false;
346 }
347
348 if (act && IsReservedSignal(signal)) {
349 TRACE("sigaction for reserved signal %d not set", signal);
350 act = nullptr;
351 }
352
353 std::lock_guard<std::mutex> lock(*GetSignalActionsGuardMutex());
354 GuestSignalAction& action = GetCurrentGuestThread()->GetSignalActionsTable()->at(signal - 1);
355 return action.Change(signal, act, HandleHostSignal, old_act, error);
356 }
357
358 } // namespace berberis
359