Avoid infinite recursion when raising SIGABRT in SIGABRT handler

When VM state is corrupted enough, we can call abort() from the SIGABRT
handler. Previously, we would spam until the stack is full:

    ABRT received in SEGV handler
    SEGV received in ABRT handler
    ABRT received in SEGV handler
    ABRT received in ABRT handler
    ABRT received in ABRT handler
    ABRT received in ABRT handler
    [...]

We've seen this on CI:
https://github.com/ruby/ruby/actions/runs/26591192708/job/78350130653

To test this situation locally, temporarily patch in a call to abort()
in rb_bug_for_fatal_signal() then use `Process.kill(:ABRT, Process.pid)`.

Fix by restoring the default signal handler before aborting.
This commit is contained in:
Alan Wu committed 2026-06-01 16:25:09 -04:00
1 parent 9c62016982
commit 3a5a46f8ad
1 file changed
+13 -1
Notes: git 2026-07-07 18:14:04 +00:00
+13 -1
View File
@@ -1020,8 +1020,20 @@ check_reserved_signal_(const char *name, size_t name_len, int signo)
#if __has_feature(address_sanitizer) || \
__has_feature(memory_sanitizer) || \
defined(HAVE_VALGRIND_MEMCHECK_H)
ruby_posix_signal(signo, SIG_DFL);
# define SANITIZING true
#else
# define SANITIZING false
#endif
#ifdef SIGABRT
// Avoid infinite loop when already aborting
# define RECURSIVE (signo == SIGABRT)
#else
# define RECURSIVE false
#endif
if (SANITIZING || RECURSIVE) ruby_signal(signo, SIG_DFL);
# undef SANITIZING
# undef RECURSIVE
W(name, name_len);
W(msg1, sizeof(msg1));
W(prev, strlen(prev));