• 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 // Claim a signal chain for a particular signal.
ClaimSignalChain(int signal,struct sigaction * oldaction)103 extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
104   CheckSignalValid(signal);
105   user_sigactions[signal].Claim(*oldaction);
106 }
107 
UnclaimSignalChain(int signal)108 extern "C" void UnclaimSignalChain(int signal) {
109   CheckSignalValid(signal);
110 
111   user_sigactions[signal].Unclaim(signal);
112 }
113 
114 // Invoke the user's signal handler.
InvokeUserSignalHandler(int sig,siginfo_t * info,void * context)115 extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
116   // Check the arguments.
117   CheckSignalValid(sig);
118 
119   // The signal must have been claimed in order to get here.  Check it.
120   if (!user_sigactions[sig].IsClaimed()) {
121     abort();
122   }
123 
124   const struct sigaction& action = user_sigactions[sig].GetAction();
125   if ((action.sa_flags & SA_SIGINFO) == 0) {
126     if (action.sa_handler != NULL) {
127       action.sa_handler(sig);
128     } else {
129        signal(sig, SIG_DFL);
130        raise(sig);
131     }
132   } else {
133     if (action.sa_sigaction != NULL) {
134       action.sa_sigaction(sig, info, context);
135     } else {
136        signal(sig, SIG_DFL);
137        raise(sig);
138     }
139   }
140 }
141 
EnsureFrontOfChain(int signal,struct sigaction * expected_action)142 extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
143   CheckSignalValid(signal);
144   // Read the current action without looking at the chain, it should be the expected action.
145   SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
146   struct sigaction current_action;
147   linked_sigaction(signal, nullptr, &current_action);
148   // If the sigactions don't match then we put the current action on the chain and make ourself as
149   // the main action.
150   if (current_action.sa_sigaction != expected_action->sa_sigaction) {
151     log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction);
152     user_sigactions[signal].Claim(current_action);
153     linked_sigaction(signal, expected_action, nullptr);
154   }
155 }
156 
157 // These functions are C linkage since they replace the functions in libc.
158 
sigaction(int signal,const struct sigaction * new_action,struct sigaction * old_action)159 extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
160   // If this signal has been claimed as a signal chain, record the user's
161   // action but don't pass it on to the kernel.
162   // Note that we check that the signal number is in range here.  An out of range signal
163   // number should behave exactly as the libc sigaction.
164   if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
165     struct sigaction saved_action = user_sigactions[signal].GetAction();
166     if (new_action != NULL) {
167       user_sigactions[signal].SetAction(*new_action);
168     }
169     if (old_action != NULL) {
170       *old_action = saved_action;
171     }
172     return 0;
173   }
174 
175   // Will only get here if the signal chain has not been claimed.  We want
176   // to pass the sigaction on to the kernel via the real sigaction in libc.
177 
178   if (linked_sigaction_sym == nullptr) {
179     // Perform lazy initialization.
180     // This will only occur outside of a signal context since we have
181     // not been initialized and therefore cannot be within the ART
182     // runtime.
183     InitializeSignalChain();
184   }
185 
186   if (linked_sigaction_sym == nullptr) {
187     log("Unable to find next sigaction in signal chain");
188     abort();
189   }
190   SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
191   return linked_sigaction(signal, new_action, old_action);
192 }
193 
sigprocmask(int how,const sigset_t * bionic_new_set,sigset_t * bionic_old_set)194 extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
195   const sigset_t* new_set_ptr = bionic_new_set;
196   sigset_t tmpset;
197   if (bionic_new_set != NULL) {
198     tmpset = *bionic_new_set;
199 
200     if (how == SIG_BLOCK) {
201       // Don't allow claimed signals in the mask.  If a signal chain has been claimed
202       // we can't allow the user to block that signal.
203       for (int i = 0 ; i < _NSIG; ++i) {
204         if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
205             sigdelset(&tmpset, i);
206         }
207       }
208     }
209     new_set_ptr = &tmpset;
210   }
211 
212   if (linked_sigprocmask_sym == nullptr) {
213     // Perform lazy initialization.
214     InitializeSignalChain();
215   }
216 
217   if (linked_sigprocmask_sym == nullptr) {
218     log("Unable to find next sigprocmask in signal chain");
219     abort();
220   }
221 
222   typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
223   SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
224   return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
225 }
226 
InitializeSignalChain()227 extern "C" void InitializeSignalChain() {
228   // Warning.
229   // Don't call this from within a signal context as it makes calls to
230   // dlsym.  Calling into the dynamic linker will result in locks being
231   // taken and if it so happens that a signal occurs while one of these
232   // locks is already taken, dlsym will block trying to reenter a
233   // mutex and we will never get out of it.
234   if (initialized) {
235     // Don't initialize twice.
236     return;
237   }
238   linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
239   if (linked_sigaction_sym == nullptr) {
240     linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
241     if (linked_sigaction_sym == nullptr ||
242       linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
243         linked_sigaction_sym = nullptr;
244     }
245   }
246 
247   linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
248   if (linked_sigprocmask_sym == nullptr) {
249     linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
250     if (linked_sigprocmask_sym == nullptr ||
251         linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
252          linked_sigprocmask_sym = nullptr;
253     }
254   }
255   initialized = true;
256 }
257 }   // namespace art
258 
259