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) {