[libc] Add sigaction
authorAlex Brachet <alexbrachetmialot@gmail.com>
Wed, 18 Mar 2020 05:08:59 +0000 (01:08 -0400)
committerAlex Brachet <alexbrachetmialot@gmail.com>
Wed, 18 Mar 2020 05:08:59 +0000 (01:08 -0400)
Summary: This patch adds `sigaction` and the `sa_restorer` signal trampoline function `__restore_rt`

Reviewers: sivachandra, MaskRay, PaulkaToast

Reviewed By: sivachandra

Subscribers: gchatelet, mgorny, tschuett, libc-commits

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

libc/config/linux/api.td
libc/config/linux/signal.h.in
libc/lib/CMakeLists.txt
libc/spec/posix.td
libc/spec/stdc.td
libc/src/signal/linux/CMakeLists.txt
libc/src/signal/linux/__restore.cpp [new file with mode: 0644]
libc/src/signal/linux/sigaction.cpp [new file with mode: 0644]
libc/src/signal/sigaction.h [new file with mode: 0644]
libc/test/src/signal/CMakeLists.txt
libc/test/src/signal/sigaction_test.cpp [new file with mode: 0644]

index aecdce2..e9391cb 100644 (file)
@@ -180,9 +180,28 @@ def SysMManAPI : PublicAPI<"sys/mman.h"> {
   ];
 }
 
