sigchain: allow use of sigprocmask inside handlers.

When we're inside a signal handler, we don't need to filter out claimed
signals from sigprocmask calls, since return from the signal handler
will restore the previous signal mask. Use the handling_signal thread
local to pass sigprocmask through when we're in a signal handler.

Bug: http://b/36205469
Bug: http://b/36262089
Change-Id: I7943e7a68c7929d9244cef83ddeb3d64243d8840
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index d9d2eaf..8509b57 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -161,18 +161,18 @@
 
 static SignalChain chains[_NSIG];
 
-class ScopedFlagSetter {
+class ScopedFlagRestorer {
  public:
-  explicit ScopedFlagSetter(bool* flag) : flag_(flag) {
-    *flag_ = true;
+  explicit ScopedFlagRestorer(bool* flag) : flag_(flag), original_value_(*flag) {
   }
 
-  ~ScopedFlagSetter() {
-    *flag_ = false;
+  ~ScopedFlagRestorer() {
+    *flag_ = original_value_;
   }
 
  private:
   bool* flag_;
+  bool original_value_;
 };
 
 class ScopedSignalUnblocker {
@@ -198,13 +198,14 @@
   sigset_t previous_mask_;
 };
 
-
 void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
+  ScopedFlagRestorer flag(&handling_signal);
+
   // Try the special handlers first.
   // If one of them crashes, we'll reenter this handler and pass that crash onto the user handler.
   if (!handling_signal) {
-    ScopedFlagSetter flag(&handling_signal);
-    ScopedSignalUnblocker unblocked { SIGABRT, SIGSEGV, SIGBUS }; // NOLINT
+    ScopedSignalUnblocker unblocked { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }; // NOLINT
+    handling_signal = true;
 
     for (const auto& handler : chains[signo].special_handlers_) {
       if (handler != nullptr && handler(signo, siginfo, ucontext_raw)) {
@@ -301,6 +302,11 @@
 #endif
 
 extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+  // When inside a signal handler, forward directly to the actual sigprocmask.
+  if (handling_signal) {
+    return linked_sigprocmask(how, bionic_new_set, bionic_old_set);
+  }
+
   const sigset_t* new_set_ptr = bionic_new_set;
   sigset_t tmpset;
   if (bionic_new_set != nullptr) {