• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #ifdef HAVE_ANDROID_OS
18 #include <android/log.h>
19 #else
20 #include <stdarg.h>
21 #include <iostream>
22 #endif
23 
24 #include <dlfcn.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include "sigchain.h"
30 
31 #if defined(__APPLE__)
32 #define _NSIG NSIG
33 #endif
34 
35 namespace art {
36 
37 typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*);
38 
39 class SignalAction {
40  public:
SignalAction()41   SignalAction() : claimed_(false) {
42   }
43 
44   // Claim the signal and keep the action specified.
Claim(const struct sigaction & action)45   void Claim(const struct sigaction& action) {
46     action_ = action;
47     claimed_ = true;
48   }
49 
50   // Unclaim the signal and restore the old action.
Unclaim(int signal)51   void Unclaim(int signal) {
52     claimed_ = false;
53     sigaction(signal, &action_, NULL);        // Restore old action.
54   }
55 
56   // Get the action associated with this signal.
GetAction() const57   const struct sigaction& GetAction() const {
58     return action_;
59   }
60 
61   // Is the signal claimed?
IsClaimed() const62   bool IsClaimed() const {
63     return claimed_;
64   }
65 
66   // Change the recorded action to that specified.
SetAction(const struct sigaction & action)67   void SetAction(const struct sigaction& action) {
68     action_ = action;
69   }
70 
71  private:
72   struct sigaction action_;     // Action to be performed.
73   bool claimed_;                // Whether signal is claimed or not.
74 };
75 
76 // User's signal handlers
77 static SignalAction user_sigactions[_NSIG];
78 static bool initialized;
79 static void* linked_sigaction_sym;
80 static void* linked_sigprocmask_sym;
81 
log(const char * format,...)82 static void log(const char* format, ...) {
83   char buf[256];
84   va_list ap;
85   va_start(ap, format);
86   vsnprintf(buf, sizeof(buf), format, ap);
87 #ifdef HAVE_ANDROID_OS
88   __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
89 #else
90   std::cout << buf << "\n";
91 #endif
92   va_end(ap);
93 }
94 
CheckSignalValid(int signal)95 static void CheckSignalValid(int signal) {
96   if (signal <= 0 || signal >= _NSIG) {
97     log("Invalid signal %d", signal);
98     abort();
99   }
100 }
101 
102 
103 // Claim a signal chain for a particular signal.
ClaimSignalChain(int signal,struct sigaction * oldaction)104 void ClaimSignalChain(int signal, struct sigaction* oldaction) {
105   CheckSignalValid(signal);
106   user_sigactions[signal].Claim(*oldaction);
107 }
108 
UnclaimSignalChain(int signal)109 void UnclaimSignalChain(int signal) {
110   CheckSignalValid(signal);
111 
112   user_sigactions[signal].Unclaim(signal);
113 }
114 
115 // Invoke the user's signal handler.
InvokeUserSignalHandler(int sig,siginfo_t * info,void * context)116 void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
117   // Check the arguments.
118   CheckSignalValid(sig);
119 
120   // The signal must have been claimed in order to get here.  Check it.
121   if (!user_sigactions[sig].IsClaimed()) {
122     abort();
123   }
124 
125   const struct sigaction& action = user_sigactions[sig].GetAction();
126   if ((action.sa_flags & SA_SIGINFO) == 0) {
127     if (action.sa_handler != NULL) {
128       action.sa_handler(sig);
129     } else {
130        signal(sig, SIG_DFL);
131        raise(sig);
132     }
133   } else {
134     if (action.sa_sigaction != NULL) {
135       action.sa_sigaction(sig, info, context);
136     } else {
137        signal(sig, SIG_DFL);
138        raise(sig);
139     }
140   }
141 }
142 
EnsureFrontOfChain(int signal,struct sigaction * expected_action)143 void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
144   CheckSignalValid(signal);
145   // Read the current action without looking at the chain, it should be the expected action.
146   SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
147   struct sigaction current_action;
148   linked_sigaction(signal, nullptr, &current_action);
149   // If the sigactions don't match then we put the current action on the chain and make ourself as
150   // the main action.
151   if (current_action.sa_sigaction != expected_action->sa_sigaction) {
152     log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
153     user_sigactions[signal].Claim(current_action);
154     linked_sigaction(signal, expected_action, nullptr);
155   }
156 }
157 
158 extern "C" {
159 // These functions are C linkage since they replace the functions in libc.
160 
sigaction(int signal,const struct sigaction * new_action,struct sigaction * old_action)161 int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
162   // If this signal has been claimed as a signal chain, record the user's
163   // action but don't pass it on to the kernel.
164   // Note that we check that the signal number is in range here.  An out of range signal
165   // number should behave exactly as the libc sigaction.
166   if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
167     if (old_action != NULL) {
168       *old_action = user_sigactions[signal].GetAction();
169     }
170     if (new_action != NULL) {
171       user_sigactions[signal].SetAction(*new_action);
172     }
173     return 0;
174   }
175 
176   // Will only get here if the signal chain has not been claimed.  We want
177   // to pass the sigaction on to the kernel via the real sigaction in libc.
178 
179   if (linked_sigaction_sym == nullptr) {
180     // Perform lazy initialization.
181     // This will only occur outside of a signal context since we have
182     // not been initialized and therefore cannot be within the ART
183     // runtime.
184     InitializeSignalChain();
185   }
186 
187   if (linked_sigaction_sym == nullptr) {
188     log("Unable to find next sigaction in signal chain");
189     abort();
190   }
191   SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
192   return linked_sigaction(signal, new_action, old_action);
193 }
194 
sigprocmask(int how,const sigset_t * bionic_new_set,sigset_t * bionic_old_set)195 int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
196   const sigset_t* new_set_ptr = bionic_new_set;
197   sigset_t tmpset;
198   if (bionic_new_set != NULL) {
199     tmpset = *bionic_new_set;
200 
201     if (how == SIG_BLOCK) {
202       // Don't allow claimed signals in the mask.  If a signal chain has been claimed
203       // we can't allow the user to block that signal.
204       for (int i = 0 ; i < _NSIG; ++i) {
205         if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
206             sigdelset(&tmpset, i);
207         }
208       }
209     }
210     new_set_ptr = &tmpset;
211   }
212 
213   if (linked_sigprocmask_sym == nullptr) {
214     // Perform lazy initialization.
215     InitializeSignalChain();
216   }
217 
218   if (linked_sigprocmask_sym == nullptr) {
219     log("Unable to find next sigprocmask in signal chain");
220     abort();
221   }
222 
223   typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
224   SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
225   return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
226 }
227 }   // extern "C"
228 
InitializeSignalChain()229 void InitializeSignalChain() {
230   // Warning.
231   // Don't call this from within a signal context as it makes calls to
232   // dlsym.  Calling into the dynamic linker will result in locks being
233   // taken and if it so happens that a signal occurs while one of these
234   // locks is already taken, dlsym will block trying to reenter a
235   // mutex and we will never get out of it.
236   if (initialized) {
237     // Don't initialize twice.
238     return;
239   }
240   linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
241   if (linked_sigaction_sym == nullptr) {
242     linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
243     if (linked_sigaction_sym == nullptr ||
244       linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
245         linked_sigaction_sym = nullptr;
246     }
247   }
248 
249   linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
250   if (linked_sigprocmask_sym == nullptr) {
251     linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
252     if (linked_sigprocmask_sym == nullptr ||
253         linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
254          linked_sigprocmask_sym = nullptr;
255     }
256   }
257   initialized = true;
258 }
259 }   // namespace art
260 
261