diff options
author | Pedro Alves <pedro@palves.net> | 2021-06-17 16:16:55 +0100 |
---|---|---|
committer | Pedro Alves <pedro@palves.net> | 2021-06-17 16:22:12 +0100 |
commit | 606a431366407ca2f041206dd4708450c2edc318 (patch) | |
tree | 5633ab257fb5343875cfcddb848c2c70f2f98760 /gdbsupport | |
parent | 6a7f1c20e82c349a2d4c977a27319e895e8ca284 (diff) | |
download | gdb-606a431366407ca2f041206dd4708450c2edc318.zip gdb-606a431366407ca2f041206dd4708450c2edc318.tar.gz gdb-606a431366407ca2f041206dd4708450c2edc318.tar.bz2 |
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 <pedro@palves.net>
* 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.
Diffstat (limited to 'gdbsupport')
-rw-r--r-- | gdbsupport/ChangeLog | 9 | ||||
-rw-r--r-- | gdbsupport/scoped_ignore_signal.h | 37 |
2 files changed, 44 insertions, 2 deletions
diff --git a/gdbsupport/ChangeLog b/gdbsupport/ChangeLog index ac424f3..d054899 100644 --- a/gdbsupport/ChangeLog +++ b/gdbsupport/ChangeLog @@ -1,5 +1,14 @@ 2021-06-17 Pedro Alves <pedro@palves.net> + * 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 <pedro@palves.net> + * scoped_ignore_sigttou.h: New file, moved from gdb/ and renamed. 2021-05-17 Andrew Burgess <andrew.burgess@embecosm.com> diff --git a/gdbsupport/scoped_ignore_signal.h b/gdbsupport/scoped_ignore_signal.h index cccd390..55a921c 100644 --- a/gdbsupport/scoped_ignore_signal.h +++ b/gdbsupport/scoped_ignore_signal.h @@ -22,7 +22,10 @@ #include <signal.h> -/* 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 <int Sig> 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 |