Fix signal unsafe call inside a signal

It can easily happen that the signal handler function
`handle_fatal_signal` uses various signal unsafe functions.
The problematic functions are `_` and `strsignal` which
can be pre-computed after the `setlocale` call is done.

Unfortunately when compiled with --disable-libbacktrace a
different code path is used, that calls the glibc function
`backtrace` which calls `malloc` and `free` and is therefore
also signal unsafe, that is probably unfixable, so there
is no attempt to fix anything in this code path.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31713#c9
This commit is contained in:
Bernd Edlinger 2024-05-20 07:12:30 +02:00
parent 51527eb809
commit 7ffd4868a7
4 changed files with 100 additions and 9 deletions

View File

@ -145,10 +145,25 @@ gdb_internal_backtrace_1 ()
#else
#error "unexpected internal backtrace policy"
#endif
static const char *str_backtrace = "----- Backtrace -----\n";
static const char *str_backtrace_unavailable = "Backtrace unavailable\n";
#endif /* GDB_PRINT_INTERNAL_BACKTRACE */
/* See bt-utils.h. */
void
gdb_internal_backtrace_init_str ()
{
#ifdef GDB_PRINT_INTERNAL_BACKTRACE
str_backtrace = _("----- Backtrace -----\n");
str_backtrace_unavailable = _("Backtrace unavailable\n");
#endif
}
/* See bt-utils.h. */
void
gdb_internal_backtrace ()
{
@ -161,12 +176,12 @@ gdb_internal_backtrace ()
gdb_stderr->write_async_safe (msg, strlen (msg));
};
sig_write (_("----- Backtrace -----\n"));
sig_write (str_backtrace);
if (gdb_stderr->fd () > -1)
gdb_internal_backtrace_1 ();
else
sig_write (_("Backtrace unavailable\n"));
sig_write (str_backtrace_unavailable);
sig_write ("---------------------\n");
#endif

View File

@ -71,4 +71,8 @@ extern void gdb_internal_backtrace ();
extern void gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
cmd_list_element *c);
/* Initialize language specific strings. */
extern void gdb_internal_backtrace_init_str ();
#endif /* BT_UTILS_H */

View File

@ -908,6 +908,51 @@ unblock_signal (int sig)
return false;
}
/* Signal safe language specific strings. */
#ifdef GDB_PRINT_INTERNAL_BACKTRACE
static const char *str_fatal_signal;
static const char *str_sigsegv;
#ifdef SIGFPE
static const char *str_sigfpe;
#endif
#ifdef SIGBUS
static const char *str_sigbus;
#endif
#ifdef SIGABRT
static const char *str_sigabrt;
#endif
static const char *str_unknown_signal;
static const char *str_fatal_error_detected_gdb_will_now_terminate;
static const char *str_this_is_a_bug;
static const char *str_for_instructions_see;
/* Initialize language specific strings. */
static void
init_str_handle_fatal_signal ()
{
str_fatal_signal = _("Fatal signal: ");
str_sigsegv = strsignal (SIGSEGV);
#ifdef SIGFPE
str_sigfpe = strsignal (SIGFPE);
#endif
#ifdef SIGBUS
str_sigbus = strsignal (SIGBUS);
#endif
#ifdef SIGABRT
str_sigabrt = strsignal (SIGABRT);
#endif
str_unknown_signal = _("Unknown signal");
str_fatal_error_detected_gdb_will_now_terminate =
_("A fatal error internal to GDB has been detected, "
"further\ndebugging is not possible. GDB will now "
"terminate.\n\n");
str_this_is_a_bug = _("This is a bug, please report it.");
str_for_instructions_see = _(" For instructions, see:\n");
}
#endif
/* Called to handle fatal signals. SIG is the signal number. */
[[noreturn]] static void
@ -926,19 +971,40 @@ handle_fatal_signal (int sig)
if (bt_on_fatal_signal)
{
sig_write ("\n\n");
sig_write (_("Fatal signal: "));
sig_write (strsignal (sig));
sig_write (str_fatal_signal);
switch (sig)
{
case SIGSEGV:
sig_write (str_sigsegv);
break;
#ifdef SIGFPE
case SIGFPE:
sig_write (str_sigfpe);
break;
#endif
#ifdef SIGBUS
case SIGBUS:
sig_write (str_sigbus);
break;
#endif
#ifdef SIGABRT
case SIGABRT:
sig_write (str_sigabrt);
break;
#endif
default:
sig_write (str_unknown_signal);
break;
}
sig_write ("\n");
gdb_internal_backtrace ();
sig_write (_("A fatal error internal to GDB has been detected, "
"further\ndebugging is not possible. GDB will now "
"terminate.\n\n"));
sig_write (_("This is a bug, please report it."));
sig_write (str_fatal_error_detected_gdb_will_now_terminate);
sig_write (str_this_is_a_bug);
if (REPORT_BUGS_TO[0] != '\0')
{
sig_write (_(" For instructions, see:\n"));
sig_write (str_for_instructions_see);
sig_write (REPORT_BUGS_TO);
sig_write (".");
}
@ -1066,6 +1132,10 @@ gdb_init_signals (void)
create_async_signal_handler (async_sigtstp_handler, NULL, "sigtstp");
#endif
#ifdef GDB_PRINT_INTERNAL_BACKTRACE
init_str_handle_fatal_signal ();
#endif
#ifdef SIGFPE
signal (SIGFPE, handle_fatal_signal);
#endif

View File

@ -58,6 +58,7 @@
#include "observable.h"
#include "serial.h"
#include "cli-out.h"
#include "bt-utils.h"
/* The selected interpreter. */
std::string interpreter_p;
@ -676,6 +677,7 @@ captured_main_1 (struct captured_main_args *context)
/* Note: `error' cannot be called before this point, because the
caller will crash when trying to print the exception. */
main_ui = new ui (stdin, stdout, stderr);
gdb_internal_backtrace_init_str ();
current_ui = main_ui;
gdb_stdtarg = gdb_stderr;