From: Pedro Alves Date: Thu, 17 Jun 2021 15:16:55 +0000 (+0100) Subject: scoped_ignore_signal: Use sigprocmask+sigtimedwait instead of signal X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=606a431366407ca2f041206dd4708450c2edc318;p=deliverable%2Fbinutils-gdb.git scoped_ignore_signal: Use sigprocmask+sigtimedwait instead of signal The problem with using signal(...) to temporarily ignore a signal, is that that changes the the signal disposition for the whole process. If multiple threads do it at the same time, you have a race. Fix this by using sigprocmask + sigtimedwait to implement the ignoring instead, if available, which I think probably means everywhere except Windows nowadays. This way, we only change the signal mask for the current thread, so there's no race. Change-Id: Idfe3fb08327ef8cae926f3de9ee81c56a83b1738 gdbsupport/ChangeLog: yyyy-mm-dd Pedro Alves * scoped_ignore_signal.h (scoped_ignore_signal::scoped_ignore_signal) [HAVE_SIGPROCMASK]: Use sigprocmask to block the signal instead of changing the signal disposition for the whole process. (scoped_ignore_signal::~scoped_ignore_signal) [HAVE_SIGPROCMASK]: Use sigtimedwait and sigprocmask to flush and unblock the signal. --- diff --git a/gdbsupport/ChangeLog b/gdbsupport/ChangeLog index ac424f3ae7..d054899333 100644 --- a/gdbsupport/ChangeLog +++ b/gdbsupport/ChangeLog @@ -1,3 +1,12 @@ +2021-06-17 Pedro Alves + + * scoped_ignore_signal.h + (scoped_ignore_signal::scoped_ignore_signal) + [HAVE_SIGPROCMASK]: Use sigprocmask to block the signal instead of + changing the signal disposition for the whole process. + (scoped_ignore_signal::~scoped_ignore_signal) [HAVE_SIGPROCMASK]: + Use sigtimedwait and sigprocmask to flush and unblock the signal. + 2021-06-17 Pedro Alves * scoped_ignore_sigttou.h: New file, moved from gdb/ and renamed. diff --git a/gdbsupport/scoped_ignore_signal.h b/gdbsupport/scoped_ignore_signal.h index cccd390529..55a921cb33 100644 --- a/gdbsupport/scoped_ignore_signal.h +++ b/gdbsupport/scoped_ignore_signal.h @@ -22,7 +22,10 @@ #include -/* RAII class used to ignore a signal in a scope. */ +/* RAII class used to ignore a signal in a scope. If sigprocmask is + supported, then the signal is only ignored by the calling thread. + Otherwise, the signal disposition is set to SIG_IGN, which affects + the whole process. */ template class scoped_ignore_signal @@ -30,18 +33,48 @@ class scoped_ignore_signal public: scoped_ignore_signal () { +#ifdef HAVE_SIGPROCMASK + sigset_t set, old_state; + + sigemptyset (&set); + sigaddset (&set, Sig); + sigprocmask (SIG_BLOCK, &set, &old_state); + m_was_blocked = sigismember (&old_state, Sig); +#else m_osig = signal (Sig, SIG_IGN); +#endif } ~scoped_ignore_signal () { +#ifdef HAVE_SIGPROCMASK + if (!m_was_blocked) + { + sigset_t set; + const timespec zero_timeout = {}; + + sigemptyset (&set); + sigaddset (&set, Sig); + + /* If we got a pending Sig signal, consume it before + unblocking. */ + sigtimedwait (&set, nullptr, &zero_timeout); + + sigprocmask (SIG_UNBLOCK, &set, nullptr); + } +#else signal (Sig, m_osig); +#endif } DISABLE_COPY_AND_ASSIGN (scoped_ignore_signal); private: - sighandler_t m_osig = nullptr; +#ifdef HAVE_SIGPROCMASK + bool m_was_blocked; +#else + sighandler_t m_osig; +#endif }; struct scoped_ignore_signal_nop