Block TIMER_SIGNAL in sigprocmask(SIG_SETMASK, ...).

Previously, we were zeroing out the reserved signals, when we actually
wanted to have TIMER_SIGNAL always be blocked, and the other signals
always be unblocked. This resulted in process termination when a
SIGEV_THREAD timer callback calls sigprocmask(SIG_SETMASK, ...) with
any signal mask value, and then subsequently fails to complete its
callback and reach the sigtimedwait in bionic before the next timer
iteration triggers.

Add a how argument to filter_reserved_signals to appropriately
block/unblock our reserved signals.

Bug: http://b/116783733
Test: bionic-unit-tests32/64
Change-Id: Ie5339682cdeb914711cd4089cd26ee395704d0df
diff --git a/libc/bionic/poll.cpp b/libc/bionic/poll.cpp
index 3df8b18..41b2657 100644
--- a/libc/bionic/poll.cpp
+++ b/libc/bionic/poll.cpp
@@ -71,7 +71,7 @@
   sigset64_t mutable_ss;
   sigset64_t* mutable_ss_ptr = nullptr;
   if (ss != nullptr) {
-    mutable_ss = filter_reserved_signals(*ss);
+    mutable_ss = filter_reserved_signals(*ss, SIG_SETMASK);
     mutable_ss_ptr = &mutable_ss;
   }
 
@@ -121,7 +121,7 @@
   sigset64_t mutable_ss;
   sigset64_t* mutable_ss_ptr = nullptr;
   if (ss != nullptr) {
-    mutable_ss = filter_reserved_signals(*ss);
+    mutable_ss = filter_reserved_signals(*ss, SIG_SETMASK);
     mutable_ss_ptr = &mutable_ss;
   }
 
diff --git a/libc/bionic/sigaction.cpp b/libc/bionic/sigaction.cpp
index fb57d1c..42dcccd 100644
--- a/libc/bionic/sigaction.cpp
+++ b/libc/bionic/sigaction.cpp
@@ -43,7 +43,7 @@
   if (bionic_new_action != nullptr) {
     kernel_new_action.sa_flags = bionic_new_action->sa_flags;
     kernel_new_action.sa_handler = bionic_new_action->sa_handler;
-    kernel_new_action.sa_mask = filter_reserved_signals(bionic_new_action->sa_mask);
+    kernel_new_action.sa_mask = filter_reserved_signals(bionic_new_action->sa_mask, SIG_SETMASK);
 #if defined(SA_RESTORER)
     kernel_new_action.sa_restorer = bionic_new_action->sa_restorer;
 #if defined(__aarch64__)
@@ -122,7 +122,7 @@
       kernel_new.sa_restorer = (kernel_new.sa_flags & SA_SIGINFO) ? &__restore_rt : &__restore;
     }
 #endif
-    kernel_new.sa_mask = filter_reserved_signals(kernel_new.sa_mask);
+    kernel_new.sa_mask = filter_reserved_signals(kernel_new.sa_mask, SIG_SETMASK);
   }
 
   return __rt_sigaction(signal,
diff --git a/libc/bionic/signal.cpp b/libc/bionic/signal.cpp
index 175182b..d6be09a 100644
--- a/libc/bionic/signal.cpp
+++ b/libc/bionic/signal.cpp
@@ -263,7 +263,7 @@
   sigset64_t mutable_set;
   sigset64_t* mutable_set_ptr = nullptr;
   if (set) {
-    mutable_set = filter_reserved_signals(*set);
+    mutable_set = filter_reserved_signals(*set, SIG_SETMASK);
     mutable_set_ptr = &mutable_set;
   }
   return __rt_sigsuspend(mutable_set_ptr, sizeof(*set));
@@ -279,7 +279,7 @@
   sigset64_t mutable_set;
   sigset64_t* mutable_set_ptr = nullptr;
   if (set) {
-    mutable_set = filter_reserved_signals(*set);
+    mutable_set = filter_reserved_signals(*set, SIG_SETMASK);
     mutable_set_ptr = &mutable_set;
   }
   return __rt_sigtimedwait(mutable_set_ptr, info, timeout, sizeof(*set));
diff --git a/libc/bionic/sigprocmask.cpp b/libc/bionic/sigprocmask.cpp
index 36866f3..5f70f32 100644
--- a/libc/bionic/sigprocmask.cpp
+++ b/libc/bionic/sigprocmask.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <errno.h>
 #include <signal.h>
 
 #include "private/sigrtmin.h"
@@ -65,10 +66,16 @@
 int sigprocmask64(int how,
                   const sigset64_t* new_set,
                   sigset64_t* old_set) __attribute__((__noinline__)) {
+  // how is only checked for validity if new_set is provided.
+  if (new_set && how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) {
+    errno = EINVAL;
+    return -1;
+  }
+
   sigset64_t mutable_new_set;
   sigset64_t* mutable_new_set_ptr = nullptr;
   if (new_set) {
-    mutable_new_set = filter_reserved_signals(*new_set);
+    mutable_new_set = filter_reserved_signals(*new_set, how);
     mutable_new_set_ptr = &mutable_new_set;
   }
   return __rt_sigprocmask(how, mutable_new_set_ptr, old_set, sizeof(*new_set));
diff --git a/libc/private/sigrtmin.h b/libc/private/sigrtmin.h
index ea8673d..431a1dd 100644
--- a/libc/private/sigrtmin.h
+++ b/libc/private/sigrtmin.h
@@ -32,6 +32,8 @@
 
 #include <signal.h>
 
+#include "bionic_macros.h"
+
 // Realtime signals reserved for internal use:
 //   32 (__SIGRTMIN + 0)        POSIX timers
 //   33 (__SIGRTMIN + 1)        libbacktrace
@@ -42,9 +44,29 @@
 // in <android/legacy_signal_inlines.h> to match.
 
 #define __SIGRT_RESERVED 4
-static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset) {
-  for (int signo = __SIGRTMIN; signo < __SIGRTMIN + __SIGRT_RESERVED; ++signo) {
-    sigdelset64(&sigset, signo);
+static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset, int how) {
+  int (*block)(sigset64_t*, int);
+  int (*unblock)(sigset64_t*, int);
+  switch (how) {
+    case SIG_BLOCK:
+      __BIONIC_FALLTHROUGH;
+    case SIG_SETMASK:
+      block = sigaddset64;
+      unblock = sigdelset64;
+      break;
+
+    case SIG_UNBLOCK:
+      block = sigdelset64;
+      unblock = sigaddset64;
+      break;
   }
+
+  // The POSIX timer signal must be blocked.
+  block(&sigset, __SIGRTMIN + 0);
+
+  // Everything else must remain unblocked.
+  unblock(&sigset, __SIGRTMIN + 1);
+  unblock(&sigset, __SIGRTMIN + 2);
+  unblock(&sigset, __SIGRTMIN + 3);
   return sigset;
 }