[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);
+}