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