[libc] Add sigprocmask
authorAlex Brachet <alexbrachetmialot@gmail.com>
Mon, 2 Mar 2020 08:47:21 +0000 (03:47 -0500)
committerAlex Brachet <alexbrachetmialot@gmail.com>
Mon, 2 Mar 2020 08:47:21 +0000 (03:47 -0500)
Summary: This patch adds `sigprocmask`, `sigemptyset` and `sigaddset`

Reviewers: sivachandra, MaskRay, gchatelet

Reviewed By: sivachandra

Subscribers: mgorny, tschuett, libc-commits

Differential Revision: https://reviews.llvm.org/D75026

16 files changed:
libc/config/linux/api.td
libc/include/signal.h.def
libc/lib/CMakeLists.txt
libc/spec/posix.td
libc/src/signal/linux/CMakeLists.txt
libc/src/signal/linux/sigaddset.cpp [new file with mode: 0644]
libc/src/signal/linux/sigemptyset.cpp [new file with mode: 0644]
libc/src/signal/linux/signal.h
libc/src/signal/linux/sigprocmask.cpp [new file with mode: 0644]
libc/src/signal/sigaddset.h [new file with mode: 0644]
libc/src/signal/sigemptyset.h [new file with mode: 0644]
libc/src/signal/sigprocmask.h [new file with mode: 0644]
libc/test/src/signal/CMakeLists.txt
libc/test/src/signal/raise_test.cpp
libc/test/src/signal/sigaddset_test.cpp [new file with mode: 0644]
libc/test/src/signal/sigprocmask_test.cpp [new file with mode: 0644]

index ebb4883..3ed556a 100644 (file)
@@ -135,12 +135,11 @@ def SysMManAPI : PublicAPI<"sys/mman.h"> {
 }
 
 def SignalAPI : PublicAPI<"signal.h"> {
-  let TypeDeclarations = [
-    SizeT, // This is needed by <linux/signal.h>.
-  ];
-
   let Functions = [
     "raise",
+    "sigprocmask",
+    "sigemptyset",
+    "sigaddset",
   ];
 }
 
index 9e42095..a8d422e 100644 (file)
 
 #include <__llvm-libc-common.h>
 
-%%public_api()
+#define __need_size_t
+#include <stddef.h>
 
 %%include_file(${platform_signal})
 
+%%public_api()
+
 #endif // LLVM_LIBC_SIGNAL_H
index cccb793..b4ab410 100644 (file)
@@ -15,6 +15,9 @@ add_entrypoint_library(
 
     # signal.h entrypoints
     raise
+    sigaddset
+    sigemptyset
+    sigprocmask
 )
 
 add_entrypoint_library(
index b2ad036..edd670a 100644 (file)
@@ -1,3 +1,9 @@
+def SigSetType : NamedType<"sigset_t">;
+def SigSetPtrType : PtrType<SigSetType>;
+def ConstSigSetPtrType : ConstType<SigSetPtrType>;
+def RestrictSigSetType : RestrictedPtrType<SigSetType>;
+def ConstRestrictSigSetType : ConstType<RestrictSigSetType>;
+
 def POSIX : StandardSpec<"POSIX"> {
   NamedType OffTType = NamedType<"off_t">;
 
@@ -128,8 +134,33 @@ def POSIX : StandardSpec<"POSIX"> {
       ]
   >;
 
+  HeaderSpec Signal = HeaderSpec<
+      "signal.h",
+      [], // Macros
+      [], // Types
+      [], // Enumerations
+      [
+        FunctionSpec<
+          "sigprocmask",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>, ArgSpec<ConstRestrictSigSetType>, ArgSpec<RestrictSigSetType>]
+        >,
+        FunctionSpec<
+          "sigemptyset",
+          RetValSpec<IntType>,
+          [ArgSpec<SigSetPtrType>]
+        >,
+        FunctionSpec<
+          "sigaddset",
+          RetValSpec<IntType>,
+          [ArgSpec<SigSetPtrType>]
+        >,
+      ]
+  >;
+
   let Headers = [
     Errno,
     SysMMan,
+    Signal,
   ];
 }
index a475a58..53bf5fc 100644 (file)
@@ -9,6 +9,43 @@ add_entrypoint_object(
   DEPENDS
     sys_syscall_h
     linux_syscall_h
+    signal_h
+)
+
+add_entrypoint_object(
+  sigprocmask
+  SRCS
+    sigprocmask.cpp
+  HDRS
+    signal.h
+    ../sigprocmask.h
+  DEPENDS
+    sys_syscall_h
+    linux_syscall_h
+    __errno_location
+    signal_h
+)
+
+add_entrypoint_object(
+  sigemptyset
+  SRCS
+    sigemptyset.cpp
+  HDRS
+    signal.h
+    ../sigemptyset.h
+  DEPENDS
+    __errno_location
+    signal_h
+)
+
+add_entrypoint_object(
+  sigaddset
+  SRCS
+    sigaddset.cpp
+  HDRS
+    signal.h
+    ../sigaddset.h
+  DEPENDS
     __errno_location
     signal_h
 )
