public inbox for gdb-testers@sourceware.org
help / color / mirror / Atom feed
From: gdb-buildbot@sergiodj.net
To: gdb-testers@sourceware.org
Subject: [binutils-gdb] Introduce thread-safe way to handle SIGSEGV
Date: Wed, 27 Nov 2019 10:30:00 -0000	[thread overview]
Message-ID: <3b3978bca2a204a772563c8e121e4a02be72e802@gdb-build> (raw)

*** TEST RESULTS FOR COMMIT 3b3978bca2a204a772563c8e121e4a02be72e802 ***

commit 3b3978bca2a204a772563c8e121e4a02be72e802
Author:     Tom Tromey <tom@tromey.com>
AuthorDate: Mon Mar 4 15:12:04 2019 -0700
Commit:     Tom Tromey <tom@tromey.com>
CommitDate: Tue Nov 26 14:02:57 2019 -0700

    Introduce thread-safe way to handle SIGSEGV
    
    The gdb demangler installs a SIGSEGV handler in order to protect gdb
    from demangler bugs.  However, this is not thread-safe, as signal
    handlers are global to the process.
    
    This patch changes gdb to always install a global SIGSEGV handler, and
    then lets threads indicate their interest in handling the signal by
    setting a thread-local variable.
    
    This patch then arranges for the demangler code to use this; being
    sure to arrange for calls to warning and the like to be done on the
    main thread.
    
    One thing I wondered while writing this patch is if there are any
    systems that do not have "sigaction".  If gdb could assume this, it
    would simplify this code.
    
    gdb/ChangeLog
    2019-11-26  Tom Tromey  <tom@tromey.com>
    
            * event-top.h (thread_local_segv_handler): Declare.
            * event-top.c (thread_local_segv_handler): New global.
            (install_handle_sigsegv, handle_sigsegv): New functions.
            (async_init_signals): Install SIGSEGV handler.
            * cp-support.c (gdb_demangle_jmp_buf): Change type.  Now
            thread-local.
            (report_failed_demangle): New function.
            (gdb_demangle): Make core_dump_allowed atomic.  Remove signal
            handler-setting code, instead use segv_handler.  Run warning code
            on main thread.
    
    Change-Id: Ic832bbb033b64744e4b44f14b41db7e4168ce427

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 80a3ea0a2a..48e7ca1ca0 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,16 @@
+2019-11-26  Tom Tromey  <tom@tromey.com>
+
+	* event-top.h (thread_local_segv_handler): Declare.
+	* event-top.c (thread_local_segv_handler): New global.
+	(install_handle_sigsegv, handle_sigsegv): New functions.
+	(async_init_signals): Install SIGSEGV handler.
+	* cp-support.c (gdb_demangle_jmp_buf): Change type.  Now
+	thread-local.
+	(report_failed_demangle): New function.
+	(gdb_demangle): Make core_dump_allowed atomic.  Remove signal
+	handler-setting code, instead use segv_handler.  Run warning code
+	on main thread.
+
 2019-11-26  Tom Tromey  <tom@tromey.com>
 
 	* run-on-main-thread.c: New file.
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index f1ddc74976..4de2a98e87 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -38,6 +38,9 @@
 #include "safe-ctype.h"
 #include "gdbsupport/selftest.h"
 #include "gdbsupport/gdb-sigmask.h"
+#include <atomic>
+#include "event-top.h"
+#include "run-on-main-thread.h"
 
 #define d_left(dc) (dc)->u.s_binary.left
 #define d_right(dc) (dc)->u.s_binary.right
@@ -1476,11 +1479,11 @@ static bool catch_demangler_crashes = true;
 
 /* Stack context and environment for demangler crash recovery.  */
 
-static SIGJMP_BUF gdb_demangle_jmp_buf;
+static thread_local SIGJMP_BUF *gdb_demangle_jmp_buf;
 
-/* If nonzero, attempt to dump core from the signal handler.  */
+/* If true, attempt to dump core from the signal handler.  */
 
-static int gdb_demangle_attempt_core_dump = 1;
+static std::atomic<bool> gdb_demangle_attempt_core_dump;
 
 /* Signal handler for gdb_demangle.  */
 
@@ -1492,10 +1495,46 @@ gdb_demangle_signal_handler (int signo)
       if (fork () == 0)
 	dump_core ();
 
-      gdb_demangle_attempt_core_dump = 0;
+      gdb_demangle_attempt_core_dump = false;
     }
 
