From 2376f699fcc15bff11541d6f6f1c7eee32b48255 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 14 Sep 2023 17:13:21 -0300 Subject: [PATCH] Avoid infinite loop when doing SIGTRAP in arm64-apple (#51284) The guard instruction for unreachables and other crashes in aarch64 is `brk`, in macos there isn't a distinction between a brk for a breakpoint and one for a crash, as an attempt we check the value of `pc` when the signal is triggered, if it is `brk #0x1` we say that it is a crash and go into the sigdie_handler. We should probably do the same in aarch64 linux, though I haven't dug too deep into what values it uses for traps, and if what compiler used matters, on apple I assumed we use clang/LLVM It might be possible to test this by calling some inline assembly. This means that something like https://github.com/JuliaLang/julia/issues/51267 actually crashes with ```c [16908] signal (5): Trace/BPT trap: 5 in expression starting at /Users/gabrielbaraldi/julia/test.jl:2 _collect at ./array.jl:768 collect at ./array.jl:757 top-level scope at /Users/gabrielbaraldi/julia/test.jl:5 _jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2892 jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:925 jl_toplevel_eval_flex at /Users/gabrielbaraldi/julia/src/toplevel.c:877 ijl_toplevel_eval at /Users/gabrielbaraldi/julia/src/toplevel.c:943 [inlined] ijl_toplevel_eval_in at /Users/gabrielbaraldi/julia/src/toplevel.c:985 eval at ./boot.jl:383 [inlined] include_string at ./loading.jl:2070 _jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873 ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074 _include at ./loading.jl:2130 include at ./Base.jl:494 jfptr_include_46486 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line) _jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873 ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074 exec_options at ./client.jl:317 _start at ./client.jl:552 jfptr__start_83179 at /Users/gabrielbaraldi/julia/usr/lib/julia/sys.dylib (unknown line) _jl_invoke at /Users/gabrielbaraldi/julia/src/gf.c:2873 ijl_apply_generic at /Users/gabrielbaraldi/julia/src/gf.c:3074 jl_apply at /Users/gabrielbaraldi/julia/src/./julia.h:1970 [inlined] true_main at /Users/gabrielbaraldi/julia/src/jlapi.c:582 jl_repl_entrypoint at /Users/gabrielbaraldi/julia/src/jlapi.c:731 Allocations: 570978 (Pool: 570031; Big: 947); GC: 1 fish: Job 1, './julia test.jl' terminated by signal SIGTRAP (Trace or breakpoint trap) ``` instead of hanging silently --------- Co-authored-by: Jameson Nash (cherry picked from commit d51ad06f664b3439b4aee51b5cd5edd6b9d53c69) --- src/signals-unix.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/signals-unix.c b/src/signals-unix.c index c1947b4b4dce4..8d02aa96a8586 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -1033,6 +1033,19 @@ static void sigint_handler(int sig) jl_sigint_passed = 1; } +#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) +static void sigtrap_handler(int sig, siginfo_t *info, void *context) +{ + uintptr_t pc = ((ucontext_t*)context)->uc_mcontext->__ss.__pc; // TODO: Do this in linux as well + uint32_t* code = (uint32_t*)(pc); // https://gcc.gnu.org/legacy-ml/gcc-patches/2013-11/msg02228.html + if (*code == 0xd4200020) { // brk #0x1 which is what LLVM defines as trap + signal(sig, SIG_DFL); + sig = SIGILL; // redefine this as as an "unreachable reached" error message + sigdie_handler(sig, info, context); + } +} +#endif + void jl_install_default_signal_handlers(void) { struct sigaction actf; @@ -1043,6 +1056,20 @@ void jl_install_default_signal_handlers(void) if (sigaction(SIGFPE, &actf, NULL) < 0) { jl_errorf("fatal error: sigaction: %s", strerror(errno)); } +#if defined(_OS_DARWIN_) && defined(_CPU_AARCH64_) + struct sigaction acttrap; + memset(&acttrap, 0, sizeof(struct sigaction)); + sigemptyset(&acttrap.sa_mask); + acttrap.sa_sigaction = sigtrap_handler; + acttrap.sa_flags = SA_ONSTACK | SA_SIGINFO; + if (sigaction(SIGTRAP, &acttrap, NULL) < 0) { + jl_errorf("fatal error: sigaction: %s", strerror(errno)); + } +#else + if (signal(SIGTRAP, SIG_IGN) == SIG_ERR) { + jl_error("fatal error: Couldn't set SIGTRAP"); + } +#endif struct sigaction actint; memset(&actint, 0, sizeof(struct sigaction)); sigemptyset(&actint.sa_mask); @@ -1054,9 +1081,6 @@ void jl_install_default_signal_handlers(void) if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { jl_error("fatal error: Couldn't set SIGPIPE"); } - if (signal(SIGTRAP, SIG_IGN) == SIG_ERR) { - jl_error("fatal error: Couldn't set SIGTRAP"); - } #if defined(HAVE_MACH) allocate_mach_handler();