From 3a4ff2d02eef18921eee766c81c1a96335764020 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sun, 16 Aug 2026 20:02:57 -0700 Subject: [PATCH] fix(signal): terminate without raising SIGABRT --- libc-top-half/musl/src/signal/sigaction.c | 9 +++- test/wasix/c_signals/CMakeLists.txt | 4 ++ test/wasix/c_signals/main.c | 54 +++++++++++++++++++++++ test/wasix/c_signals/test.sh | 41 +++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 test/wasix/c_signals/CMakeLists.txt create mode 100644 test/wasix/c_signals/main.c create mode 100755 test/wasix/c_signals/test.sh diff --git a/libc-top-half/musl/src/signal/sigaction.c b/libc-top-half/musl/src/signal/sigaction.c index 5adc7420b..7bd50c42a 100644 --- a/libc-top-half/musl/src/signal/sigaction.c +++ b/libc-top-half/musl/src/signal/sigaction.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "syscall.h" @@ -31,8 +32,12 @@ static void core_handler(int sig) { _Noreturn static void terminate_handler(int sig) { - fprintf(stderr, "Program recieved termination signal: %s\n", strsignal(sig)); - abort(); + /* A terminating signal is not an abort. Calling abort() here raises + * SIGABRT while the original signal is still being dispatched, which can + * recursively re-enter the default signal path on runtimes that deliver + * the nested signal immediately. POSIX termination is silent; report it + * through the conventional shell status instead. */ + _Exit(128 + sig); } _Noreturn diff --git a/test/wasix/c_signals/CMakeLists.txt b/test/wasix/c_signals/CMakeLists.txt new file mode 100644 index 000000000..a973015e0 --- /dev/null +++ b/test/wasix/c_signals/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5.0) +project(c_signals C) + +add_executable(main main.c) diff --git a/test/wasix/c_signals/main.c b/test/wasix/c_signals/main.c new file mode 100644 index 000000000..1b6473677 --- /dev/null +++ b/test/wasix/c_signals/main.c @@ -0,0 +1,54 @@ +#include +#include +#include + +static volatile sig_atomic_t handled; + +static void siginfo_handler(int sig, siginfo_t *info, void *context) +{ + if (sig == SIGUSR1 && info != NULL && info->si_signo == SIGUSR1 && + context == NULL) { + handled++; + } +} + +static int install_siginfo_handler(int flags) +{ + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_sigaction = siginfo_handler; + action.sa_flags = SA_SIGINFO | flags; + sigemptyset(&action.sa_mask); + return sigaction(SIGUSR1, &action, NULL); +} + +int main(int argc, char **argv) +{ + if (argc != 2) return 2; + + if (strcmp(argv[1], "siginfo") == 0) { + if (install_siginfo_handler(0) != 0) return 3; + if (raise(SIGUSR1) != 0) return 4; + return handled == 1 ? 0 : 5; + } + + if (strcmp(argv[1], "reset") == 0) { + if (install_siginfo_handler(SA_RESETHAND) != 0) return 6; + if (raise(SIGUSR1) != 0) return 7; + if (handled != 1) return 8; + raise(SIGUSR1); + return 9; + } + + if (strcmp(argv[1], "terminate") == 0) { + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + if (sigaction(SIGTERM, &action, NULL) != 0) return 10; + raise(SIGTERM); + return 12; + } + + return 11; +} diff --git a/test/wasix/c_signals/test.sh b/test/wasix/c_signals/test.sh new file mode 100755 index 000000000..e83768694 --- /dev/null +++ b/test/wasix/c_signals/test.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -u + +stderr_file=$(mktemp) +trap 'rm -f "$stderr_file"' EXIT + +wasmer run --enable-all ./main siginfo +RESULT=$? +if [ "$RESULT" != "0" ]; then + echo "SA_SIGINFO test failed: different exit code ($RESULT vs. 0)" >&2 + exit 1 +fi + +wasmer run --enable-all ./main reset 2>"$stderr_file" +RESULT=$? +if [ "$RESULT" != "138" ]; then + echo "SA_RESETHAND test failed: different exit code ($RESULT vs. 138)" >&2 + cat "$stderr_file" >&2 + exit 1 +fi +if grep -q "fatal signal: Aborted" "$stderr_file"; then + echo "SA_RESETHAND test failed: default termination recursed into SIGABRT" >&2 + cat "$stderr_file" >&2 + exit 1 +fi + +wasmer run --enable-all ./main terminate 2>"$stderr_file" +RESULT=$? +if [ "$RESULT" != "143" ]; then + echo "default SIGTERM test failed: different exit code ($RESULT vs. 143)" >&2 + cat "$stderr_file" >&2 + exit 1 +fi +if [ -s "$stderr_file" ]; then + echo "default SIGTERM test failed: default termination wrote to stderr" >&2 + cat "$stderr_file" >&2 + exit 1 +fi + +echo "c_signals test passed"