]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb] Fix sig_write for null gdb_stderr
authorTom de Vries <tdevries@suse.de>
Tue, 29 Apr 2025 15:01:55 +0000 (17:01 +0200)
committerTom de Vries <tdevries@suse.de>
Tue, 29 Apr 2025 15:01:55 +0000 (17:01 +0200)
When running test-case gdb.tui/tui-layout-asm.exp with target board
dwarf5-fission-debug-types, the test-case fails and I get a core dump:
...
 # of unexpected core files      1
...

Looking at the backtrace of the core file, what seems to be happening is that:
- gdbpy_flush attempts to flush gdb_stdout, which is nullptr
- that causes a segfault
- gdb intercepts this and starts to handle it using handle_fatal_signal
- handle_fatal_signal calls sig_write, which attempts to write to gdb_stderr,
  which is nullptr,
- that causes another segfault
- gdb exits

I managed to reproduce the problem by the following trigger patch in
stdin_event_handler:
...
-  if (error)
+  if (1 || error)
     {
       current_ui = main_ui;
       ui->unregister_file_handler ();
-      if (main_ui == ui)
+      if (1 || main_ui == ui)
  {
    gdb_printf (gdb_stderr, _("error detected on stdin\n"));
+   gdb_stderr = nullptr;
+   gdb_stdout = nullptr;
+   gdb_stdlog = nullptr;
    quit_command ((char *) 0, 0);
  }
...
which gives us:
...
$ gdb
(gdb) <q>error detected on stdin
Segmentation fault (core dumped)
$ q
...

Fix sig_write to handle the case that gdb_stderr == nullptr, such that we get
instead:
...
$ gdb
(gdb) <q>error detected on stdin

Fatal signal: Segmentation fault
----- Backtrace -----
  ...
---------------------
A fatal error internal to GDB has been detected, further
debugging is not possible.  GDB will now terminate.

This is a bug, please report it.  For instructions, see:
<https://www.gnu.org/software/gdb/bugs/>.

Segmentation fault (core dumped)
$ q
...

Tested on x86_64-linux.

Approved-By: Simon Marchi <simon.marchi@efficios.com>
gdb/bt-utils.c
gdb/event-top.c

index 98726e78f545861d79107be52e13b2b0c08a2235..922402eefc0eed349caf4a63c011be46efea16cf 100644 (file)
@@ -45,7 +45,10 @@ gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
 void
 sig_write (const char *msg)
 {
-  gdb_stderr->write_async_safe (msg, strlen (msg));
+  if (gdb_stderr == nullptr || gdb_stderr->fd () == -1)
+    std::ignore = ::write (2, msg, strlen (msg));
+  else
+    gdb_stderr->write_async_safe (msg, strlen (msg));
 }
 
 #ifdef GDB_PRINT_INTERNAL_BACKTRACE
@@ -130,7 +133,10 @@ gdb_internal_backtrace_1 ()
   void *buffer[25];
   int frames = backtrace (buffer, ARRAY_SIZE (buffer));
 
-  backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ());
+  int fd = ((gdb_stderr == nullptr || gdb_stderr->fd () == -1)
+           ? 2
+           : gdb_stderr->fd ());
+  backtrace_symbols_fd (buffer, frames, fd);
   if (frames == ARRAY_SIZE (buffer))
     sig_write (_("Backtrace might be incomplete.\n"));
 }
@@ -166,10 +172,7 @@ gdb_internal_backtrace ()
 #ifdef GDB_PRINT_INTERNAL_BACKTRACE
   sig_write (str_backtrace);
 
-  if (gdb_stderr->fd () > -1)
-    gdb_internal_backtrace_1 ();
-  else
-    sig_write (str_backtrace_unavailable);
+  gdb_internal_backtrace_1 ();
 
   sig_write ("---------------------\n");
 #endif
index ad9459e3cbf058d8271ed9a3079b36288f8b6bb2..968117c171c1f957e2c29e30212e0587930dfcb6 100644 (file)
@@ -1022,7 +1022,13 @@ handle_fatal_signal (int sig)
        }
       sig_write ("\n\n");
 
-      gdb_stderr->flush ();
+      if (gdb_stderr == nullptr || gdb_stderr->fd () == -1)
+       {
+         /* Writing to file descriptor instead of stream, no flush
+            required.  */
+       }
+      else
+       gdb_stderr->flush ();
     }
 #endif