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
35namespace art {
36
37typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*);
38
39class SignalAction {
40 public:
41  SignalAction() : claimed_(false) {
42  }
43
44  // Claim the signal and keep the action specified.
45  void Claim(const struct sigaction& action) {
46    action_ = action;
47    claimed_ = true;
48  }
49
50  // Unclaim the signal and restore the old action.
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.
57  const struct sigaction& GetAction() const {
58    return action_;
59  }
60
61  // Is the signal claimed?
62  bool IsClaimed() const {
63    return claimed_;
64  }
65
66  // Change the recorded action to that specified.
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
77static SignalAction user_sigactions[_NSIG];
78static bool initialized;
79static void* linked_sigaction_sym;
80static void* linked_sigprocmask_sym;
81
82static 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
95static 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.
104void ClaimSignalChain(int signal, struct sigaction* oldaction) {
105  CheckSignalValid(signal);
106  user_sigactions[signal].Claim(*oldaction);
107}
108
109void UnclaimSignalChain(int signal) {
110  CheckSignalValid(signal);
111
112  user_sigactions[signal].Unclaim(signal);
113}
114
115// Invoke the user's signal handler.
116void 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
143void 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
158extern "C" {
159// These functions are C linkage since they replace the functions in libc.
160
161int 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
195int 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
229void 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