diff --git a/libc/src/signal/linux/sigaddset.cpp b/libc/src/signal/linux/sigaddset.cpp
new file mode 100644 (file)
index 0000000..bd6c02d
--- /dev/null
@@ -0,0 +1,28 @@
+//===----------------- Linux implementation of sigaddset ------------------===//
+//
+// 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/sigaddset.h"
+#include "include/errno.h" // For E* macros.
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/linux/signal.h"
+
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(sigaddset)(sigset_t *set, int signum) {
+  if (!set || (unsigned)(signum - 1) >= (8 * sizeof(sigset_t))) {
+    llvmlibc_errno = EINVAL;
+    return -1;
+  }
+  auto *sigset = reinterpret_cast<__llvm_libc::Sigset *>(set);
+  sigset->addset(signum);
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/signal/linux/sigemptyset.cpp b/libc/src/signal/linux/sigemptyset.cpp
new file mode 100644 (file)
index 0000000..6114900
--- /dev/null
@@ -0,0 +1,27 @@
+//===--------------- Linux implementation of sigemptyset ------------------===//
+//
+// 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/sigemptyset.h"
+#include "include/errno.h" // For E* macros.
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/linux/signal.h"
+
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(sigemptyset)(sigset_t *set) {
+  if (!set) {
+    llvmlibc_errno = EINVAL;
+    return -1;
+  }
+  *set = __llvm_libc::Sigset::emptySet();
+  return 0;
+}
+
+} // namespace __llvm_libc
index f70185e..93b3359 100644 (file)
@@ -24,6 +24,11 @@ struct Sigset {
   sigset_t nativeSigset;
 
   constexpr static Sigset fullset() { return {-1UL}; }
+  constexpr static Sigset emptySet() { return {0}; }
+
+  constexpr void addset(int signal) {
+    nativeSigset |= (1L << (signal - 1));
+  }
 
   operator sigset_t() const { return nativeSigset; }
 };
