[libc] Implement pthread_sigmask (#198682) * Extract `rt_sigprocmask` syscall wrapper into the libc/src/__support/OSUtil/linux/syscall_wrappers/ directory * Convert all existing users of this syscall, and simplify the logic where applicable. * Implement `pthread_sigmask`, which is effectively another POSIX wrapper around `rt_sigprocmask` syscall similar to `sigprocmask` GitOrigin-RevId: b996b4eebbedb3b96b584e4db244b6ce685fae45
diff --git a/config/linux/aarch64/entrypoints.txt b/config/linux/aarch64/entrypoints.txt index 2039d8f..07d4ad3 100644 --- a/config/linux/aarch64/entrypoints.txt +++ b/config/linux/aarch64/entrypoints.txt
@@ -1159,6 +1159,7 @@ # signal.h entrypoints libc.src.signal.kill + libc.src.signal.pthread_sigmask libc.src.signal.raise libc.src.signal.sigaction libc.src.signal.sigaddset
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt index 8187e9f..054b99b 100644 --- a/config/linux/x86_64/entrypoints.txt +++ b/config/linux/x86_64/entrypoints.txt
@@ -1357,6 +1357,7 @@ # signal.h entrypoints libc.src.signal.kill + libc.src.signal.pthread_sigmask libc.src.signal.raise libc.src.signal.sigaction libc.src.signal.sigaddset
diff --git a/include/signal.yaml b/include/signal.yaml index 5d14082..6713b4f 100644 --- a/include/signal.yaml +++ b/include/signal.yaml
@@ -106,3 +106,11 @@ - type: int - type: const sigset_t *__restrict - type: sigset_t *__restrict + - name: pthread_sigmask + standards: + - posix + return_type: int + arguments: + - type: int + - type: const sigset_t *__restrict + - type: sigset_t *__restrict
diff --git a/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt index dc1702e..d597371 100644 --- a/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt +++ b/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -268,11 +268,25 @@ ) add_header_library( + rt_sigprocmask + HDRS + rt_sigprocmask.h + DEPENDS + libc.src.__support.OSUtil.osutil + libc.src.__support.common + libc.src.__support.error_or + libc.src.__support.macros.config + libc.hdr.types.sigset_t + libc.include.sys_syscall +) + +add_header_library( raise HDRS raise.h DEPENDS libc.src.__support.OSUtil.osutil + libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask libc.src.__support.common libc.src.__support.error_or libc.src.__support.macros.config
diff --git a/src/__support/OSUtil/linux/syscall_wrappers/raise.h b/src/__support/OSUtil/linux/syscall_wrappers/raise.h index 33d0ade..2b61e4e 100644 --- a/src/__support/OSUtil/linux/syscall_wrappers/raise.h +++ b/src/__support/OSUtil/linux/syscall_wrappers/raise.h
@@ -12,6 +12,7 @@ #include "hdr/signal_macros.h" #include "hdr/types/sigset_t.h" #include "src/__support/OSUtil/linux/syscall.h" // syscall_impl +#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h" #include "src/__support/common.h" #include "src/__support/error_or.h" #include "src/__support/macros/config.h" @@ -27,16 +28,14 @@ public: LIBC_INLINE SigMaskGuard(ErrorOr<int> &status) : old_set{}, status(status) { sigset_t full_set = sigset_t{{-1UL}}; - status = syscall_impl<int>(SYS_rt_sigprocmask, SIG_BLOCK, &full_set, - &old_set, sizeof(sigset_t)); + status = linux_syscalls::rt_sigprocmask(SIG_BLOCK, &full_set, &old_set); } LIBC_INLINE ~SigMaskGuard() { if (status.has_value()) { - int restore_result = - syscall_impl<int>(SYS_rt_sigprocmask, SIG_SETMASK, &old_set, - nullptr, sizeof(sigset_t)); - if (restore_result < 0) - status = Error(-restore_result); + auto restore_result = + linux_syscalls::rt_sigprocmask(SIG_SETMASK, &old_set, nullptr); + if (!restore_result.has_value()) + status = restore_result.error(); } } };
diff --git a/src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h b/src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h new file mode 100644 index 0000000..bc48b9e --- /dev/null +++ b/src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h
@@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for rt_sigprocmask. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RT_SIGPROCMASK_H +#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RT_SIGPROCMASK_H + +#include "hdr/types/sigset_t.h" +#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked +#include "src/__support/common.h" +#include "src/__support/error_or.h" +#include "src/__support/macros/config.h" +#include <sys/syscall.h> // For syscall numbers + +namespace LIBC_NAMESPACE_DECL { +namespace linux_syscalls { + +LIBC_INLINE ErrorOr<int> rt_sigprocmask(int how, const sigset_t *set, + sigset_t *oldset) { + return syscall_checked<int>(SYS_rt_sigprocmask, how, set, oldset, + sizeof(sigset_t)); +} + +} // namespace linux_syscalls +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RT_SIGPROCMASK_H
diff --git a/src/setjmp/linux/CMakeLists.txt b/src/setjmp/linux/CMakeLists.txt index 62acec0..bde4e5b 100644 --- a/src/setjmp/linux/CMakeLists.txt +++ b/src/setjmp/linux/CMakeLists.txt
@@ -6,7 +6,7 @@ sigsetjmp_epilogue.cpp DEPENDS libc.src.__support.common - libc.src.__support.OSUtil.osutil + libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask libc.hdr.types.sigjmp_buf libc.hdr.types.sigset_t )
diff --git a/src/setjmp/linux/sigsetjmp_epilogue.cpp b/src/setjmp/linux/sigsetjmp_epilogue.cpp index 7e14312..7922f51 100644 --- a/src/setjmp/linux/sigsetjmp_epilogue.cpp +++ b/src/setjmp/linux/sigsetjmp_epilogue.cpp
@@ -7,19 +7,18 @@ //===----------------------------------------------------------------------===// #include "src/setjmp/sigsetjmp_epilogue.h" -#include "src/__support/OSUtil/syscall.h" +#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h" #include "src/__support/common.h" -#include <sys/syscall.h> // For syscall numbers. namespace LIBC_NAMESPACE_DECL { [[gnu::returns_twice]] int sigsetjmp_epilogue(sigjmp_buf buffer, int retval) { // If set is NULL, then the signal mask is unchanged (i.e., how is // ignored), but the current value of the signal mask is nevertheless // returned in oldset (if it is not NULL). - syscall_impl<long>(SYS_rt_sigprocmask, SIG_SETMASK, - /* set= */ retval ? &buffer->sigmask : nullptr, - /* old_set= */ retval ? nullptr : &buffer->sigmask, - sizeof(sigset_t)); + linux_syscalls::rt_sigprocmask( + SIG_SETMASK, + /* set= */ retval ? &buffer->sigmask : nullptr, + /* old_set= */ retval ? nullptr : &buffer->sigmask); return retval; } } // namespace LIBC_NAMESPACE_DECL
diff --git a/src/signal/CMakeLists.txt b/src/signal/CMakeLists.txt index c70ab95..a55b55e 100644 --- a/src/signal/CMakeLists.txt +++ b/src/signal/CMakeLists.txt
@@ -38,6 +38,13 @@ ) add_entrypoint_object( + pthread_sigmask + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.pthread_sigmask +) + +add_entrypoint_object( sigemptyset ALIAS DEPENDS
diff --git a/src/signal/linux/CMakeLists.txt b/src/signal/linux/CMakeLists.txt index 45ffb69..9820276 100644 --- a/src/signal/linux/CMakeLists.txt +++ b/src/signal/linux/CMakeLists.txt
@@ -10,6 +10,7 @@ libc.include.sys_syscall libc.src.__support.OSUtil.linux.vdso libc.src.__support.OSUtil.osutil + libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask libc.src.__support.error_or libc.src.__support.threads.raw_rwlock ) @@ -89,12 +90,24 @@ DEPENDS .signal_utils libc.hdr.types.sigset_t - libc.include.sys_syscall - libc.src.__support.OSUtil.osutil + libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask libc.src.errno.errno ) add_entrypoint_object( + pthread_sigmask + SRCS + pthread_sigmask.cpp + HDRS + ../pthread_sigmask.h + DEPENDS + libc.hdr.types.sigset_t + libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask + libc.src.__support.common + libc.src.__support.macros.config +) + +add_entrypoint_object( sigemptyset SRCS sigemptyset.cpp
diff --git a/src/signal/linux/pthread_sigmask.cpp b/src/signal/linux/pthread_sigmask.cpp new file mode 100644 index 0000000..b38c055 --- /dev/null +++ b/src/signal/linux/pthread_sigmask.cpp
@@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Linux implementation of pthread_sigmask. +/// +//===----------------------------------------------------------------------===// + +#include "src/signal/pthread_sigmask.h" + +#include "hdr/types/sigset_t.h" +#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, pthread_sigmask, + (int how, const sigset_t *__restrict set, + sigset_t *__restrict oldset)) { + auto result = linux_syscalls::rt_sigprocmask(how, set, oldset); + if (result.has_value()) + return 0; + + return result.error(); +} + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/signal/linux/signal_utils.h b/src/signal/linux/signal_utils.h index 09569bc..c1f0679 100644 --- a/src/signal/linux/signal_utils.h +++ b/src/signal/linux/signal_utils.h
@@ -14,6 +14,7 @@ #include "hdr/types/sigset_t.h" #include "hdr/types/size_t.h" #include "hdr/types/struct_sigaction.h" +#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h" #include "src/__support/OSUtil/linux/vdso.h" #include "src/__support/OSUtil/syscall.h" // For internal syscall function. #include "src/__support/common.h" @@ -103,23 +104,20 @@ return true; } -LIBC_INLINE int block_all_signals(sigset_t &set) { +LIBC_INLINE ErrorOr<int> block_all_signals(sigset_t &set) { sigset_t full = full_set(); - return LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, SIG_BLOCK, &full, - &set, sizeof(sigset_t)); + return linux_syscalls::rt_sigprocmask(SIG_BLOCK, &full, &set); } -LIBC_INLINE int restore_signals(const sigset_t &set) { - return LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, SIG_SETMASK, - &set, nullptr, sizeof(sigset_t)); +LIBC_INLINE ErrorOr<int> restore_signals(const sigset_t &set) { + return linux_syscalls::rt_sigprocmask(SIG_SETMASK, &set, nullptr); } -LIBC_INLINE int unblock_signal(int signal) { +LIBC_INLINE ErrorOr<int> unblock_signal(int signal) { sigset_t set = empty_set(); if (!add_signal(set, signal)) - return -EINVAL; - return LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, SIG_UNBLOCK, - &set, nullptr, sizeof(sigset_t)); + return Error(EINVAL); + return linux_syscalls::rt_sigprocmask(SIG_UNBLOCK, &set, nullptr); } // This guard is used to: @@ -144,13 +142,13 @@ // This uses a valid sigset_t size and internal storage. A failure here // would indicate a kernel ABI mismatch, which is not actionable here. - block_all_signals(old_mask); + (void)block_all_signals(old_mask); } LIBC_INLINE ~SigAbortGuard() { // This restores a previously saved mask from internal storage. A failure // here would likewise be a non-recoverable kernel ABI issue. - restore_signals(old_mask); + (void)restore_signals(old_mask); (void)abort_lock.unlock(); } };
diff --git a/src/signal/linux/sigprocmask.cpp b/src/signal/linux/sigprocmask.cpp index af3c424..a681cc3 100644 --- a/src/signal/linux/sigprocmask.cpp +++ b/src/signal/linux/sigprocmask.cpp
@@ -9,25 +9,22 @@ #include "src/signal/sigprocmask.h" #include "hdr/types/sigset_t.h" -#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h" #include "src/__support/common.h" #include "src/__support/libc_errno.h" #include "src/__support/macros/config.h" #include "src/signal/linux/signal_utils.h" -#include <sys/syscall.h> // For syscall numbers. - namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(int, sigprocmask, (int how, const sigset_t *__restrict set, sigset_t *__restrict oldset)) { - int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_rt_sigprocmask, how, set, - oldset, sizeof(sigset_t)); - if (!ret) + auto result = linux_syscalls::rt_sigprocmask(how, set, oldset); + if (result.has_value()) return 0; - libc_errno = -ret; + libc_errno = result.error(); return -1; }
diff --git a/src/signal/pthread_sigmask.h b/src/signal/pthread_sigmask.h new file mode 100644 index 0000000..1d8d2ef --- /dev/null +++ b/src/signal/pthread_sigmask.h
@@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for pthread_sigmask. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SIGNAL_PTHREAD_SIGMASK_H +#define LLVM_LIBC_SRC_SIGNAL_PTHREAD_SIGMASK_H + +#include "hdr/types/sigset_t.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int pthread_sigmask(int how, const sigset_t *__restrict set, + sigset_t *__restrict oldset); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SIGNAL_PTHREAD_SIGMASK_H
diff --git a/src/stdlib/linux/abort_utils.h b/src/stdlib/linux/abort_utils.h index 36604f1..9ba752e 100644 --- a/src/stdlib/linux/abort_utils.h +++ b/src/stdlib/linux/abort_utils.h
@@ -43,7 +43,7 @@ // Now unblock the signal. The pending abort signal is now unblocked and // should be delivered to its default handler. // If this fails, there is still no meaningful recovery path while aborting. - unblock_signal(SIGABRT); + (void)unblock_signal(SIGABRT); internal::exit(127); }
diff --git a/test/src/signal/CMakeLists.txt b/test/src/signal/CMakeLists.txt index 2135164..ba05609 100644 --- a/test/src/signal/CMakeLists.txt +++ b/test/src/signal/CMakeLists.txt
@@ -56,6 +56,21 @@ ) add_libc_unittest( + pthread_sigmask_test + SUITE + libc_signal_unittests + SRCS + pthread_sigmask_test.cpp + DEPENDS + libc.src.errno.errno + libc.src.signal.pthread_sigmask + libc.src.signal.raise + libc.src.signal.sigaddset + libc.src.signal.sigemptyset + libc.test.UnitTest.ErrnoCheckingTest +) + +add_libc_unittest( sigaddset_test SUITE libc_signal_unittests
diff --git a/test/src/signal/pthread_sigmask_test.cpp b/test/src/signal/pthread_sigmask_test.cpp new file mode 100644 index 0000000..02eaa83 --- /dev/null +++ b/test/src/signal/pthread_sigmask_test.cpp
@@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Unittests for pthread_sigmask. +/// +//===----------------------------------------------------------------------===// + +#include "src/signal/pthread_sigmask.h" +#include "src/signal/raise.h" +#include "src/signal/sigaddset.h" +#include "src/signal/sigemptyset.h" +#include "test/UnitTest/ErrnoCheckingTest.h" +#include "test/UnitTest/Test.h" + +#include <signal.h> + +class LlvmLibcPthreadSigmaskTest + : public LIBC_NAMESPACE::testing::ErrnoCheckingTest { + sigset_t old_set; + +public: + void SetUp() override { + ErrnoCheckingTest::SetUp(); + LIBC_NAMESPACE::pthread_sigmask(0, nullptr, &old_set); + } + + void TearDown() override { + LIBC_NAMESPACE::pthread_sigmask(SIG_SETMASK, &old_set, nullptr); + ErrnoCheckingTest::TearDown(); + } +}; + +// This tests for invalid input. +TEST_F(LlvmLibcPthreadSigmaskTest, PthreadSigmaskInvalid) { + sigset_t valid; + // 17 and -4 are out of the range for pthread_sigmask's how parameter. + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(17, &valid, nullptr), EINVAL); + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(-4, &valid, nullptr), EINVAL); + + // This pointer is out of this process's address range. + sigset_t *invalid = reinterpret_cast<sigset_t *>(-1); + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_SETMASK, invalid, nullptr), + EFAULT); + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_SETMASK, nullptr, invalid), + EFAULT); +} + +// This tests that when nothing is blocked, a process gets killed and also tests +// that when signals are blocked they are not delivered to the process. +TEST_F(LlvmLibcPthreadSigmaskTest, BlockUnblock) { + sigset_t sigset; + EXPECT_EQ(LIBC_NAMESPACE::sigemptyset(&sigset), 0); + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_SETMASK, &sigset, nullptr), 0); + EXPECT_DEATH([] { LIBC_NAMESPACE::raise(SIGUSR1); }, WITH_SIGNAL(SIGUSR1)); + EXPECT_EQ(LIBC_NAMESPACE::sigaddset(&sigset, SIGUSR1), 0); + EXPECT_EQ(LIBC_NAMESPACE::pthread_sigmask(SIG_SETMASK, &sigset, nullptr), 0); + EXPECT_EXITS([] { LIBC_NAMESPACE::raise(SIGUSR1); }, 0); +}