diff options
author | Tom de Vries <tdevries@suse.de> | 2025-04-29 17:01:55 +0200 |
---|---|---|
committer | Tom de Vries <tdevries@suse.de> | 2025-04-29 17:01:55 +0200 |
commit | cb8c89ba54bc5b580e2af78cc28f33d21b540423 (patch) | |
tree | 625f9e0bb22b9d2584ce27dd1c6761d035237676 | |
parent | 27ff35ce34d89687feb2584d0eb9102218ee9e74 (diff) | |
download | binutils-cb8c89ba54bc5b580e2af78cc28f33d21b540423.zip binutils-cb8c89ba54bc5b580e2af78cc28f33d21b540423.tar.gz binutils-cb8c89ba54bc5b580e2af78cc28f33d21b540423.tar.bz2 |
[gdb] Fix sig_write for null gdb_stderr
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>
-rw-r--r-- | gdb/bt-utils.c | 15 | ||||
-rw-r--r-- | gdb/event-top.c | 8 |
2 files changed, 16 insertions, 7 deletions
diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c index 98726e7..922402e 100644 --- a/gdb/bt-utils.c +++ b/gdb/bt-utils.c @@ -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 diff --git a/gdb/event-top.c b/gdb/event-top.c index ad9459e..968117c 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -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 |