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 @@ -135,12 +135,11 @@ } def SignalAPI : PublicAPI<"signal.h"> { - let TypeDeclarations = [ - SizeT, // This is needed by . - ]; - let Functions = [ "raise", + "sigprocmask", + "sigemptyset", + "sigaddset", ]; } diff --git a/libc/include/signal.h.def b/libc/include/signal.h.def --- a/libc/include/signal.h.def +++ b/libc/include/signal.h.def @@ -11,8 +11,11 @@ #include <__llvm-libc-common.h> -%%public_api() +#define __need_size_t +#include %%include_file(${platform_signal}) +%%public_api() + #endif // LLVM_LIBC_SIGNAL_H diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -15,6 +15,9 @@ # signal.h entrypoints raise + sigaddset + sigemptyset + sigprocmask ) add_entrypoint_library( diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -1,3 +1,9 @@ +def SigSetType : NamedType<"sigset_t">; +def SigSetPtrType : PtrType; +def ConstSigSetPtrType : ConstType; +def RestrictSigSetType : RestrictedPtrType; +def ConstRestrictSigSetType : ConstType; + def POSIX : StandardSpec<"POSIX"> { NamedType OffTType = NamedType<"off_t">; @@ -128,8 +134,33 @@ ] >; + HeaderSpec Signal = HeaderSpec< + "signal.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "sigprocmask", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, + FunctionSpec< + "sigemptyset", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "sigaddset", + RetValSpec, + [ArgSpec] + >, + ] + >; + let Headers = [ Errno, SysMMan, + Signal, ]; } 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 @@ -9,6 +9,43 @@ 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 --- /dev/null +++ b/libc/src/signal/linux/sigaddset.cpp @@ -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 --- /dev/null +++ b/libc/src/signal/linux/sigemptyset.cpp @@ -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 diff --git a/libc/src/signal/linux/signal.h b/libc/src/signal/linux/signal.h --- a/libc/src/signal/linux/signal.h +++ b/libc/src/signal/linux/signal.h @@ -24,6 +24,11 @@ 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 @@ 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 --- /dev/null +++ b/libc/src/signal/linux/sigprocmask.cpp @@ -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 --- /dev/null +++ b/libc/src/signal/sigaddset.h @@ -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 --- /dev/null +++ b/libc/src/signal/sigemptyset.h @@ -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 --- /dev/null +++ b/libc/src/signal/sigprocmask.h @@ -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 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 @@ -10,3 +10,30 @@ 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 +) diff --git a/libc/test/src/signal/raise_test.cpp b/libc/test/src/signal/raise_test.cpp --- a/libc/test/src/signal/raise_test.cpp +++ b/libc/test/src/signal/raise_test.cpp @@ -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 --- /dev/null +++ b/libc/test/src/signal/sigaddset_test.cpp @@ -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 --- /dev/null +++ b/libc/test/src/signal/sigprocmask_test.cpp @@ -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(-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); +}