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/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;
}