[binutils-gdb] scoped_ignore_signal: Use sigprocmask+sigtimedwait instead of signal

Pedro Alves palves@sourceware.org
Thu Jun 17 15:22:58 GMT 2021


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=606a431366407ca2f041206dd4708450c2edc318

commit 606a431366407ca2f041206dd4708450c2edc318
Author: Pedro Alves <pedro@palves.net>
Date:   Thu Jun 17 16:16:55 2021 +0100

    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.

Diff:
---
 gdbsupport/ChangeLog              |  9 +++++++++
 gdbsupport/scoped_ignore_signal.h | 37 +++++++++++++++++++++++++++++++++++--
 2 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/gdbsupport/ChangeLog b/gdbsupport/ChangeLog
index ac424f3ae7f..d0548993333 100644
--- a/gdbsupport/ChangeLog
+++ b/gdbsupport/ChangeLog
@@ -1,3 +1,12 @@
+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.
diff --git a/gdbsupport/scoped_ignore_signal.h b/gdbsupport/scoped_ignore_signal.h
index cccd390529b..55a921cb332 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


More information about the Gdb-cvs mailing list