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 "guest_signal_action.h"
18
19 #include <cerrno>
20 #include <csignal>
21 #include <cstring>
22
23 #include "berberis/base/bit_util.h"
24 #include "berberis/base/checks.h"
25 #include "berberis/base/host_signal.h"
26 #include "berberis/base/scoped_errno.h"
27 #include "berberis/guest_os_primitives/guest_signal.h"
28 #include "berberis/runtime_primitives/host_function_wrapper_impl.h" // UnwrapHostFunction
29
30 // glibc doesn't define SA_RESTORER globally.
31 #ifndef SA_RESTORER
32 #define SA_RESTORER 0x04000000
33 #endif
34
35 namespace berberis {
36
37 namespace {
38
DoSigaction(int sig,const HostStructSigaction * sa,HostStructSigaction * old_sa,int * error)39 bool DoSigaction(int sig, const HostStructSigaction* sa, HostStructSigaction* old_sa, int* error) {
40 ScopedErrno scoped_errno;
41 if (HostSigaction(sig, sa, old_sa) == 0) {
42 return true;
43 }
44 *error = errno;
45 return false;
46 }
47
ConvertHostSigactionToGuest(const HostStructSigaction * host_sa,Guest_sigaction * guest_sa)48 void ConvertHostSigactionToGuest(const HostStructSigaction* host_sa, Guest_sigaction* guest_sa) {
49 guest_sa->guest_sa_sigaction = WrapHostSigactionForGuest(*host_sa);
50
51 // We don't support SA_RESTORER flag for non-canonical handlers. See: b/36458045
52 if (bool(host_sa->sa_flags & SA_RESTORER)) {
53 // Recognize canonical (kernel-provided) x86 handlers.
54 // ATTENTION: kernel tolerates the case when SA_RESTORER is set but sa_restorer is null!
55 if (host_sa->sa_restorer) {
56 const char* handler = bit_cast<const char*>(host_sa->sa_restorer);
57 #if defined(__i386__)
58 if ((memcmp(handler, "\x58\xb8\x77\x00\x00\x00\xcd\x80", 8) != 0) && // x86 sigreturn
59 (memcmp(handler, "\xb8\xad\x00\x00\x00\xcd\x80", 7) != 0)) { // x86 rt_sigreturn
60 LOG_ALWAYS_FATAL("Unknown x86 sa_restorer in host sigaction!");
61 }
62 #elif defined(__x86_64__)
63 if (memcmp(handler, "\x48\xc7\xc0\x0f\x00\x00\x00\x0f\x05", 9) != 0) { // x86_64 sigreturn
64 LOG_ALWAYS_FATAL("Unknown x86_64 sa_restorer in host sigaction!");
65 }
66 #elif defined(__riscv)
67 LOG_ALWAYS_FATAL("Unimplemented for riscv64");
68 #elif defined(__aarch64__)
69 LOG_ALWAYS_FATAL("Unimplemented for arm64");
70 #else
71 #error "Unknown host arch"
72 #endif
73 }
74 }
75
76 guest_sa->sa_flags = host_sa->sa_flags & ~SA_RESTORER;
77 ResetSigactionRestorer(guest_sa);
78 ConvertToSmallSigset(host_sa->sa_mask, &guest_sa->sa_mask);
79 }
80
ConvertGuestSigactionToHost(const Guest_sigaction * guest_sa,GuestSignalAction::host_sa_sigaction_t claimed_host_sa_sigaction,HostStructSigaction * host_sa)81 bool ConvertGuestSigactionToHost(const Guest_sigaction* guest_sa,
82 GuestSignalAction::host_sa_sigaction_t claimed_host_sa_sigaction,
83 HostStructSigaction* host_sa) {
84 bool claim = false;
85 if (guest_sa->sa_flags & SA_SIGINFO) {
86 if (guest_sa->guest_sa_sigaction == 0) {
87 // It can happen that we are requested to set SIG_DFL (= 0) _sigaction_ (not _handler_)!
88 // Don't claim and just keep host responsible for this!
89 host_sa->sa_sigaction = nullptr;
90 } else if (void* func = UnwrapHostFunction(guest_sa->guest_sa_sigaction)) {
91 host_sa->sa_sigaction = reinterpret_cast<GuestSignalAction::host_sa_sigaction_t>(func);
92 } else {
93 host_sa->sa_sigaction = claimed_host_sa_sigaction;
94 claim = true;
95 }
96 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_DFL) {
97 host_sa->sa_handler = SIG_DFL;
98 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_IGN) {
99 host_sa->sa_handler = SIG_IGN;
100 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_ERR) {
101 host_sa->sa_handler = SIG_ERR;
102 } else {
103 void* func = UnwrapHostFunction(guest_sa->guest_sa_sigaction);
104 if (func) {
105 host_sa->sa_handler = reinterpret_cast<void (*)(int)>(func);
106 } else {
107 host_sa->sa_sigaction = claimed_host_sa_sigaction;
108 claim = true;
109 }
110 }
111
112 // We don't support SA_RESTORER flag for non-canonical handlers. See: b/36458045
113 if (bool(guest_sa->sa_flags & SA_RESTORER)) {
114 CheckSigactionRestorer(guest_sa);
115 }
116
117 host_sa->sa_flags = guest_sa->sa_flags & ~SA_RESTORER;
118 host_sa->sa_restorer = nullptr;
119 if (claim) {
120 host_sa->sa_flags |= SA_SIGINFO;
121 }
122
123 // ATTENTION: it might seem tempting to run claimed_host_sa_sigaction with all signals blocked.
124 // But, guest signal handler should run with current thread signal mask + guest action signal
125 // mask, and might expect certain signals to interrupt. If pending signals are disabled, then
126 // claimed_host_sa_sigaction executes guest signal handler within, so at that point signal mask
127 // should be correct. Unfortunately, if claimed_host_sa_sigaction gets invoked with all signals
128 // blocked, there seems to be no way to restore the correct signal mask before running guest
129 // signal handler.
130 ConvertToBigSigset(guest_sa->sa_mask, &host_sa->sa_mask);
131
132 return claim;
133 }
134
135 } // namespace
136
Change(int sig,const Guest_sigaction * new_sa,host_sa_sigaction_t claimed_host_sa_sigaction,Guest_sigaction * old_sa,int * error)137 bool GuestSignalAction::Change(int sig,
138 const Guest_sigaction* new_sa,
139 host_sa_sigaction_t claimed_host_sa_sigaction,
140 Guest_sigaction* old_sa,
141 int* error) {
142 HostStructSigaction host_sa{};
143
144 Guest_sigaction saved_new_sa{};
145 HostStructSigaction* new_host_sa = nullptr;
146 bool claim = false;
147 if (new_sa) {
148 // ATTENTION: new_sa and old_sa might point to the same object!
149 // Make a copy of new_sa so we can write to old_sa before all reads of new_sa!
150 saved_new_sa = *new_sa;
151 new_sa = &saved_new_sa;
152
153 new_host_sa = &host_sa;
154 claim = ConvertGuestSigactionToHost(new_sa, claimed_host_sa_sigaction, new_host_sa);
155 }
156
157 // Even if we only set new action for already claimed signal, we still need to call host
158 // sigaction to update kernel action mask and flags!
159 HostStructSigaction* old_host_sa = &host_sa;
160 if (!DoSigaction(sig, new_host_sa, old_host_sa, error)) {
161 return false;
162 }
163
164 if (old_sa) {
165 if (IsClaimed()) {
166 *old_sa = GetClaimedGuestAction();
167 } else {
168 ConvertHostSigactionToGuest(old_host_sa, old_sa);
169 }
170 }
171
172 if (new_sa) {
173 if (claim) {
174 Claim(new_sa);
175 } else {
176 Unclaim();
177 }
178 }
179
180 return true;
181 }
182
183 } // namespace berberis
184