Skip to content

Commit 92be8bc

Browse files
gbaraldiclaude
andauthored
signal-handling: use an async-signal-safe signal-name table (#62244)
`strsignal` is not async safe because it checks your locale and calls malloc. Replace it with a table since the signal safe versions of it are gnu only and require a recent glibc (2.32) This pull request was written with the assistance of generative AI. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1d641c7 commit 92be8bc

3 files changed

Lines changed: 110 additions & 18 deletions

File tree

src/signal-handling.c

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,113 @@ static void stack_overflow_warning(void)
446446
jl_safe_printf("Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.\n");
447447
}
448448

449+
// Async-signal-safe replacement for libc strsignal(). We call this from fatal-signal
450+
// handlers, and glibc's strsignal() is not async-signal-safe: it routes through gettext
451+
// (to localize the description), which calls malloc(). If the interrupted thread already
452+
// held the malloc arena lock, that reentrant malloc() self-deadlocks. A fixed table of
453+
// string literals avoids gettext/malloc entirely and is portable across libc flavors
454+
// (musl/BSD/macOS lack glibc's sigdescr_np/sigabbrev_np). Cases are #ifdef-guarded so this
455+
// compiles wherever a given signal is (or is not) defined.
456+
static const char *jl_strsignal(int sig) JL_NOTSAFEPOINT
457+
{
458+
switch (sig) {
459+
#ifdef SIGHUP
460+
case SIGHUP: return "Hangup";
461+
#endif
462+
#ifdef SIGINT
463+
case SIGINT: return "Interrupt";
464+
#endif
465+
#ifdef SIGQUIT
466+
case SIGQUIT: return "Quit";
467+
#endif
468+
#ifdef SIGILL
469+
case SIGILL: return "Illegal instruction";
470+
#endif
471+
#ifdef SIGTRAP
472+
case SIGTRAP: return "Trace/breakpoint trap";
473+
#endif
474+
#ifdef SIGABRT
475+
case SIGABRT: return "Aborted";
476+
#endif
477+
#if defined(SIGABRT_COMPAT) && (!defined(SIGABRT) || SIGABRT_COMPAT != SIGABRT)
478+
case SIGABRT_COMPAT: return "Aborted";
479+
#endif
480+
#ifdef SIGBUS
481+
case SIGBUS: return "Bus error";
482+
#endif
483+
#ifdef SIGFPE
484+
case SIGFPE: return "Floating point exception";
485+
#endif
486+
#ifdef SIGKILL
487+
case SIGKILL: return "Killed";
488+
#endif
489+
#ifdef SIGUSR1
490+
case SIGUSR1: return "User defined signal 1";
491+
#endif
492+
#ifdef SIGSEGV
493+
case SIGSEGV: return "Segmentation fault";
494+
#endif
495+
#ifdef SIGUSR2
496+
case SIGUSR2: return "User defined signal 2";
497+
#endif
498+
#ifdef SIGPIPE
499+
case SIGPIPE: return "Broken pipe";
500+
#endif
501+
#ifdef SIGALRM
502+
case SIGALRM: return "Alarm clock";
503+
#endif
504+
#ifdef SIGTERM
505+
case SIGTERM: return "Terminated";
506+
#endif
507+
#ifdef SIGBREAK
508+
case SIGBREAK: return "Break";
509+
#endif
510+
#ifdef SIGSTKFLT
511+
case SIGSTKFLT: return "Stack fault";
512+
#endif
513+
#ifdef SIGCHLD
514+
case SIGCHLD: return "Child exited";
515+
#endif
516+
#ifdef SIGCONT
517+
case SIGCONT: return "Continued";
518+
#endif
519+
#ifdef SIGSTOP
520+
case SIGSTOP: return "Stopped (signal)";
521+
#endif
522+
#ifdef SIGTSTP
523+
case SIGTSTP: return "Stopped";
524+
#endif
525+
#ifdef SIGTTIN
526+
case SIGTTIN: return "Stopped (tty input)";
527+
#endif
528+
#ifdef SIGTTOU
529+
case SIGTTOU: return "Stopped (tty output)";
530+
#endif
531+
#ifdef SIGURG
532+
case SIGURG: return "Urgent I/O condition";
533+
#endif
534+
#ifdef SIGXCPU
535+
case SIGXCPU: return "CPU time limit exceeded";
536+
#endif
537+
#ifdef SIGXFSZ
538+
case SIGXFSZ: return "File size limit exceeded";
539+
#endif
540+
#ifdef SIGVTALRM
541+
case SIGVTALRM: return "Virtual timer expired";
542+
#endif
543+
#ifdef SIGPROF
544+
case SIGPROF: return "Profiling timer expired";
545+
#endif
546+
#ifdef SIGWINCH
547+
case SIGWINCH: return "Window changed";
548+
#endif
549+
#ifdef SIGSYS
550+
case SIGSYS: return "Bad system call";
551+
#endif
552+
default: return "Unknown signal";
553+
}
554+
}
555+
449556
#if defined(_WIN32)
450557
#include "signals-win.c"
451558
#else
@@ -636,9 +743,9 @@ void jl_fprint_critical_error(ios_t *s, int sig, int si_code, bt_context_t *cont
636743
pthread_sigmask(SIG_UNBLOCK, &sset, NULL);
637744
#endif
638745
if (si_code)
639-
jl_safe_fprintf(s, "\n[%d] signal %d (%d): %s\n", getpid(), sig, si_code, strsignal(sig));
746+
jl_safe_fprintf(s, "\n[%d] signal %d (%d): %s\n", getpid(), sig, si_code, jl_strsignal(sig));
640747
else
641-
jl_safe_fprintf(s, "\n[%d] signal %d: %s\n", getpid(), sig, strsignal(sig));
748+
jl_safe_fprintf(s, "\n[%d] signal %d: %s\n", getpid(), sig, jl_strsignal(sig));
642749
if (sig == SIGQUIT) {
643750
jl_print_task_backtraces(0);
644751
}

src/signals-unix.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ static void *signal_listener(void *arg)
12231223
jl_safe_printf("\ncmd: %s %d running %d of %d\n", jl_options.julia_bin ? jl_options.julia_bin : "julia", uv_os_getpid(), n_threads_running, nthreads);
12241224
#endif
12251225

1226-
jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig));
1226+
jl_safe_printf("\nsignal (%d): %s\n", sig, jl_strsignal(sig));
12271227
size_t i;
12281228
for (i = 0; i < signal_bt_size; i += jl_bt_entry_size(signal_bt_data + i)) {
12291229
jl_fprint_bt_entry_codeloc(ios_safe_stderr, signal_bt_data + i);

src/signals-win.c

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,6 @@ void __cdecl fpreset (void);
2727
#define _FPE_STACKUNDERFLOW 0x8b
2828
#define _FPE_EXPLICITGEN 0x8c /* raise( SIGFPE ); */
2929

30-
static char *strsignal(int sig)
31-
{
32-
switch (sig) {
33-
case SIGINT: return "SIGINT"; break;
34-
case SIGILL: return "SIGILL"; break;
35-
case SIGABRT_COMPAT: return "SIGABRT_COMPAT"; break;
36-
case SIGFPE: return "SIGFPE"; break;
37-
case SIGSEGV: return "SIGSEGV"; break;
38-
case SIGTERM: return "SIGTERM"; break;
39-
case SIGBREAK: return "SIGBREAK"; break;
40-
case SIGABRT: return "SIGABRT"; break;
41-
}
42-
return "?";
43-
}
44-
4530
static void jl_try_throw_sigint(void)
4631
{
4732
jl_task_t *ct = jl_current_task;

0 commit comments

Comments
 (0)