blob: c5015e8d0f7c4f2686349029de331405b030dfde [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
110// Claim a signal chain for a particular signal.
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700111extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700112 CheckSignalValid(signal);
113 user_sigactions[signal].Claim(*oldaction);
114}
115
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700116extern "C" void UnclaimSignalChain(int signal) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700117 CheckSignalValid(signal);
118
119 user_sigactions[signal].Unclaim(signal);
120}
121
122// Invoke the user's signal handler.
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700123extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700124 // Check the arguments.
125 CheckSignalValid(sig);
126
127 // The signal must have been claimed in order to get here. Check it.
128 if (!user_sigactions[sig].IsClaimed()) {
129 abort();
130 }
131
132 const struct sigaction& action = user_sigactions[sig].GetAction();
Dave Allison91a83662014-08-28 16:12:40 -0700133 if (user_sigactions[sig].OldStyle()) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700134 if (action.sa_handler != NULL) {
135 action.sa_handler(sig);
Dave Allison69dfe512014-07-11 17:11:58 +0000136 } else {
137 signal(sig, SIG_DFL);
138 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700139 }
140 } else {
141 if (action.sa_sigaction != NULL) {
142 action.sa_sigaction(sig, info, context);
Dave Allison69dfe512014-07-11 17:11:58 +0000143 } else {
144 signal(sig, SIG_DFL);
145 raise(sig);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700146 }
147 }
148}
149
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700150// These functions are C linkage since they replace the functions in libc.
151
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700152extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700153 // If this signal has been claimed as a signal chain, record the user's
154 // action but don't pass it on to the kernel.
155 // Note that we check that the signal number is in range here. An out of range signal
156 // number should behave exactly as the libc sigaction.
157 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
158 if (old_action != NULL) {
159 *old_action = user_sigactions[signal].GetAction();
160 }
161 if (new_action != NULL) {
Dave Allison91a83662014-08-28 16:12:40 -0700162 user_sigactions[signal].SetAction(*new_action, false);
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700163 }
164 return 0;
165 }
166
167 // Will only get here if the signal chain has not been claimed. We want
168 // to pass the sigaction on to the kernel via the real sigaction in libc.
169
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700170 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700171 // Perform lazy initialization.
172 // This will only occur outside of a signal context since we have
173 // not been initialized and therefore cannot be within the ART
174 // runtime.
175 InitializeSignalChain();
176 }
177
178 if (linked_sigaction_sym == nullptr) {
179 log("Unable to find next sigaction in signal chain");
180 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700181 }
182
183 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
184 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
185 return linked_sigaction(signal, new_action, old_action);
186}
187
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700188extern "C" sighandler_t signal(int signal, sighandler_t handler) {
Dave Allison91a83662014-08-28 16:12:40 -0700189 struct sigaction sa;
190 sigemptyset(&sa.sa_mask);
191 sa.sa_handler = handler;
192 sa.sa_flags = SA_RESTART;
193 sighandler_t oldhandler;
194
195 // If this signal has been claimed as a signal chain, record the user's
196 // action but don't pass it on to the kernel.
197 // Note that we check that the signal number is in range here. An out of range signal
198 // number should behave exactly as the libc sigaction.
199 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
200 oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
201 user_sigactions[signal].SetAction(sa, true);
202 return oldhandler;
203 }
204
205 // Will only get here if the signal chain has not been claimed. We want
206 // to pass the sigaction on to the kernel via the real sigaction in libc.
207
Dave Allison91a83662014-08-28 16:12:40 -0700208 if (linked_sigaction_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700209 // Perform lazy initialization.
210 InitializeSignalChain();
211 }
212
213 if (linked_sigaction_sym == nullptr) {
214 log("Unable to find next sigaction in signal chain");
215 abort();
Dave Allison91a83662014-08-28 16:12:40 -0700216 }
217
218 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
219 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
220 if (linked_sigaction(signal, &sa, &sa) == -1) {
221 return SIG_ERR;
222 }
223
224 return reinterpret_cast<sighandler_t>(sa.sa_handler);
225}
226
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700227extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700228 const sigset_t* new_set_ptr = bionic_new_set;
229 sigset_t tmpset;
230 if (bionic_new_set != NULL) {
231 tmpset = *bionic_new_set;
232
233 if (how == SIG_BLOCK) {
234 // Don't allow claimed signals in the mask. If a signal chain has been claimed
235 // we can't allow the user to block that signal.
236 for (int i = 0 ; i < _NSIG; ++i) {
237 if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
238 sigdelset(&tmpset, i);
239 }
240 }
241 }
242 new_set_ptr = &tmpset;
243 }
244
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700245 if (linked_sigprocmask_sym == nullptr) {
Dave Allisoncefcea82014-09-16 10:01:01 -0700246 // Perform lazy initialization.
247 InitializeSignalChain();
248 }
249
250 if (linked_sigprocmask_sym == nullptr) {
251 log("Unable to find next sigprocmask in signal chain");
252 abort();
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700253 }
254
255 typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
256 SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
257 return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
258}
Dave Allisoncefcea82014-09-16 10:01:01 -0700259
Dmitriy Ivanovf57874d2014-10-07 13:43:23 -0700260extern "C" void InitializeSignalChain() {
Dave Allisoncefcea82014-09-16 10:01:01 -0700261 // Warning.
262 // Don't call this from within a signal context as it makes calls to
263 // dlsym. Calling into the dynamic linker will result in locks being
264 // taken and if it so happens that a signal occurs while one of these
265 // locks is already taken, dlsym will block trying to reenter a
266 // mutex and we will never get out of it.
267 if (initialized) {
268 // Don't initialize twice.
269 return;
270 }
271 linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
272 if (linked_sigaction_sym == nullptr) {
273 linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
274 if (linked_sigaction_sym == nullptr ||
275 linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
276 linked_sigaction_sym = nullptr;
277 }
278 }
279
280 linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
281 if (linked_sigprocmask_sym == nullptr) {
282 linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
283 if (linked_sigprocmask_sym == nullptr ||
284 linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
285 linked_sigprocmask_sym = nullptr;
286 }
287 }
288 initialized = true;
289}
Dave Allisonf4b80bc2014-05-14 15:41:25 -0700290} // namespace art
291