From a849a6377130a4ab63ae994f05a833637b7d0082 Mon Sep 17 00:00:00 2001 From: Gavin Hayes Date: Wed, 7 Sep 2022 08:38:12 -0400 Subject: [PATCH] Implement sigpending for sysv and nt (#597) --- libc/calls/sig.internal.h | 1 + libc/calls/sig2.c | 22 +++++++ libc/calls/sigpending.c | 61 ++++++++++++++++++ libc/calls/struct/sigset.h | 1 + libc/calls/struct/sigset.internal.h | 1 + libc/sysv/calls/sigpending.s | 2 - libc/sysv/calls/sys_sigpending.s | 2 + libc/sysv/syscalls.sh | 2 +- test/libc/calls/sigpending_test.c | 97 +++++++++++++++++++++++++++++ 9 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 libc/calls/sigpending.c delete mode 100644 libc/sysv/calls/sigpending.s create mode 100644 libc/sysv/calls/sys_sigpending.s create mode 100644 test/libc/calls/sigpending_test.c diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index f988703f4..7a53dd05f 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -32,6 +32,7 @@ int __sig_add(int, int) hidden; int __sig_mask(int, const sigset_t *, sigset_t *) hidden; int __sig_raise(int, int) hidden; void __sig_check_ignore(const int, const unsigned) hidden; +void __sig_pending(sigset_t *) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index af3db3ff7..606514086 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -298,3 +298,25 @@ textwindows void __sig_check_ignore(const int sig, const unsigned rva) { __sig_unlock(); } } + +/** + * Determines the pending signals on New Technology. + * + * @param pending is to hold the pending signals + * @threadsafe + */ +textwindows void __sig_pending(sigset_t *pending) { + struct Signal *cur; + sigemptyset(pending); + if (__sig.queue) { + __sig_lock(); + for (cur = __sig.queue; cur; cur = cur->next) { + if (__sighandrvas[cur->sig] != (unsigned)(intptr_t)SIG_IGN) { + pending->__bits[(cur->sig - 1) >> 6] |= (1ull << ((cur->sig - 1) & 63)); + } else { + STRACE("%G is ignored", cur->sig); + } + } + __sig_unlock(); + } +} diff --git a/libc/calls/sigpending.c b/libc/calls/sigpending.c new file mode 100644 index 000000000..c25a671e8 --- /dev/null +++ b/libc/calls/sigpending.c @@ -0,0 +1,61 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/sig.internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/sigset.internal.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/sysv/errfuns.h" + +/** + * Determines the blocked pending signals + * + * @param pending is where the bitset of pending signals is returned, + * may not be NULL. + * @return -1 w/ EFAULT + * @asyncsignalsafe + */ +int sigpending(sigset_t *pending) { + int rc; + if (IsAsan() && !__asan_is_valid(pending, sizeof(*pending))) { + rc = efault(); + } else if (IsLinux() || IsNetbsd() || IsOpenbsd() || IsFreebsd() || IsXnu()) { + rc = sys_sigpending(pending, 8); + // 128 signals on NetBSD and FreeBSD, 64 on Linux, 32 on OpenBSD and XNU. + if (IsOpenbsd()) { + pending->__bits[0] = (unsigned)rc; + rc = 0; + } else if (IsXnu()) { + pending->__bits[0] &= 0xFFFFFFFF; + } + if (IsLinux() || IsOpenbsd() || IsXnu()) { + pending->__bits[1] = 0; + } + } else if (IsWindows()) { + sigemptyset(pending); + __sig_pending(pending); + rc = 0; + } else { + rc = enosys(); + } + STRACE("sigpending([%s]) → %d% m", DescribeSigset(rc, pending), rc); + return rc; +} diff --git a/libc/calls/struct/sigset.h b/libc/calls/struct/sigset.h index de3887938..f3ac87163 100644 --- a/libc/calls/struct/sigset.h +++ b/libc/calls/struct/sigset.h @@ -14,6 +14,7 @@ int sigfillset(sigset_t *) paramsnonnull(); int sigismember(const sigset_t *, int) paramsnonnull() nosideeffect; int sigprocmask(int, const sigset_t *, sigset_t *); int sigsuspend(const sigset_t *); +int sigpending(sigset_t *) paramsnonnull() nosideeffect; int pthread_sigmask(int, const sigset_t *, sigset_t *); COSMOPOLITAN_C_END_ diff --git a/libc/calls/struct/sigset.internal.h b/libc/calls/struct/sigset.internal.h index 1d7545b5e..5b145056c 100644 --- a/libc/calls/struct/sigset.internal.h +++ b/libc/calls/struct/sigset.internal.h @@ -9,6 +9,7 @@ int __sys_sigprocmask(int, const struct sigset *, struct sigset *, uint64_t) hidden; int sys_sigprocmask(int, const struct sigset *, struct sigset *) hidden; int sys_sigsuspend(const struct sigset *, uint64_t) hidden; +int sys_sigpending(struct sigset *, size_t) hidden; const char *DescribeSigset(char[128], int, const sigset_t *); #define DescribeSigset(rc, ss) DescribeSigset(alloca(128), rc, ss) diff --git a/libc/sysv/calls/sigpending.s b/libc/sysv/calls/sigpending.s deleted file mode 100644 index e2f9ee248..000000000 --- a/libc/sysv/calls/sigpending.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sigpending,0x124034034203407f,globl diff --git a/libc/sysv/calls/sys_sigpending.s b/libc/sysv/calls/sys_sigpending.s new file mode 100644 index 000000000..5f1fecfb7 --- /dev/null +++ b/libc/sysv/calls/sys_sigpending.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_sigpending,0x124034157203407f,globl,hidden diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 3e9c39c27..2ef0cdb00 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -161,7 +161,7 @@ scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu scall sys_getresuid 0xfff119168ffff076 globl hidden # semantics aren't well-defined scall sys_getresgid 0xfff11b169ffff078 globl hidden # semantics aren't well-defined -scall sigpending 0x124034034203407f globl # a.k.a. rt_sigpending on linux +scall sys_sigpending 0x124034157203407f globl hidden # a.k.a. rt_sigpending on linux scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU scall sys_sigaltstack 0x1191200352035083 globl hidden scall sys_mknod 0x1c200e00e200e085 globl hidden diff --git a/test/libc/calls/sigpending_test.c b/test/libc/calls/sigpending_test.c new file mode 100644 index 000000000..851c46104 --- /dev/null +++ b/test/libc/calls/sigpending_test.c @@ -0,0 +1,97 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Gavin Arthur Hayes │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/testlib.h" + +static unsigned OnSignalCnt = 0; +void OnSignal(int sig, siginfo_t *si, void *ctx) { + OnSignalCnt++; + EXPECT_EQ(SIGUSR1, sig); +} + +TEST(sigpending, test) { + sigset_t pending; + sigfillset(&pending); + EXPECT_EQ(0, sigpending(&pending)); + unsigned cnt = 0; + for (unsigned sig = 1; sig < NSIG; sig++) { + if (sigismember(&pending, sig)) { + cnt++; + } + } + EXPECT_EQ(0, cnt); + + struct sigaction sa = {.sa_sigaction = OnSignal, .sa_flags = SA_SIGINFO}; + ASSERT_EQ(0, sigaction(SIGUSR1, &sa, NULL)); + sigset_t blocked; + sigemptyset(&blocked); + sigaddset(&blocked, SIGUSR1); + ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &blocked, NULL)); + ASSERT_EQ(0, raise(SIGUSR1)); + sigfillset(&pending); + EXPECT_EQ(0, sigpending(&pending)); + cnt = 0; + for (unsigned sig = 1; sig < NSIG; sig++) { + if (sigismember(&pending, sig)) { + cnt++; + } + } + EXPECT_EQ(1, cnt); + EXPECT_EQ(1, sigismember(&pending, SIGUSR1)); + + ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_IGN)); + sigemptyset(&pending); + EXPECT_EQ(0, sigpending(&pending)); + cnt = 0; + for (unsigned sig = 1; sig < NSIG; sig++) { + if (sigismember(&pending, sig)) { + cnt++; + } + } + EXPECT_EQ(0, cnt); + + ASSERT_EQ(0, sigaction(SIGUSR1, &sa, NULL)); + ASSERT_EQ(0, raise(SIGUSR1)); + EXPECT_EQ(0, sigpending(&pending)); + cnt = 0; + for (unsigned sig = 1; sig < NSIG; sig++) { + if (sigismember(&pending, sig)) { + cnt++; + } + } + EXPECT_EQ(1, cnt); + EXPECT_EQ(1, sigismember(&pending, SIGUSR1)); + + sigemptyset(&blocked); + ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &blocked, NULL)); + EXPECT_EQ(0, sigpending(&pending)); + cnt = 0; + for (unsigned sig = 1; sig < NSIG; sig++) { + if (sigismember(&pending, sig)) { + cnt++; + } + } + EXPECT_EQ(0, cnt); + EXPECT_EQ(1, OnSignalCnt); +}