-  SIGLONGJMP (gdb_demangle_jmp_buf, signo);
+  SIGLONGJMP (*gdb_demangle_jmp_buf, signo);
+}
+
+/* A helper for gdb_demangle that reports a demangling failure.  */
+
+static void
+report_failed_demangle (const char *name, bool core_dump_allowed,
+			int crash_signal)
+{
+  static bool error_reported = false;
+
+  if (!error_reported)
+    {
+      std::string short_msg
+	= string_printf (_("unable to demangle '%s' "
+			   "(demangler failed with signal %d)"),
+			 name, crash_signal);
+
+      std::string long_msg
+	= string_printf ("%s:%d: %s: %s", __FILE__, __LINE__,
+			 "demangler-warning", short_msg.c_str ());
+
+      target_terminal::scoped_restore_terminal_state term_state;
+      target_terminal::ours_for_output ();
+
+      begin_line ();
+      if (core_dump_allowed)
+	fprintf_unfiltered (gdb_stderr,
+			    _("%s\nAttempting to dump core.\n"),
+			    long_msg.c_str ());
+      else
+	warn_cant_dump_core (long_msg.c_str ());
+
+      demangler_warning (__FILE__, __LINE__, "%s", short_msg.c_str ());
+
+      error_reported = true;
+    }
 }
 
 #endif
@@ -1509,45 +1548,27 @@ gdb_demangle (const char *name, int options)
   int crash_signal = 0;
 
 #ifdef HAVE_WORKING_FORK
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
-  struct sigaction sa, old_sa;
-#else
-  sighandler_t ofunc;
-#endif
-  static int core_dump_allowed = -1;
-
-  if (core_dump_allowed == -1)
-    {
-      core_dump_allowed = can_dump_core (LIMIT_CUR);
-
-      if (!core_dump_allowed)
-	gdb_demangle_attempt_core_dump = 0;
-    }
-
+  scoped_restore restore_segv
+    = make_scoped_restore (&thread_local_segv_handler,
+			   catch_demangler_crashes
+			   ? gdb_demangle_signal_handler
+			   : nullptr);
+
+  bool core_dump_allowed = gdb_demangle_attempt_core_dump;
+  SIGJMP_BUF jmp_buf;
+  scoped_restore restore_jmp_buf
+    = make_scoped_restore (&gdb_demangle_jmp_buf, &jmp_buf);
   if (catch_demangler_crashes)
     {
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
-      sa.sa_handler = gdb_demangle_signal_handler;
-      sigemptyset (&sa.sa_mask);
-#ifdef HAVE_SIGALTSTACK
-      sa.sa_flags = SA_ONSTACK;
-#else
-      sa.sa_flags = 0;
-#endif
-      sigaction (SIGSEGV, &sa, &old_sa);
-#else
-      ofunc = signal (SIGSEGV, gdb_demangle_signal_handler);
-#endif
-
       /* The signal handler may keep the signal blocked when we longjmp out
          of it.  If we have sigprocmask, we can use it to unblock the signal
 	 afterwards and we can avoid the performance overhead of saving the
 	 signal mask just in case the signal gets triggered.  Otherwise, just
 	 tell sigsetjmp to save the mask.  */
 #ifdef HAVE_SIGPROCMASK
-      crash_signal = SIGSETJMP (gdb_demangle_jmp_buf, 0);
+      crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 0);
 #else
-      crash_signal = SIGSETJMP (gdb_demangle_jmp_buf, 1);
+      crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 1);
 #endif
     }
 #endif
@@ -1558,16 +1579,8 @@ gdb_demangle (const char *name, int options)
 #ifdef HAVE_WORKING_FORK
   if (catch_demangler_crashes)
     {
-#if defined (HAVE_SIGACTION) && defined (SA_RESTART)
-      sigaction (SIGSEGV, &old_sa, NULL);
-#else
-      signal (SIGSEGV, ofunc);
-#endif
-
       if (crash_signal != 0)
-	{
-	  static int error_reported = 0;
-
+        {
 #ifdef HAVE_SIGPROCMASK
 	  /* If we got the signal, SIGSEGV may still be blocked; restore it.  */
 	  sigset_t segv_sig_set;
@@ -1576,35 +1589,18 @@ gdb_demangle (const char *name, int options)
 	  gdb_sigmask (SIG_UNBLOCK, &segv_sig_set, NULL);
 #endif
 
-	  if (!error_reported)
-	    {
-	      std::string short_msg
-		= string_printf (_("unable to demangle '%s' "
-				   "(demangler failed with signal %d)"),
-				 name, crash_signal);
-
-	      std::string long_msg
-		= string_printf ("%s:%d: %s: %s", __FILE__, __LINE__,
-				 "demangler-warning", short_msg.c_str ());
-
-	      target_terminal::scoped_restore_terminal_state term_state;
-	      target_terminal::ours_for_output ();
-
-	      begin_line ();
-	      if (core_dump_allowed)
-		fprintf_unfiltered (gdb_stderr,
-				    _("%s\nAttempting to dump core.\n"),
-				    long_msg.c_str ());
-	      else
-		warn_cant_dump_core (long_msg.c_str ());
-
-	      demangler_warning (__FILE__, __LINE__, "%s", short_msg.c_str ());
-
-	      error_reported = 1;
-	    }
-
-	  result = NULL;
-	}
+	  /* If there was a failure, we can't report it here, because
+	     we might be in a background thread.  Instead, arrange for
+	     the reporting to happen on the main thread.  */
+          std::string copy = name;
+          run_on_main_thread ([=] ()
+            {
+              report_failed_demangle (copy.c_str (), core_dump_allowed,
+                                      crash_signal);
+            });
+
+          result = NULL;
+        }
     }
 #endif
 
