blob: 2eb518c53141438d02d66426e1d079a4c0c7dba0 [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;
Dave Allison8ce6b902014-08-26 11:07:58 -070054 sigaction(signal, &action_, NULL); // 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()) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700136 if (action.sa_handler != NULL) {
137 action.sa_handler(sig);
Dave Allison69dfe512014-07-11 17:11:58 +0000138 } else {
139 signal(sig, SIG_DFL);
140 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700141 }
142 } else {
143 if (action.sa_sigaction != NULL) {
144 action.sa_sigaction(sig, info, context);
Dave Allison69dfe512014-07-11 17:11:58 +0000145 } else {
146 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.
172 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
Dmitriy Ivanovc01683b2015-01-06 14:55:26 -0800173 struct sigaction saved_action = user_sigactions[signal].GetAction();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700174 if (new_action != NULL) {
Dave Allison91a83662014-08-28 16:12:40 -0700175 user_sigactions[signal].SetAction(*new_action, false);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700176 }
Dmitriy Ivanovc01683b2015-01-06 14:55:26 -0800177 if (old_action != NULL) {
178 *old_action = saved_action;
179 }
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700180 return 0;
181 }
182
183 // Will only get here if the signal chain has not been claimed. We want
184 // to pass the sigaction on to the kernel via the real sigaction in libc.
185
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700186 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700187 // Perform lazy initialization.
188 // This will only occur outside of a signal context since we have
189 // not been initialized and therefore cannot be within the ART
190 // runtime.
191 InitializeSignalChain();
192 }
193
194 if (linked_sigaction_sym == nullptr) {
195 log("Unable to find next sigaction in signal chain");
196 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700197 }
Mathieu Chartierd0004802014-10-15 16:59:47 -0700198 SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700199 return linked_sigaction(signal, new_action, old_action);
200}
201
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700202extern "C" sighandler_t signal(int signal, sighandler_t handler) {
Dave Allison91a83662014-08-28 16:12:40 -0700203 struct sigaction sa;
204 sigemptyset(&sa.sa_mask);
205 sa.sa_handler = handler;
206 sa.sa_flags = SA_RESTART;
207 sighandler_t oldhandler;
208
209 // If this signal has been claimed as a signal chain, record the user's
210 // action but don't pass it on to the kernel.
211 // Note that we check that the signal number is in range here. An out of range signal
212 // number should behave exactly as the libc sigaction.
213 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
214 oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
215 user_sigactions[signal].SetAction(sa, true);
216 return oldhandler;
217 }
218
219 // Will only get here if the signal chain has not been claimed. We want
220 // to pass the sigaction on to the kernel via the real sigaction in libc.
221
Dave Allison91a83662014-08-28 16:12:40 -0700222 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700223 // Perform lazy initialization.
224 InitializeSignalChain();
225 }
226
227 if (linked_sigaction_sym == nullptr) {
228 log("Unable to find next sigaction in signal chain");
229 abort();
Dave Allison91a83662014-08-28 16:12:40 -0700230 }
231
232 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
233 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
234 if (linked_sigaction(signal, &sa, &sa) == -1) {
235 return SIG_ERR;
236 }
237
238 return reinterpret_cast<sighandler_t>(sa.sa_handler);
239}
240
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700241extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700242 const sigset_t* new_set_ptr = bionic_new_set;
243 sigset_t tmpset;
244 if (bionic_new_set != NULL) {
245 tmpset = *bionic_new_set;
246
247 if (how == SIG_BLOCK) {
248 // Don't allow claimed signals in the mask. If a signal chain has been claimed
249 // we can't allow the user to block that signal.
250 for (int i = 0 ; i < _NSIG; ++i) {
251 if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
252 sigdelset(&tmpset, i);
253 }
254 }
255 }
256 new_set_ptr = &tmpset;
257 }
258
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700259 if (linked_sigprocmask_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700260 // Perform lazy initialization.
261 InitializeSignalChain();
262 }
263
264 if (linked_sigprocmask_sym == nullptr) {
265 log("Unable to find next sigprocmask in signal chain");
266 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700267 }
268
269 typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
270 SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
271 return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
272}
Dave Allisoncefcea82014-09-16 10:01:01 -0700273
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700274extern "C" void InitializeSignalChain() {
Dave Allisoncefcea82014-09-16 10:01:01 -0700275 // Warning.
276 // Don't call this from within a signal context as it makes calls to
277 // dlsym. Calling into the dynamic linker will result in locks being
278 // taken and if it so happens that a signal occurs while one of these
279 // locks is already taken, dlsym will block trying to reenter a
280 // mutex and we will never get out of it.
281 if (initialized) {
282 // Don't initialize twice.
283 return;
284 }
285 linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
286 if (linked_sigaction_sym == nullptr) {
287 linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
288 if (linked_sigaction_sym == nullptr ||
289 linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
290 linked_sigaction_sym = nullptr;
291 }
292 }
293
294 linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
295 if (linked_sigprocmask_sym == nullptr) {
296 linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
297 if (linked_sigprocmask_sym == nullptr ||
298 linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
299 linked_sigprocmask_sym = nullptr;
300 }
301 }
302 initialized = true;
303}
Mathieu Chartierd0004802014-10-15 16:59:47 -0700304
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700305} // namespace art
306