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 #else
67 #error "Unknown host arch"
68 #endif
69 }
70 }
71
72 guest_sa->sa_flags = host_sa->sa_flags & ~SA_RESTORER;
73 ResetSigactionRestorer(guest_sa);
74 ConvertToSmallSigset(host_sa->sa_mask, &guest_sa->sa_mask);
75 }
76
ConvertGuestSigactionToHost(const Guest_sigaction * guest_sa,GuestSignalAction::host_sa_sigaction_t claimed_host_sa_sigaction,HostStructSigaction * host_sa)77 bool ConvertGuestSigactionToHost(const Guest_sigaction* guest_sa,
78 GuestSignalAction::host_sa_sigaction_t claimed_host_sa_sigaction,
79 HostStructSigaction* host_sa) {
80 bool claim = false;
81 if (guest_sa->sa_flags & SA_SIGINFO) {
82 if (guest_sa->guest_sa_sigaction == 0) {
83 // It can happen that we are requested to set SIG_DFL (= 0) _sigaction_ (not _handler_)!
84 // Don't claim and just keep host responsible for this!
85 host_sa->sa_sigaction = nullptr;
86 } else if (void* func = UnwrapHostFunction(guest_sa->guest_sa_sigaction)) {
87 host_sa->sa_sigaction = reinterpret_cast<GuestSignalAction::host_sa_sigaction_t>(func);
88 } else {
89 host_sa->sa_sigaction = claimed_host_sa_sigaction;
90 claim = true;
91 }
92 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_DFL) {
93 host_sa->sa_handler = SIG_DFL;
94 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_IGN) {
95 host_sa->sa_handler = SIG_IGN;
96 } else if (guest_sa->guest_sa_sigaction == Guest_SIG_ERR) {
97 host_sa->sa_handler = SIG_ERR;
98 } else {
99 void* func = UnwrapHostFunction(guest_sa->guest_sa_sigaction);
100 if (func) {
101 host_sa->sa_handler = reinterpret_cast<void (*)(int)>(func);
102 } else {
103 host_sa->sa_sigaction = claimed_host_sa_sigaction;
104 claim = true;
105 }
106 }
107
108 // We don't support SA_RESTORER flag for non-canonical handlers. See: b/36458045
109 if (bool(guest_sa->sa_flags & SA_RESTORER)) {
110 CheckSigactionRestorer(guest_sa);
111 }
112
113 host_sa->sa_flags = guest_sa->sa_flags & ~SA_RESTORER;
114 host_sa->sa_restorer = nullptr;
115 if (claim) {
116 host_sa->sa_flags |= SA_SIGINFO;
117 }
118
119 // ATTENTION: it might seem tempting to run claimed_host_sa_sigaction with all signals blocked.
120 // But, guest signal handler should run with current thread signal mask + guest action signal
121 // mask, and might expect certain signals to interrupt. If pending signals are disabled, then
122 // claimed_host_sa_sigaction executes guest signal handler within, so at that point signal mask
123 // should be correct. Unfortunately, if claimed_host_sa_sigaction gets invoked with all signals
124 // blocked, there seems to be no way to restore the correct signal mask before running guest
125 // signal handler.
126 ConvertToBigSigset(guest_sa->sa_mask, &host_sa->sa_mask);
127
128 return claim;
129 }
130
131 } // namespace
132
Change(int sig,const Guest_sigaction * new_sa,host_sa_sigaction_t claimed_host_sa_sigaction,Guest_sigaction * old_sa,int * error)133 bool GuestSignalAction::Change(int sig,
134 const Guest_sigaction* new_sa,
135 host_sa_sigaction_t claimed_host_sa_sigaction,
136 Guest_sigaction* old_sa,
137 int* error) {
138 HostStructSigaction host_sa{};
139
140 Guest_sigaction saved_new_sa{};
141 HostStructSigaction* new_host_sa = nullptr;
142 bool claim = false;
143 if (new_sa) {
144 // ATTENTION: new_sa and old_sa might point to the same object!
145 // Make a copy of new_sa so we can write to old_sa before all reads of new_sa!
146 saved_new_sa = *new_sa;
147 new_sa = &saved_new_sa;
148
149 new_host_sa = &host_sa;
150 claim = ConvertGuestSigactionToHost(new_sa, claimed_host_sa_sigaction, new_host_sa);
151 }
152
153 // Even if we only set new action for already claimed signal, we still need to call host
154 // sigaction to update kernel action mask and flags!
155 HostStructSigaction* old_host_sa = &host_sa;
156 if (!DoSigaction(sig, new_host_sa, old_host_sa, error)) {
157 return false;
158 }
159
160 if (old_sa) {
161 if (IsClaimed()) {
162 *old_sa = GetClaimedGuestAction();
163 } else {
164 ConvertHostSigactionToGuest(old_host_sa, old_sa);
165 }
166 }
167
168 if (new_sa) {
169 if (claim) {
170 Claim(new_sa);
171 } else {
172 Unclaim();
173 }
174 }
175
176 return true;
177 }
178
179 } // namespace berberis
180