@@ -33,8 +38,10 @@ constexpr static Sigset all = Sigset::fullset();
 static inline int block_all_signals(Sigset &set) {
   sigset_t nativeSigset = all;
   sigset_t oldSet = set;
-  return __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_BLOCK, &nativeSigset,
+  int ret = __llvm_libc::syscall(SYS_rt_sigprocmask, SIG_BLOCK, &nativeSigset,
                               &oldSet, sizeof(sigset_t));
+  set = {oldSet};
+  return ret;
 }
 
 static inline int restore_signals(const Sigset &set) {
diff --git a/libc/src/signal/linux/sigprocmask.cpp b/libc/src/signal/linux/sigprocmask.cpp
new file mode 100644 (file)
index 0000000..18f238b
--- /dev/null
@@ -0,0 +1,28 @@
+//===--------------- Linux implementation of sigprocmask ------------------===//
+//
+// 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/sigprocmask.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/linux/signal.h"
+
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+int LLVM_LIBC_ENTRYPOINT(sigprocmask)(int how, const sigset_t *__restrict set,
+                                      sigset_t *__restrict oldset) {
+  int ret = __llvm_libc::syscall(SYS_rt_sigprocmask, how, set, oldset,
+                                 sizeof(sigset_t));
+  if (!ret)
+    return 0;
+
+  llvmlibc_errno = -ret;
+  return -1;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/signal/sigaddset.h b/libc/src/signal/sigaddset.h
new file mode 100644 (file)
index 0000000..d25e6dc
--- /dev/null
@@ -0,0 +1,20 @@
+//===------------- Implementation header for sigaddset ---------*- 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_SIGNAL_SIGEADDSET_H
+#define LLVM_LIBC_SRC_SIGNAL_SIGEADDSET_H
+
+#include "include/signal.h"
+
+namespace __llvm_libc {
+
+int sigaddset(sigset_t *set, int signum);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SIGNAL_SIGEADDSET_H
diff --git a/libc/src/signal/sigemptyset.h b/libc/src/signal/sigemptyset.h
new file mode 100644 (file)
index 0000000..6f99163
--- /dev/null
@@ -0,0 +1,20 @@
+//===------------ Implementation header for sigemptyset --------*- 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_SIGNAL_SIGEMPTYSET_H
+#define LLVM_LIBC_SRC_SIGNAL_SIGEMPTYSET_H
+
+#include "include/signal.h"
+
+namespace __llvm_libc {
+
+int sigemptyset(sigset_t *set);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SIGNAL_SIGEMPTYSET_H
diff --git a/libc/src/signal/sigprocmask.h b/libc/src/signal/sigprocmask.h
new file mode 100644 (file)
index 0000000..0e959ca
--- /dev/null
@@ -0,0 +1,21 @@
+//===------------ Implementation header for sigprocmask --------*- 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_SIGNAL_SIGPROCMASK_H
+#define LLVM_LIBC_SRC_SIGNAL_SIGPROCMASK_H
+
+#include "include/signal.h"
+
+namespace __llvm_libc {
+
+int sigprocmask(int how, const sigset_t *__restrict set,
+                sigset_t *__restrict oldset);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SIGNAL_SIGPROCMASK_H
index dd1cc3b..38f6961 100644 (file)
@@ -10,3 +10,30 @@ add_libc_unittest(
     raise
     signal_h
 )
+
+add_libc_unittest(
+  sigprocmask_test
+  SUITE
+    libc_signal_unittests
+  SRCS
+    sigprocmask_test.cpp
+  DEPENDS
+    raise
+    sigprocmask
+    sigaddset
+    sigemptyset
+    signal_h
+    __errno_location
+)
+
+add_libc_unittest(
+  sigaddset_test
+  SUITE
+    libc_signal_unittests
+  SRCS
+    sigaddset_test.cpp
+  DEPENDS
+    sigaddset
+    signal_h
+    __errno_location
+)
index 882fee4..eea890a 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "include/signal.h"
 #include "src/signal/raise.h"
+
 #include "utils/UnitTest/Test.h"
 
 TEST(SignalTest, Raise) {
diff --git a/libc/test/src/signal/sigaddset_test.cpp b/libc/test/src/signal/sigaddset_test.cpp
new file mode 100644 (file)
index 0000000..58df1fe
--- /dev/null
@@ -0,0 +1,42 @@
+//===----------------------- Unittests for sigaddset ----------------------===//
+//
+// 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 "include/errno.h"
+#include "include/signal.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/sigaddset.h"
+
+#include "utils/UnitTest/Test.h"
+
+// This tests invalid inputs and ensures errno is properly set.
+TEST(SignalTest, SigaddsetInvalid) {
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigaddset(nullptr, SIGSEGV), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  sigset_t sigset;
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigaddset(&sigset, -1), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  // This doesn't use NSIG because __llvm_libc::sigaddset error checking is
+  // against sizeof(sigset_t) not NSIG.
+  constexpr int bitsInSigsetT = 8 * sizeof(sigset_t);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigaddset(&sigset, bitsInSigsetT + 1), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigaddset(&sigset, 0), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigaddset(&sigset, bitsInSigsetT), 0);
+  EXPECT_EQ(llvmlibc_errno, 0);
+}
diff --git a/libc/test/src/signal/sigprocmask_test.cpp b/libc/test/src/signal/sigprocmask_test.cpp
new file mode 100644 (file)
index 0000000..a0918d1
--- /dev/null
@@ -0,0 +1,64 @@
+//===--------------------- Unittests for sigprocmask ----------------------===//
+//
+// 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 "include/errno.h"
+#include "include/signal.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/raise.h"
+#include "src/signal/sigaddset.h"
+#include "src/signal/sigemptyset.h"
+#include "src/signal/sigprocmask.h"
+
+#include "utils/UnitTest/Test.h"
+
+class SignalTest : public __llvm_libc::testing::Test {
+  sigset_t oldSet;
+
+public:
+  void SetUp() override { __llvm_libc::sigprocmask(0, nullptr, &oldSet); }
+
+  void TearDown() override {
+    __llvm_libc::sigprocmask(SIG_SETMASK, &oldSet, nullptr);
+  }
+};
+
+// This tests for invalid input.
+TEST_F(SignalTest, SigprocmaskInvalid) {
+  sigset_t valid;
+  // 17 and -4 are out of the range for sigprocmask's how paramater.
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigprocmask(17, &valid, nullptr), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigprocmask(-4, &valid, nullptr), -1);
+  EXPECT_EQ(llvmlibc_errno, EINVAL);
+
+  // This pointer is out of this processes address range.
+  sigset_t *invalid = reinterpret_cast<sigset_t *>(-1);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, invalid, nullptr), -1);
+  EXPECT_EQ(llvmlibc_errno, EFAULT);
+
+  llvmlibc_errno = 0;
+  EXPECT_EQ(__llvm_libc::sigprocmask(-4, nullptr, invalid), -1);
+  EXPECT_EQ(llvmlibc_errno, EFAULT);
+}
+
+// This tests that when nothing is blocked, a process gets killed and alse tests
+// that when signals are blocked they are not delivered to the process.
+TEST_F(SignalTest, BlockUnblock) {
+  sigset_t sigset;
+  EXPECT_EQ(__llvm_libc::sigemptyset(&sigset), 0);
+  EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, &sigset, nullptr), 0);
+  EXPECT_DEATH([] { __llvm_libc::raise(SIGUSR1); }, SIGUSR1);
+  EXPECT_EQ(__llvm_libc::sigaddset(&sigset, SIGUSR1), 0);
+  EXPECT_EQ(__llvm_libc::sigprocmask(SIG_SETMASK, &sigset, nullptr), 0);
+  EXPECT_EXITS([] { __llvm_libc::raise(SIGUSR1); }, 0);
+}