[libc-commits] [libc] [libc] update abort implementation and lift it for internal usage (PR #189756)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Wed Apr 1 07:19:01 PDT 2026
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/189756
>From 60be7798c2844812a54471013645ed3558fef8cc Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Tue, 31 Mar 2026 17:39:31 -0400
Subject: [PATCH 1/3] [libc] update abort implementation and lift it for
internal usage
---
libc/src/signal/linux/signal_utils.h | 8 ++
libc/src/stdlib/linux/CMakeLists.txt | 16 +++-
libc/src/stdlib/linux/abort.cpp | 19 +---
libc/src/stdlib/linux/abort_utils.h | 78 ++++++++++++++++
.../integration/src/stdlib/CMakeLists.txt | 23 +++++
.../integration/src/stdlib/abort_test.cpp | 89 +++++++++++++++++++
6 files changed, 214 insertions(+), 19 deletions(-)
create mode 100644 libc/src/stdlib/linux/abort_utils.h
create mode 100644 libc/test/integration/src/stdlib/abort_test.cpp
diff --git a/libc/src/signal/linux/signal_utils.h b/libc/src/signal/linux/signal_utils.h
index be4501c27dba5..e36d47f4fb602 100644
--- a/libc/src/signal/linux/signal_utils.h
+++ b/libc/src/signal/linux/signal_utils.h
@@ -113,6 +113,14 @@ LIBC_INLINE int restore_signals(const sigset_t &set) {
&set, nullptr, sizeof(sigset_t));
}
+LIBC_INLINE int unblock_signal(int signal) {
+ sigset_t set;
+ if (!add_signal(set, signal))
+ return -EINVAL;
+ return LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, SIG_UNBLOCK,
+ &set, nullptr, sizeof(sigset_t));
+}
+
LIBC_INLINE ErrorOr<int>
do_sigaction(int signal, const struct sigaction *__restrict libc_new,
struct sigaction *__restrict libc_old) {
diff --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt
index 1d3c00a5e0ddb..4e476d8d57354 100644
--- a/libc/src/stdlib/linux/CMakeLists.txt
+++ b/libc/src/stdlib/linux/CMakeLists.txt
@@ -1,3 +1,15 @@
+add_header_library(
+ abort_utils
+ HDRS
+ abort_utils.h
+ DEPENDS
+ libc.include.stdlib
+ libc.src.signal.raise
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.threads.linux.rwlock
+ libc.src.signal.linux.__restore
+)
+
add_entrypoint_object(
abort
SRCS
@@ -5,7 +17,7 @@ add_entrypoint_object(
HDRS
../abort.h
DEPENDS
+ .abort_utils
+ libc.src.signal.linux.__restore
libc.include.stdlib
- libc.src.signal.raise
- libc.src.stdlib._Exit
)
diff --git a/libc/src/stdlib/linux/abort.cpp b/libc/src/stdlib/linux/abort.cpp
index d78ea675593a4..0374e9315cba8 100644
--- a/libc/src/stdlib/linux/abort.cpp
+++ b/libc/src/stdlib/linux/abort.cpp
@@ -6,26 +6,11 @@
//
//===----------------------------------------------------------------------===//
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-#include "src/signal/raise.h"
-#include "src/stdlib/_Exit.h"
-
#include "src/stdlib/abort.h"
+#include "src/stdlib/linux/abort_utils.h"
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(void, abort, ()) {
- // TODO: When sigprocmask and sigaction land:
- // Unblock SIGABRT, raise it, if it was ignored or the handler returned,
- // change its action to SIG_DFL, raise it again.
- // TODO: When C11 mutexes land:
- // Acquire recursive mutex (in case the current signal handler for SIGABRT
- // itself calls abort we don't want to deadlock on the same thread trying
- // to acquire it's own mutex.)
- LIBC_NAMESPACE::raise(SIGABRT);
- LIBC_NAMESPACE::raise(SIGKILL);
- LIBC_NAMESPACE::_Exit(127);
-}
+LLVM_LIBC_FUNCTION(void, abort, ()) { abort_utils::abort(); }
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/linux/abort_utils.h b/libc/src/stdlib/linux/abort_utils.h
new file mode 100644
index 0000000000000..c4795c879ae08
--- /dev/null
+++ b/libc/src/stdlib/linux/abort_utils.h
@@ -0,0 +1,78 @@
+//===-- Internal header for Linux abort -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_LINUX_ABORT_UTILS_H
+#define LLVM_LIBC_SRC_STDLIB_LINUX_ABORT_UTILS_H
+
+#include "hdr/types/sigset_t.h"
+#include "include/llvm-libc-types/sigset_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/OSUtil/exit.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/raise.h"
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/linux/rwlock.h"
+#include "src/signal/linux/signal_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace abort_utils {
+
+// TODO: this lock needs to be acquired during _Fork/fork.
+class AbortLockGuard {
+private:
+ sigset_t old_mask;
+ LIBC_INLINE_VAR static RwLock abort_lock;
+
+public:
+ LIBC_INLINE constexpr AbortLockGuard(bool exclusive) : old_mask{} {
+ RwLock::LockResult result = RwLock::LockResult::Success;
+ do {
+ if (exclusive)
+ result = abort_lock.write_lock(cpp::nullopt);
+ else
+ result = abort_lock.read_lock(cpp::nullopt);
+ } while (result == RwLock::LockResult::Overflow);
+
+ (void)block_all_signals(old_mask);
+ }
+
+ LIBC_INLINE ~AbortLockGuard() {
+ (void)restore_signals(old_mask);
+ (void)abort_lock.unlock();
+ }
+};
+
+[[noreturn]] LIBC_INLINE void abort() {
+ // 1. Try to raise SIGABRT.
+ (void)LIBC_NAMESPACE::linux_syscalls::raise(SIGABRT);
+
+ // We get back from abort, potentially from a abort handler.
+ // We recover the handler to default and raise it again. Since this is the
+ // real abort routine, we demand exclusive access to the abort lock.
+ // We have already returned from the first raise, so it is okay to grab
+ // exclusive access.
+ AbortLockGuard guard(true);
+ struct sigaction sa{};
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = 0;
+ (void)do_sigaction(SIGABRT, &sa, nullptr);
+ (void)LIBC_NAMESPACE::linux_syscalls::raise(SIGABRT);
+
+ // Now unblock the signal. The pending abort signal is now unblocked and
+ // should be delivered to its default handler.
+ (void)unblock_signal(SIGABRT);
+
+ LIBC_NAMESPACE::internal::exit(127);
+}
+} // namespace abort_utils
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDLIB_LINUX_ABORT_UTILS_H
diff --git a/libc/test/integration/src/stdlib/CMakeLists.txt b/libc/test/integration/src/stdlib/CMakeLists.txt
index 1773d9fc9f0f5..cf39b7ceb2f87 100644
--- a/libc/test/integration/src/stdlib/CMakeLists.txt
+++ b/libc/test/integration/src/stdlib/CMakeLists.txt
@@ -16,3 +16,26 @@ add_integration_test(
FRANCE=Paris
GERMANY=Berlin
)
+
+if(${LIBC_TARGET_OS} STREQUAL "linux")
+ add_integration_test(
+ abort_test
+ SUITE
+ stdlib-integration-tests
+ SRCS
+ abort_test.cpp
+ DEPENDS
+ libc.include.signal
+ libc.include.sys_wait
+ libc.include.unistd
+ libc.src.signal.signal
+ libc.src.stdlib._Exit
+ libc.src.stdlib.abort
+ libc.src.sys.wait.waitpid
+ libc.src.unistd.close
+ libc.src.unistd.fork
+ libc.src.unistd.pipe
+ libc.src.unistd.read
+ libc.src.unistd.write
+ )
+endif()
diff --git a/libc/test/integration/src/stdlib/abort_test.cpp b/libc/test/integration/src/stdlib/abort_test.cpp
new file mode 100644
index 0000000000000..5b8a38655ef8c
--- /dev/null
+++ b/libc/test/integration/src/stdlib/abort_test.cpp
@@ -0,0 +1,89 @@
+//===-- Integration tests for abort --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/signal/signal.h"
+#include "src/stdlib/_Exit.h"
+#include "src/stdlib/abort.h"
+#include "src/stdlib/linux/abort_utils.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/close.h"
+#include "src/unistd/fork.h"
+#include "src/unistd/pipe.h"
+#include "src/unistd/read.h"
+#include "src/unistd/write.h"
+
+#include "test/IntegrationTest/test.h"
+
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace {
+
+constexpr char HANDLER_MARKER = 'A';
+int handler_pipe_fd = -1;
+
+void expect_child_died_with_signal(pid_t pid, int signal) {
+ int status = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), signal);
+}
+
+void child_abort() { LIBC_NAMESPACE::abort(); }
+
+void returning_sigabrt_handler(int) {
+ if (handler_pipe_fd >= 0)
+ LIBC_NAMESPACE::write(handler_pipe_fd, &HANDLER_MARKER, 1);
+}
+
+void child_abort_with_returning_handler() {
+ auto previous = LIBC_NAMESPACE::signal(SIGABRT, returning_sigabrt_handler);
+ ASSERT_NE(previous, SIG_ERR);
+ LIBC_NAMESPACE::abort();
+}
+
+void abort_kills_child_with_sigabrt() {
+ pid_t pid = LIBC_NAMESPACE::fork();
+ if (pid == 0)
+ child_abort();
+
+ ASSERT_TRUE(pid > 0);
+ expect_child_died_with_signal(pid, SIGABRT);
+}
+
+void abort_reraises_sigabrt_after_returning_handler() {
+ int pipefd[2];
+ ASSERT_EQ(LIBC_NAMESPACE::pipe(pipefd), 0);
+
+ pid_t pid = LIBC_NAMESPACE::fork();
+ if (pid == 0) {
+ LIBC_NAMESPACE::close(pipefd[0]);
+ handler_pipe_fd = pipefd[1];
+ child_abort_with_returning_handler();
+ }
+
+ ASSERT_TRUE(pid > 0);
+ ASSERT_EQ(LIBC_NAMESPACE::close(pipefd[1]), 0);
+
+ expect_child_died_with_signal(pid, SIGABRT);
+
+ char marker = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::read(pipefd[0], &marker, 1), ssize_t(1));
+ ASSERT_EQ(marker, HANDLER_MARKER);
+ ASSERT_EQ(LIBC_NAMESPACE::close(pipefd[0]), 0);
+}
+
+} // namespace
+
+TEST_MAIN([[maybe_unused]] int argc, [[maybe_unused]] char **argv,
+ [[maybe_unused]] char **envp) {
+ abort_kills_child_with_sigabrt();
+ abort_reraises_sigabrt_after_returning_handler();
+ return 0;
+}
>From 87eb626c1a325625b8fd8e8200db8173fc3ac249 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 1 Apr 2026 10:17:51 -0400
Subject: [PATCH 2/3] fix
---
libc/src/stdlib/linux/CMakeLists.txt | 2 +-
libc/src/stdlib/linux/abort_utils.h | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/libc/src/stdlib/linux/CMakeLists.txt b/libc/src/stdlib/linux/CMakeLists.txt
index 4e476d8d57354..ca09842fa906d 100644
--- a/libc/src/stdlib/linux/CMakeLists.txt
+++ b/libc/src/stdlib/linux/CMakeLists.txt
@@ -6,7 +6,7 @@ add_header_library(
libc.include.stdlib
libc.src.signal.raise
libc.src.__support.OSUtil.osutil
- libc.src.__support.threads.linux.rwlock
+ libc.src.__support.threads.raw_rwlock
libc.src.signal.linux.__restore
)
diff --git a/libc/src/stdlib/linux/abort_utils.h b/libc/src/stdlib/linux/abort_utils.h
index c4795c879ae08..5e686f0c3475a 100644
--- a/libc/src/stdlib/linux/abort_utils.h
+++ b/libc/src/stdlib/linux/abort_utils.h
@@ -17,7 +17,7 @@
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/__support/threads/linux/rwlock.h"
+#include "src/__support/threads/raw_rwlock.h"
#include "src/signal/linux/signal_utils.h"
namespace LIBC_NAMESPACE_DECL {
@@ -28,17 +28,17 @@ namespace abort_utils {
class AbortLockGuard {
private:
sigset_t old_mask;
- LIBC_INLINE_VAR static RwLock abort_lock;
+ LIBC_INLINE_VAR static RawRwLock abort_lock;
public:
LIBC_INLINE constexpr AbortLockGuard(bool exclusive) : old_mask{} {
- RwLock::LockResult result = RwLock::LockResult::Success;
+ RawRwLock::LockResult result = RawRwLock::LockResult::Success;
do {
if (exclusive)
result = abort_lock.write_lock(cpp::nullopt);
else
result = abort_lock.read_lock(cpp::nullopt);
- } while (result == RwLock::LockResult::Overflow);
+ } while (result == RawRwLock::LockResult::Overflow);
(void)block_all_signals(old_mask);
}
>From ac8cc59295cfa2c691bf12e0b247034e9a75815c Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 1 Apr 2026 10:18:46 -0400
Subject: [PATCH 3/3] fix trivial auto var
---
libc/src/signal/linux/signal_utils.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/signal/linux/signal_utils.h b/libc/src/signal/linux/signal_utils.h
index e36d47f4fb602..66e55943a2c3f 100644
--- a/libc/src/signal/linux/signal_utils.h
+++ b/libc/src/signal/linux/signal_utils.h
@@ -114,7 +114,7 @@ LIBC_INLINE int restore_signals(const sigset_t &set) {
}
LIBC_INLINE int unblock_signal(int signal) {
- sigset_t set;
+ sigset_t set = empty_set();
if (!add_signal(set, signal))
return -EINVAL;
return LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, SIG_UNBLOCK,
More information about the libc-commits
mailing list