blob: 0359ed3dbb6eb859d0529f2ed2b7e8cd37f9b9c6 [file] [log] [blame]
Dave Allisonf4b80bc2014-05-14 15:41:25 -07001/*
2 * Copyright (C) 2014 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
Dave Allison69dfe512014-07-11 17:11:58 +000017#ifdef HAVE_ANDROID_OS
Dave Allisonf4b80bc2014-05-14 15:41:25 -070018#include <android/log.h>
Dave Allison69dfe512014-07-11 17:11:58 +000019#else
20#include <stdarg.h>
21#include <iostream>
22#endif
23
Dave Allisonf4b80bc2014-05-14 15:41:25 -070024#include <dlfcn.h>
25#include <signal.h>
26#include <stdio.h>
27#include <stdlib.h>
28
Dave Allisoncefcea82014-09-16 10:01:01 -070029#include "sigchain.h"
30
Dave Allison69dfe512014-07-11 17:11:58 +000031#if defined(__APPLE__)
32#define _NSIG NSIG
Dave Allison38680092014-08-29 12:29:34 -070033#define sighandler_t sig_t
Dave Allison69dfe512014-07-11 17:11:58 +000034#endif
35
Dave Allisonf4b80bc2014-05-14 15:41:25 -070036namespace art {
37
Mathieu Chartierd0004802014-10-15 16:59:47 -070038typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*);
39
Dave Allisonf4b80bc2014-05-14 15:41:25 -070040class SignalAction {
41 public:
Dave Allison91a83662014-08-28 16:12:40 -070042 SignalAction() : claimed_(false), uses_old_style_(false) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -070043 }
44
45 // Claim the signal and keep the action specified.
46 void Claim(const struct sigaction& action) {
47 action_ = action;
48 claimed_ = true;
49 }
50
51 // Unclaim the signal and restore the old action.
52 void Unclaim(int signal) {
Dave Allison1f8ef6f2014-08-20 17:38:41 -070053 claimed_ = false;
Mathieu Chartier2cebb242015-04-21 16:50:40 -070054 sigaction(signal, &action_, nullptr); // Restore old action.
Dave Allisonf4b80bc2014-05-14 15:41:25 -070055 }
56
57 // Get the action associated with this signal.
58 const struct sigaction& GetAction() const {
59 return action_;
60 }
61
62 // Is the signal claimed?
63 bool IsClaimed() const {
64 return claimed_;
65 }
66
67 // Change the recorded action to that specified.
Dave Allison91a83662014-08-28 16:12:40 -070068 // If oldstyle is true then this action is from an older style signal()
69 // call as opposed to sigaction(). In this case the sa_handler is
70 // used when invoking the user's handler.
71 void SetAction(const struct sigaction& action, bool oldstyle) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -070072 action_ = action;
Dave Allison91a83662014-08-28 16:12:40 -070073 uses_old_style_ = oldstyle;
74 }
75
76 bool OldStyle() const {
77 return uses_old_style_;
Dave Allisonf4b80bc2014-05-14 15:41:25 -070078 }
79
80 private:
81 struct sigaction action_; // Action to be performed.
82 bool claimed_; // Whether signal is claimed or not.
Dave Allison91a83662014-08-28 16:12:40 -070083 bool uses_old_style_; // Action is created using signal(). Use sa_handler.
Dave Allisonf4b80bc2014-05-14 15:41:25 -070084};
85
86// User's signal handlers
87static SignalAction user_sigactions[_NSIG];
Dave Allisoncefcea82014-09-16 10:01:01 -070088static bool initialized;
89static void* linked_sigaction_sym;
90static void* linked_sigprocmask_sym;
Dave Allisonf4b80bc2014-05-14 15:41:25 -070091
92static void log(const char* format, ...) {
93 char buf[256];
94 va_list ap;
95 va_start(ap, format);
96 vsnprintf(buf, sizeof(buf), format, ap);
Dave Allison69dfe512014-07-11 17:11:58 +000097#ifdef HAVE_ANDROID_OS
Dave Allisonf4b80bc2014-05-14 15:41:25 -070098 __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
Dave Allison69dfe512014-07-11 17:11:58 +000099#else
100 std::cout << buf << "\n";
101#endif
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700102 va_end(ap);
103}
104
105static void CheckSignalValid(int signal) {
106 if (signal <= 0 || signal >= _NSIG) {
107 log("Invalid signal %d", signal);
108 abort();
109 }
110}
111
112// Claim a signal chain for a particular signal.
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700113extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700114 CheckSignalValid(signal);
115 user_sigactions[signal].Claim(*oldaction);
116}
117
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700118extern "C" void UnclaimSignalChain(int signal) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700119 CheckSignalValid(signal);
120
121 user_sigactions[signal].Unclaim(signal);
122}
123
124// Invoke the user's signal handler.
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700125extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700126 // Check the arguments.
127 CheckSignalValid(sig);
128
129 // The signal must have been claimed in order to get here. Check it.
130 if (!user_sigactions[sig].IsClaimed()) {
131 abort();
132 }
133
134 const struct sigaction& action = user_sigactions[sig].GetAction();
Dave Allison91a83662014-08-28 16:12:40 -0700135 if (user_sigactions[sig].OldStyle()) {
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700136 if (action.sa_handler != nullptr) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700137 action.sa_handler(sig);
Dave Allison69dfe512014-07-11 17:11:58 +0000138 } else {
Dmitriy Ivanova3164b92015-04-01 11:08:45 -0700139 signal(sig, SIG_DFL);
140 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700141 }
142 } else {
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700143 if (action.sa_sigaction != nullptr) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700144 action.sa_sigaction(sig, info, context);
Dave Allison69dfe512014-07-11 17:11:58 +0000145 } else {
Dmitriy Ivanova3164b92015-04-01 11:08:45 -0700146 signal(sig, SIG_DFL);
147 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700148 }
149 }
150}
151
Mathieu Chartierd0004802014-10-15 16:59:47 -0700152extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
153 CheckSignalValid(signal);
154 // Read the current action without looking at the chain, it should be the expected action.
155 SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
156 struct sigaction current_action;
157 linked_sigaction(signal, nullptr, &current_action);
158 // If the sigactions don't match then we put the current action on the chain and make ourself as
159 // the main action.
160 if (current_action.sa_sigaction != expected_action->sa_sigaction) {
161 log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
162 user_sigactions[signal].Claim(current_action);
163 linked_sigaction(signal, expected_action, nullptr);
164 }
165}
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700166
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700167extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700168 // If this signal has been claimed as a signal chain, record the user's
169 // action but don't pass it on to the kernel.
170 // Note that we check that the signal number is in range here. An out of range signal
171 // number should behave exactly as the libc sigaction.
Dmitriy Ivanov34a0c202015-04-02 18:30:22 -0700172 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() &&
173 (new_action == nullptr || new_action->sa_handler != SIG_DFL)) {
Dmitriy Ivanovc01683b2015-01-06 14:55:26 -0800174 struct sigaction saved_action = user_sigactions[signal].GetAction();
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700175 if (new_action != nullptr) {
Dave Allison91a83662014-08-28 16:12:40 -0700176 user_sigactions[signal].SetAction(*new_action, false);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700177 }
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700178 if (old_action != nullptr) {
Dmitriy Ivanovc01683b2015-01-06 14:55:26 -0800179 *old_action = saved_action;
180 }
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700181 return 0;
182 }
183
184 // Will only get here if the signal chain has not been claimed. We want
185 // to pass the sigaction on to the kernel via the real sigaction in libc.
186
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700187 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700188 // Perform lazy initialization.
189 // This will only occur outside of a signal context since we have
190 // not been initialized and therefore cannot be within the ART
191 // runtime.
192 InitializeSignalChain();
193 }
194
195 if (linked_sigaction_sym == nullptr) {
196 log("Unable to find next sigaction in signal chain");
197 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700198 }
Mathieu Chartierd0004802014-10-15 16:59:47 -0700199 SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700200 return linked_sigaction(signal, new_action, old_action);
201}
202
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700203extern "C" sighandler_t signal(int signal, sighandler_t handler) {
Dave Allison91a83662014-08-28 16:12:40 -0700204 struct sigaction sa;
205 sigemptyset(&sa.sa_mask);
206 sa.sa_handler = handler;
207 sa.sa_flags = SA_RESTART;
208 sighandler_t oldhandler;
209
210 // If this signal has been claimed as a signal chain, record the user's
211 // action but don't pass it on to the kernel.
212 // Note that we check that the signal number is in range here. An out of range signal
213 // number should behave exactly as the libc sigaction.
Dmitriy Ivanov34a0c202015-04-02 18:30:22 -0700214 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() && handler != SIG_DFL) {
Dave Allison91a83662014-08-28 16:12:40 -0700215 oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
216 user_sigactions[signal].SetAction(sa, true);
217 return oldhandler;
218 }
219
220 // Will only get here if the signal chain has not been claimed. We want
221 // to pass the sigaction on to the kernel via the real sigaction in libc.
222
Dave Allison91a83662014-08-28 16:12:40 -0700223 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700224 // Perform lazy initialization.
225 InitializeSignalChain();
226 }
227
228 if (linked_sigaction_sym == nullptr) {
229 log("Unable to find next sigaction in signal chain");
230 abort();
Dave Allison91a83662014-08-28 16:12:40 -0700231 }
232
233 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
234 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
235 if (linked_sigaction(signal, &sa, &sa) == -1) {
236 return SIG_ERR;
237 }
238
239 return reinterpret_cast<sighandler_t>(sa.sa_handler);
240}
241
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700242extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700243 const sigset_t* new_set_ptr = bionic_new_set;
244 sigset_t tmpset;
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700245 if (bionic_new_set != nullptr) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700246 tmpset = *bionic_new_set;
247
248 if (how == SIG_BLOCK) {
249 // Don't allow claimed signals in the mask. If a signal chain has been claimed
250 // we can't allow the user to block that signal.
251 for (int i = 0 ; i < _NSIG; ++i) {
252 if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
Dmitriy Ivanova3164b92015-04-01 11:08:45 -0700253 sigdelset(&tmpset, i);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700254 }
255 }
256 }
257 new_set_ptr = &tmpset;
258 }
259
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700260 if (linked_sigprocmask_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700261 // Perform lazy initialization.
262 InitializeSignalChain();
263 }
264
265 if (linked_sigprocmask_sym == nullptr) {
266 log("Unable to find next sigprocmask in signal chain");
267 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700268 }
269
270 typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
271 SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
272 return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
273}
Dave Allisoncefcea82014-09-16 10:01:01 -0700274
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700275extern "C" void InitializeSignalChain() {
Dave Allisoncefcea82014-09-16 10:01:01 -0700276 // Warning.
277 // Don't call this from within a signal context as it makes calls to
278 // dlsym. Calling into the dynamic linker will result in locks being
279 // taken and if it so happens that a signal occurs while one of these
280 // locks is already taken, dlsym will block trying to reenter a
281 // mutex and we will never get out of it.
282 if (initialized) {
283 // Don't initialize twice.
284 return;
285 }
286 linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
287 if (linked_sigaction_sym == nullptr) {
288 linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
289 if (linked_sigaction_sym == nullptr ||
Dmitriy Ivanova3164b92015-04-01 11:08:45 -0700290 linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
291 linked_sigaction_sym = nullptr;
Dave Allisoncefcea82014-09-16 10:01:01 -0700292 }
293 }
294
295 linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
296 if (linked_sigprocmask_sym == nullptr) {
297 linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
298 if (linked_sigprocmask_sym == nullptr ||
299 linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
Dmitriy Ivanova3164b92015-04-01 11:08:45 -0700300 linked_sigprocmask_sym = nullptr;
Dave Allisoncefcea82014-09-16 10:01:01 -0700301 }
302 }
303 initialized = true;
304}
Mathieu Chartierd0004802014-10-15 16:59:47 -0700305
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700306} // namespace art
307