diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c
index 1d96fcfdf526464ff3559afe49a7498f354f869a..a16bc24b7cac612e310d6b320f74ea75e2c70aa2 100644
--- a/rts/posix/Signals.c
+++ b/rts/posix/Signals.c
@@ -522,7 +522,9 @@ shutdown_handler(int sig STG_UNUSED)
     // extreme prejudice.  So the first ^C tries to exit the program
     // cleanly, and the second one just kills it.
     if (getSchedState() >= SCHED_INTERRUPTING) {
-        stg_exit(EXIT_INTERRUPTED);
+        // N.B. we cannot use stg_exit() here as it calls exit() which is not
+        // signal-safe. See #23417.
+        _exit(EXIT_INTERRUPTED);
     } else {
         interruptStgRts();
     }