@@ -2211,4 +2207,6 @@ display the offending symbol."),
   selftests::register_test ("cp_remove_params",
 			    selftests::test_cp_remove_params);
 #endif
+
+  gdb_demangle_attempt_core_dump = can_dump_core (LIMIT_CUR);
 }
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 6c6e0ff3ba..9ec599e8c4 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -848,6 +848,45 @@ gdb_readline_no_editing_callback (gdb_client_data client_data)
 }
 \f
 
+/* See event-top.h.  */
+
+thread_local void (*thread_local_segv_handler) (int);
+
+static void handle_sigsegv (int sig);
+
+/* Install the SIGSEGV handler.  */
+static void
+install_handle_sigsegv ()
+{
+#if defined (HAVE_SIGACTION)
+  struct sigaction sa;
+  sa.sa_handler = handle_sigsegv;
+  sigemptyset (&sa.sa_mask);
+#ifdef HAVE_SIGALTSTACK
+  sa.sa_flags = SA_ONSTACK;
+#else
+  sa.sa_flags = 0;
+#endif
+  sigaction (SIGSEGV, &sa, nullptr);
+#else
+  signal (SIGSEGV, handle_sigsegv);
+#endif
+}
+
+/* Handler for SIGSEGV.  */
+
+static void
+handle_sigsegv (int sig)
+{
+  install_handle_sigsegv ();
+
+  if (thread_local_segv_handler == nullptr)
+    abort ();
+  thread_local_segv_handler (sig);
+}
+
+\f
+
 /* The serial event associated with the QUIT flag.  set_quit_flag sets
    this, and check_quit_flag clears it.  Used by interruptible_select
    to be able to do interruptible I/O with no race with the SIGINT
@@ -915,6 +954,8 @@ async_init_signals (void)
   sigtstp_token =
     create_async_signal_handler (async_sigtstp_handler, NULL);
 #endif
+
+  install_handle_sigsegv ();
 }
 
 /* See defs.h.  */
diff --git a/gdb/event-top.h b/gdb/event-top.h
index 1dc7b13d4f..e844125cbb 100644
--- a/gdb/event-top.h
+++ b/gdb/event-top.h
@@ -70,4 +70,10 @@ extern void gdb_rl_callback_handler_install (const char *prompt);
    currently installed.  */
 extern void gdb_rl_callback_handler_reinstall (void);
 
+/* The SIGSEGV handler for this thread, or NULL if there is none.  GDB
+   always installs a global SIGSEGV handler, and then lets threads
+   indicate their interest in handling the signal by setting this
+   thread-local variable.  */
+extern thread_local void (*thread_local_segv_handler) (int);
+
 #endif


             reply	other threads:[~2019-11-27 10:30 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-27 10:30 gdb-buildbot [this message]
2019-11-27 10:30 ` Failures on Ubuntu-Aarch64-native-gdbserver-m64, branch master gdb-buildbot
2019-11-30  2:09 ` Failures on Fedora-i686, " gdb-buildbot
2019-11-30  2:25 ` Failures on Fedora-x86_64-m32, " gdb-buildbot
2019-11-30  2:49 ` Failures on Fedora-x86_64-native-extended-gdbserver-m32, " gdb-buildbot
2019-11-30  2:52 ` *** COMPILATION FAILED *** Failures on Fedora-x86_64-w64-mingw32, branch master *** BREAKAGE *** gdb-buildbot
2019-11-30  2:58 ` Failures on Fedora-x86_64-native-extended-gdbserver-m64, branch master gdb-buildbot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3b3978bca2a204a772563c8e121e4a02be72e802@gdb-build \
    --to=gdb-buildbot@sergiodj.net \
    --cc=gdb-testers@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).