Skip to content

Commit 606a431

Browse files
committed
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 <[email protected]> * 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.
1 parent 6a7f1c2 commit 606a431

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

gdbsupport/ChangeLog

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2021-06-17 Pedro Alves <[email protected]>
2+
3+
* scoped_ignore_signal.h
4+
(scoped_ignore_signal::scoped_ignore_signal)
5+
[HAVE_SIGPROCMASK]: Use sigprocmask to block the signal instead of
6+
changing the signal disposition for the whole process.
7+
(scoped_ignore_signal::~scoped_ignore_signal) [HAVE_SIGPROCMASK]:
8+
Use sigtimedwait and sigprocmask to flush and unblock the signal.
9+
110
2021-06-17 Pedro Alves <[email protected]>
211

312
* scoped_ignore_sigttou.h: New file, moved from gdb/ and renamed.

gdbsupport/scoped_ignore_signal.h

+35-2
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,59 @@
2222

2323
#include <signal.h>
2424

25-
/* RAII class used to ignore a signal in a scope. */
25+
/* RAII class used to ignore a signal in a scope. If sigprocmask is
26+
supported, then the signal is only ignored by the calling thread.
27+
Otherwise, the signal disposition is set to SIG_IGN, which affects
28+
the whole process. */
2629

2730
template <int Sig>
2831
class scoped_ignore_signal
2932
{
3033
public:
3134
scoped_ignore_signal ()
3235
{
36+
#ifdef HAVE_SIGPROCMASK
37+
sigset_t set, old_state;
38+
39+
sigemptyset (&set);
40+
sigaddset (&set, Sig);
41+
sigprocmask (SIG_BLOCK, &set, &old_state);
42+
m_was_blocked = sigismember (&old_state, Sig);
43+
#else
3344
m_osig = signal (Sig, SIG_IGN);
45+
#endif
3446
}
3547

3648
~scoped_ignore_signal ()
3749
{
50+
#ifdef HAVE_SIGPROCMASK
51+
if (!m_was_blocked)
52+
{
53+
sigset_t set;
54+
const timespec zero_timeout = {};
55+
56+
sigemptyset (&set);
57+
sigaddset (&set, Sig);
58+
59+
/* If we got a pending Sig signal, consume it before
60+
unblocking. */
61+
sigtimedwait (&set, nullptr, &zero_timeout);
62+
63+
sigprocmask (SIG_UNBLOCK, &set, nullptr);
64+
}
65+
#else
3866
signal (Sig, m_osig);
67+
#endif
3968
}
4069

4170
DISABLE_COPY_AND_ASSIGN (scoped_ignore_signal);
4271

4372
private:
44-
sighandler_t m_osig = nullptr;
73+
#ifdef HAVE_SIGPROCMASK
74+
bool m_was_blocked;
75+
#else
76+
sighandler_t m_osig;
77+
#endif
4578
};
4679

4780
struct scoped_ignore_signal_nop

0 commit comments

Comments
 (0)