+def StructSigactionDefn : TypeDecl<"struct sigaction"> {
+  let Decl = [{
+    struct __sigaction {
+      union {
+        void (*sa_handler)(int);
+        void (*sa_action)(int, siginfo_t *, void *);
+      };
+      sigset_t sa_mask;
+      int sa_flags;
+      void (*sa_restorer)(void);
+    };
+  }];
+}
+
 def SignalAPI : PublicAPI<"signal.h"> {
+  let TypeDeclarations = [
+    StructSigactionDefn,
+  ];
+
   let Functions = [
     "raise",
+    "sigaction",
     "sigprocmask",
     "sigemptyset",
     "sigaddset",
index 1c2a307..fc7ca9a 100644 (file)
@@ -9,3 +9,7 @@
 %%begin()
 
 #include <linux/signal.h>
+
+#ifndef __LLVM_LIBC_INTERNAL_SIGACTION
+#define sigaction __sigaction
+#endif
index 238bcb4..832d79c 100644 (file)
@@ -18,6 +18,7 @@ add_entrypoint_library(
 
     # signal.h entrypoints
     raise
+    sigaction
     sigaddset
     sigemptyset
     sigprocmask
index edd670a..172b0c3 100644 (file)
@@ -4,6 +4,12 @@ def ConstSigSetPtrType : ConstType<SigSetPtrType>;
 def RestrictSigSetType : RestrictedPtrType<SigSetType>;
 def ConstRestrictSigSetType : ConstType<RestrictSigSetType>;
 
+def StructSigaction : NamedType<"struct sigaction">;
+def StructSigactionPtr : PtrType<StructSigaction>;
+def ConstStructSigactionPtr : ConstType<StructSigactionPtr>;
+def RestrictStructSigactionPtr : RestrictedPtrType<StructSigaction>;
+def ConstRestrictStructSigactionPtr : ConstType<RestrictStructSigactionPtr>;
+
 def POSIX : StandardSpec<"POSIX"> {
   NamedType OffTType = NamedType<"off_t">;
 
@@ -137,10 +143,20 @@ def POSIX : StandardSpec<"POSIX"> {
   HeaderSpec Signal = HeaderSpec<
       "signal.h",
       [], // Macros
-      [], // Types
+      [
+        SigSetType,
+        StructSigaction,
+      ],
       [], // Enumerations
       [
         FunctionSpec<
+          "sigaction",
+          RetValSpec<IntType>,
+          [ArgSpec<IntType>,
+           ArgSpec<ConstRestrictStructSigactionPtr>,
+           ArgSpec<RestrictStructSigactionPtr>]
+        >,
+        FunctionSpec<
           "sigprocmask",
           RetValSpec<IntType>,
           [ArgSpec<IntType>, ArgSpec<ConstRestrictSigSetType>, ArgSpec<RestrictSigSetType>]
index 4df8550..dfac8eb 100644 (file)
@@ -225,7 +225,6 @@ def StdC : StandardSpec<"stdc"> {
         Macro<"SIGTERM">
       ],
       [
-        NamedType<"sigset_t">,
         SizeTType,
       ],
       [], // Enumerations
index 022f41b..1d59b75 100644 (file)
@@ -12,6 +12,39 @@ add_entrypoint_object(
     signal_h
 )
 
+add_object(
+  __restore
+  SRC
+    __restore.cpp
+  COMPILE_OPTIONS
+    -fomit-frame-pointer
+    -O3
+    -Wframe-larger-than=0
+    -Werror
+    -Wno-attributes
+    # asan creates asan.module_ctor which uses stack space, causing warinngs.
+    -fno-sanitize=address
+  DEPENDS
+    linux_syscall_h
+    sys_syscall_h
+)
+
+add_entrypoint_object(
+  sigaction
+  SRCS
+    sigaction.cpp
+  HDRS
+    signal.h
+    ../sigaction.h
+  DEPENDS
+    __restore
+    sys_syscall_h
+    linux_syscall_h
+    signal_h
+  SPECIAL_OBJECTS
+    __restore
+)
+
 add_entrypoint_object(
   sigprocmask
   SRCS
diff --git a/libc/src/signal/linux/__restore.cpp b/libc/src/signal/linux/__restore.cpp
new file mode 100644 (file)
index 0000000..bfdee4b
--- /dev/null
@@ -0,0 +1,20 @@
+//===----------------- Linux implementation of __restore_rt ---------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This file is implemented seperately from sigaction.cpp so that we can
+// strongly control the options this file is compiled with. __restore_rt cannot
+// make any stack allocations so we must ensure this.
+
+#include "config/linux/syscall.h"
+#include "include/sys/syscall.h"
+
+extern "C" void __restore_rt()
+    __attribute__((no_sanitize("thread", "memory", "undefined", "fuzzer"),
+                   hidden));
+
+extern "C" void __restore_rt() { __llvm_libc::syscall(SYS_rt_sigreturn); }
diff --git a/libc/src/signal/linux/sigaction.cpp b/libc/src/signal/linux/sigaction.cpp
new file mode 100644 (file)
index 0000000..18caeac
--- /dev/null
@@ -0,0 +1,56 @@
+//===----------------- Linux implementation of sigaction ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#define __LLVM_LIBC_INTERNAL_SIGACTION
+#include "src/signal/sigaction.h"
+#include "src/errno/llvmlibc_errno.h"
+#include "src/signal/linux/signal.h"
+
+#include "src/__support/common.h"
+
+namespace __llvm_libc {
+
+// TOOD: Some architectures will have their signal trampoline functions in the
+// vdso, use those when available.
+
+extern "C" void __restore_rt();
+
+template <typename T, typename V>
+static void copySigaction(T &dest, const V &source) {
+  dest.sa_handler = source.sa_handler;
+  dest.sa_mask = source.sa_mask;
+  dest.sa_flags = source.sa_flags;
+  dest.sa_restorer = source.sa_restorer;
+}
+
+int LLVM_LIBC_ENTRYPOINT(sigaction)(
+    int signal, const struct __sigaction *__restrict libc_new,
+    struct __sigaction *__restrict libc_old) {
+  struct sigaction kernel_new;
+  if (libc_new) {
+    copySigaction(kernel_new, *libc_new);
+    if (!(kernel_new.sa_flags & SA_RESTORER)) {
+      kernel_new.sa_flags |= SA_RESTORER;
+      kernel_new.sa_restorer = __restore_rt;
+    }
+  }
+
+  struct sigaction kernel_old;
+  int ret = syscall(SYS_rt_sigaction, signal, libc_new ? &kernel_new : nullptr,
+                    libc_old ? &kernel_old : nullptr, sizeof(sigset_t));
+  if (ret) {
+    llvmlibc_errno = -ret;
+    return -1;
+  }
+
+  if (libc_old)
+    copySigaction(*libc_old, kernel_old);
+  return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/signal/sigaction.h b/libc/src/signal/sigaction.h
new file mode 100644 (file)
index 0000000..c2e8f4b
--- /dev/null
@@ -0,0 +1,22 @@
+//===------------ Implementation header for sigaction --------*- 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_SIGACTION_H
+#define LLVM_LIBC_SRC_SIGNAL_SIGACTION_H
+
+#define __LLVM_LIBC_INTERNAL_SIGACTION
+#include "include/signal.h"
+
+namespace __llvm_libc {
+
+int sigaction(int signal, const struct __sigaction *__restrict libc_new,
+              struct __sigaction *__restrict libc_old);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SIGNAL_SIGACTION_H
index 38f6961..db919b6 100644 (file)
@@ -12,6 +12,21 @@ add_libc_unittest(
 )
 
 add_libc_unittest(
+  sigaction_test
+  SUITE
+    libc_signal_unittests
+  SRCS
+    sigaction_test.cpp
+  DEPENDS
+    sigaction
+    raise
+    signal_h
+    errno_h
+    __errno_location
+    __restore
+)
+
+add_libc_unittest(
   sigprocmask_test
   SUITE
     libc_signal_unittests
diff --git a/libc/test/src/signal/sigaction_test.cpp b/libc/test/src/signal/sigaction_test.cpp
new file mode 100644 (file)
index 0000000..fc7d143
--- /dev/null
@@ -0,0 +1,66 @@
+//===----------------------- Unittests for sigaction ----------------------===//
+//
+// 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"
+#define __LLVM_LIBC_INTERNAL_SIGACTION
+#include "include/signal.h"
+#include "src/signal/raise.h"
+#include "src/signal/sigaction.h"
+
+#include "utils/UnitTest/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+
+using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
+
+TEST(Sigaction, Invalid) {
+  // -1 is a much larger signal that NSIG, so this should fail.
+  EXPECT_THAT(__llvm_libc::sigaction(-1, nullptr, nullptr), Fails(EINVAL));
+}
+
+// SIGKILL cannot have its action changed, but it can be examined.
+TEST(Sigaction, Sigkill) {
+  struct __sigaction action;
+  EXPECT_THAT(__llvm_libc::sigaction(SIGKILL, nullptr, &action), Succeeds());
+  EXPECT_THAT(__llvm_libc::sigaction(SIGKILL, &action, nullptr), Fails(EINVAL));
+}
+
+static int sigusr1Count;
+static bool correctSignal;
+
+TEST(Sigaction, CustomAction) {
+  // Zero this incase tests get run multiple times in the future.
+  sigusr1Count = 0;
+
+  struct __sigaction action;
+  EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, nullptr, &action), Succeeds());
+
+  action.sa_handler = +[](int signal) {
+    correctSignal = signal == SIGUSR1;
+    sigusr1Count++;
+  };
+  EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds());
+
+  __llvm_libc::raise(SIGUSR1);
+  EXPECT_EQ(sigusr1Count, 1);
+  EXPECT_TRUE(correctSignal);
+
+  action.sa_handler = SIG_DFL;
+  EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds());
+
+  EXPECT_DEATH([] { __llvm_libc::raise(SIGUSR1); }, SIGUSR1);
+}
+
+TEST(Sigaction, Ignore) {
+  struct __sigaction action;
+  EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, nullptr, &action), Succeeds());
+  action.sa_handler = SIG_IGN;
+  EXPECT_THAT(__llvm_libc::sigaction(SIGUSR1, &action, nullptr), Succeeds());
+
+  EXPECT_EXITS([] { __llvm_libc::raise(SIGUSR1); }, 0);
+}