blob: 26e7d319cd3717f392633173d81e1fba0d07fa2c [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
17#include <android/log.h>
18#include <dlfcn.h>
19#include <signal.h>
20#include <stdio.h>
21#include <stdlib.h>
22
23namespace art {
24
25class SignalAction {
26 public:
27 SignalAction() : claimed_(false) {
28 }
29
30 // Claim the signal and keep the action specified.
31 void Claim(const struct sigaction& action) {
32 action_ = action;
33 claimed_ = true;
34 }
35
36 // Unclaim the signal and restore the old action.
37 void Unclaim(int signal) {
38 claimed_ = false;
39 sigaction(signal, &action_, NULL); // Restore old action.
40 }
41
42 // Get the action associated with this signal.
43 const struct sigaction& GetAction() const {
44 return action_;
45 }
46
47 // Is the signal claimed?
48 bool IsClaimed() const {
49 return claimed_;
50 }
51
52 // Change the recorded action to that specified.
53 void SetAction(const struct sigaction& action) {
54 action_ = action;
55 }
56
57 private:
58 struct sigaction action_; // Action to be performed.
59 bool claimed_; // Whether signal is claimed or not.
60};
61
62// User's signal handlers
63static SignalAction user_sigactions[_NSIG];
64
65static void log(const char* format, ...) {
66 char buf[256];
67 va_list ap;
68 va_start(ap, format);
69 vsnprintf(buf, sizeof(buf), format, ap);
70 __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
71 va_end(ap);
72}
73
74static void CheckSignalValid(int signal) {
75 if (signal <= 0 || signal >= _NSIG) {
76 log("Invalid signal %d", signal);
77 abort();
78 }
79}
80
81// Claim a signal chain for a particular signal.
82void ClaimSignalChain(int signal, struct sigaction* oldaction) {
83 CheckSignalValid(signal);
84 user_sigactions[signal].Claim(*oldaction);
85}
86
87void UnclaimSignalChain(int signal) {
88 CheckSignalValid(signal);
89
90 user_sigactions[signal].Unclaim(signal);
91}
92
93// Invoke the user's signal handler.
94void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
95 // Check the arguments.
96 CheckSignalValid(sig);
97
98 // The signal must have been claimed in order to get here. Check it.
99 if (!user_sigactions[sig].IsClaimed()) {
100 abort();
101 }
102
103 const struct sigaction& action = user_sigactions[sig].GetAction();
104
105 // Only deliver the signal if the signal was not masked out.
106 if (sigismember(&action.sa_mask, sig)) {
107 return;
108 }
109 if ((action.sa_flags & SA_SIGINFO) == 0) {
110 if (action.sa_handler != NULL) {
111 action.sa_handler(sig);
112 }
113 } else {
114 if (action.sa_sigaction != NULL) {
115 action.sa_sigaction(sig, info, context);
116 }
117 }
118}
119
120extern "C" {
121// These functions are C linkage since they replace the functions in libc.
122
123int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
124 // If this signal has been claimed as a signal chain, record the user's
125 // action but don't pass it on to the kernel.
126 // Note that we check that the signal number is in range here. An out of range signal
127 // number should behave exactly as the libc sigaction.
128 if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
129 if (old_action != NULL) {
130 *old_action = user_sigactions[signal].GetAction();
131 }
132 if (new_action != NULL) {
133 user_sigactions[signal].SetAction(*new_action);
134 }
135 return 0;
136 }
137
138 // Will only get here if the signal chain has not been claimed. We want
139 // to pass the sigaction on to the kernel via the real sigaction in libc.
140
141 void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
142 if (linked_sigaction_sym == nullptr) {
143 log("Unable to find next sigaction in signal chain");
144 abort();
145 }
146
147 typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
148 SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
149 return linked_sigaction(signal, new_action, old_action);
150}
151
152
153int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
154 const sigset_t* new_set_ptr = bionic_new_set;
155 sigset_t tmpset;
156 if (bionic_new_set != NULL) {
157 tmpset = *bionic_new_set;
158
159 if (how == SIG_BLOCK) {
160 // Don't allow claimed signals in the mask. If a signal chain has been claimed
161 // we can't allow the user to block that signal.
162 for (int i = 0 ; i < _NSIG; ++i) {
163 if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
164 sigdelset(&tmpset, i);
165 }
166 }
167 }
168 new_set_ptr = &tmpset;
169 }
170
171 void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
172 if (linked_sigprocmask_sym == nullptr) {
173 log("Unable to find next sigprocmask in signal chain");
174 abort();
175 }
176
177 typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
178 SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
179 return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
180}
181} // extern "C"
182} // namespace art
183