diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -180,9 +180,28 @@ ]; } +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", diff --git a/libc/config/linux/signal.h.in b/libc/config/linux/signal.h.in --- a/libc/config/linux/signal.h.in +++ b/libc/config/linux/signal.h.in @@ -9,3 +9,7 @@ %%begin() #include + +#ifndef __LLVM_LIBC_INTERNAL_SIGACTION +#define sigaction __sigaction +#endif diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -18,6 +18,7 @@ # signal.h entrypoints raise + sigaction sigaddset sigemptyset sigprocmask diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -4,6 +4,12 @@ def RestrictSigSetType : RestrictedPtrType; def ConstRestrictSigSetType : ConstType; +def StructSigaction : NamedType<"struct sigaction">; +def StructSigactionPtr : PtrType; +def ConstStructSigactionPtr : ConstType; +def RestrictStructSigactionPtr : RestrictedPtrType; +def ConstRestrictStructSigactionPtr : ConstType; + def POSIX : StandardSpec<"POSIX"> { NamedType OffTType = NamedType<"off_t">; @@ -137,10 +143,20 @@ HeaderSpec Signal = HeaderSpec< "signal.h", [], // Macros - [], // Types + [ + SigSetType, + StructSigaction, + ], [], // Enumerations [ FunctionSpec< + "sigaction", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< "sigprocmask", RetValSpec, [ArgSpec, ArgSpec, ArgSpec] diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -225,7 +225,6 @@ Macro<"SIGTERM"> ], [ - NamedType<"sigset_t">, SizeTType, ], [], // Enumerations diff --git a/libc/src/signal/linux/CMakeLists.txt b/libc/src/signal/linux/CMakeLists.txt --- a/libc/src/signal/linux/CMakeLists.txt +++ b/libc/src/signal/linux/CMakeLists.txt @@ -12,6 +12,39 @@ 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 --- /dev/null +++ b/libc/src/signal/linux/__restore.cpp @@ -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 --- /dev/null +++ b/libc/src/signal/linux/sigaction.cpp @@ -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 +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 --- /dev/null +++ b/libc/src/signal/sigaction.h @@ -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 diff --git a/libc/test/src/signal/CMakeLists.txt b/libc/test/src/signal/CMakeLists.txt --- a/libc/test/src/signal/CMakeLists.txt +++ b/libc/test/src/signal/CMakeLists.txt @@ -12,6 +12,21 @@ ) 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 --- /dev/null +++ b/libc/test/src/signal/sigaction_test.cpp @@ -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); +}