blob: 74bfb7e9c62e6c740309cc8825538171d3664438 [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
38class SignalAction {
39 public:
Dave Allison91a83662014-08-28 16:12:40 -070040 SignalAction() : claimed_(false), uses_old_style_(false) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -070041 }
42
43 // Claim the signal and keep the action specified.
44 void Claim(const struct sigaction& action) {
45 action_ = action;
46 claimed_ = true;
47 }
48
49 // Unclaim the signal and restore the old action.
50 void Unclaim(int signal) {
Dave Allison1f8ef6f2014-08-20 17:38:41 -070051 claimed_ = false;
Dave Allison8ce6b902014-08-26 11:07:58 -070052 sigaction(signal, &action_, NULL); // Restore old action.
Dave Allisonf4b80bc2014-05-14 15:41:25 -070053 }
54
55 // Get the action associated with this signal.
56 const struct sigaction& GetAction() const {
57 return action_;
58 }
59
60 // Is the signal claimed?
61 bool IsClaimed() const {
62 return claimed_;
63 }
64
65 // Change the recorded action to that specified.
Dave Allison91a83662014-08-28 16:12:40 -070066 // If oldstyle is true then this action is from an older style signal()
67 // call as opposed to sigaction(). In this case the sa_handler is
68 // used when invoking the user's handler.
69 void SetAction(const struct sigaction& action, bool oldstyle) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -070070 action_ = action;
Dave Allison91a83662014-08-28 16:12:40 -070071 uses_old_style_ = oldstyle;
72 }
73
74 bool OldStyle() const {
75 return uses_old_style_;
Dave Allisonf4b80bc2014-05-14 15:41:25 -070076 }
77
78 private:
79 struct sigaction action_; // Action to be performed.
80 bool claimed_; // Whether signal is claimed or not.
Dave Allison91a83662014-08-28 16:12:40 -070081 bool uses_old_style_; // Action is created using signal(). Use sa_handler.
Dave Allisonf4b80bc2014-05-14 15:41:25 -070082};
83
84// User's signal handlers
85static SignalAction user_sigactions[_NSIG];
Dave Allisoncefcea82014-09-16 10:01:01 -070086static bool initialized;
87static void* linked_sigaction_sym;
88static void* linked_sigprocmask_sym;
Dave Allisonf4b80bc2014-05-14 15:41:25 -070089
90static void log(const char* format, ...) {
91 char buf[256];
92 va_list ap;
93 va_start(ap, format);
94 vsnprintf(buf, sizeof(buf), format, ap);
Dave Allison69dfe512014-07-11 17:11:58 +000095#ifdef HAVE_ANDROID_OS
Dave Allisonf4b80bc2014-05-14 15:41:25 -070096 __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
Dave Allison69dfe512014-07-11 17:11:58 +000097#else
98 std::cout << buf << "\n";
99#endif
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700100 va_end(ap);
101}
102
103static void CheckSignalValid(int signal) {
104 if (signal <= 0 || signal >= _NSIG) {
105 log("Invalid signal %d", signal);
106 abort();
107 }
108}
109
Dave Allisoncefcea82014-09-16 10:01:01 -0700110
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700111// Claim a signal chain for a particular signal.
112void ClaimSignalChain(int signal, struct sigaction* oldaction) {
113 CheckSignalValid(signal);
114 user_sigactions[signal].Claim(*oldaction);
115}
116
117void UnclaimSignalChain(int signal) {
118 CheckSignalValid(signal);
119
120 user_sigactions[signal].Unclaim(signal);
121}
122
123// Invoke the user's signal handler.
124void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
125 // Check the arguments.
126 CheckSignalValid(sig);
127
128 // The signal must have been claimed in order to get here. Check it.
129 if (!user_sigactions[sig].IsClaimed()) {
130 abort();
131 }
132
133 const struct sigaction& action = user_sigactions[sig].GetAction();
Dave Allison91a83662014-08-28 16:12:40 -0700134 if (user_sigactions[sig].OldStyle()) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700135 if (action.sa_handler != NULL) {
136 action.sa_handler(sig);
Dave Allison69dfe512014-07-11 17:11:58 +0000137 } else {
138 signal(sig, SIG_DFL);
139 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700140 }
141 } else {
142 if (action.sa_sigaction != NULL) {
143 action.sa_sigaction(sig, info, context);
Dave Allison69dfe512014-07-11 17:11:58 +0000144 } else {
145 signal(sig, SIG_DFL);
146 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700147 }
148 }
149}
150
151extern "C" {
152// These functions are C linkage since they replace the functions in libc.
153
154int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
155 // If this signal has been claimed as a signal chain, record the user's
156 // action but don't pass it on to the kernel.
157 // Note that we check that the signal number is in range here. An out of range signal
158 // number should behave exactly as the libc sigaction.
159 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
160 if (old_action != NULL) {
161 *old_action = user_sigactions[signal].GetAction();
162 }
163 if (new_action != NULL) {
Dave Allison91a83662014-08-28 16:12:40 -0700164 user_sigactions[signal].SetAction(*new_action, false);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700165 }
166 return 0;
167 }
168
169 // Will only get here if the signal chain has not been claimed. We want
170 // to pass the sigaction on to the kernel via the real sigaction in libc.
171
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700172 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700173 // Perform lazy initialization.
174 // This will only occur outside of a signal context since we have
175 // not been initialized and therefore cannot be within the ART
176 // runtime.
177 InitializeSignalChain();
178 }
179
180 if (linked_sigaction_sym == nullptr) {
181 log("Unable to find next sigaction in signal chain");
182 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700183 }
184
185 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
186 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
187 return linked_sigaction(signal, new_action, old_action);
188}
189
Dave Allison91a83662014-08-28 16:12:40 -0700190sighandler_t signal(int signal, sighandler_t handler) {
191 struct sigaction sa;
192 sigemptyset(&sa.sa_mask);
193 sa.sa_handler = handler;
194 sa.sa_flags = SA_RESTART;
195 sighandler_t oldhandler;
196
197 // If this signal has been claimed as a signal chain, record the user's
198 // action but don't pass it on to the kernel.
199 // Note that we check that the signal number is in range here. An out of range signal
200 // number should behave exactly as the libc sigaction.
201 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
202 oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
203 user_sigactions[signal].SetAction(sa, true);
204 return oldhandler;
205 }
206
207 // Will only get here if the signal chain has not been claimed. We want
208 // to pass the sigaction on to the kernel via the real sigaction in libc.
209
Dave Allison91a83662014-08-28 16:12:40 -0700210 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700211 // Perform lazy initialization.
212 InitializeSignalChain();
213 }
214
215 if (linked_sigaction_sym == nullptr) {
216 log("Unable to find next sigaction in signal chain");
217 abort();
Dave Allison91a83662014-08-28 16:12:40 -0700218 }
219
220 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
221 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
222 if (linked_sigaction(signal, &sa, &sa) == -1) {
223 return SIG_ERR;
224 }
225
226 return reinterpret_cast<sighandler_t>(sa.sa_handler);
227}
228
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700229int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
230 const sigset_t* new_set_ptr = bionic_new_set;
231 sigset_t tmpset;
232 if (bionic_new_set != NULL) {
233 tmpset = *bionic_new_set;
234
235 if (how == SIG_BLOCK) {
236 // Don't allow claimed signals in the mask. If a signal chain has been claimed
237 // we can't allow the user to block that signal.
238 for (int i = 0 ; i < _NSIG; ++i) {
239 if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
240 sigdelset(&tmpset, i);
241 }
242 }
243 }
244 new_set_ptr = &tmpset;
245 }
246
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700247 if (linked_sigprocmask_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700248 // Perform lazy initialization.
249 InitializeSignalChain();
250 }
251
252 if (linked_sigprocmask_sym == nullptr) {
253 log("Unable to find next sigprocmask in signal chain");
254 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700255 }
256
257 typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
258 SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
259 return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
260}
261} // extern "C"
Dave Allisoncefcea82014-09-16 10:01:01 -0700262
263void InitializeSignalChain() {
264 // Warning.
265 // Don't call this from within a signal context as it makes calls to
266 // dlsym. Calling into the dynamic linker will result in locks being
267 // taken and if it so happens that a signal occurs while one of these
268 // locks is already taken, dlsym will block trying to reenter a
269 // mutex and we will never get out of it.
270 if (initialized) {
271 // Don't initialize twice.
272 return;
273 }
274 linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
275 if (linked_sigaction_sym == nullptr) {
276 linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
277 if (linked_sigaction_sym == nullptr ||
278 linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
279 linked_sigaction_sym = nullptr;
280 }
281 }
282
283 linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
284 if (linked_sigprocmask_sym == nullptr) {
285 linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
286 if (linked_sigprocmask_sym == nullptr ||
287 linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
288 linked_sigprocmask_sym = nullptr;
289 }
290 }
291 initialized = true;
292}
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700293} // namespace art
294