From 7de2f229a7e9f19074342010d57155d4fa4cc451 Mon Sep 17 00:00:00 2001 From: Gavin Hayes Date: Mon, 5 Sep 2022 12:17:56 -0400 Subject: [PATCH] Discard ignored signals on New Technology (#592) --- libc/calls/sig.internal.h | 1 + libc/calls/sig2.c | 37 +++++++++++++++++++++++++++++++- libc/calls/sigaction.c | 1 + test/libc/calls/sigaction_test.c | 19 ++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 0a42d2987..f988703f4 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -31,6 +31,7 @@ bool __sig_handle(bool, int, int, ucontext_t *) hidden; 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; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index 9ef354223..13f33053a 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -246,7 +246,7 @@ textwindows int __sig_add(int sig, int si_code) { } /** - * Enqueues generic signal for delivery on New Technology. + * Checks for unblocked signals and delivers them on New Technology. * * @param restartable is for functions like read() but not poll() * @return true if EINTR should be returned by caller @@ -264,3 +264,38 @@ textwindows bool __sig_check(bool restartable) { } return delivered; } + +/** + * Determines if a signal should be ignored and if so discards existing + * instances of said signal on New Technology. + * + * Even blocked signals are discarded. + * + * @param sig the signal number to remove + * @threadsafe + */ +textwindows void __sig_check_ignore(const int sig, const unsigned rva) { + struct Signal *cur, *prev, *next; + if (rva != (unsigned)(intptr_t)SIG_IGN && + (rva != (unsigned)(intptr_t)SIG_DFL || __sig_isfatal(sig))) { + return; + } + if (__sig.queue) { + __sig_lock(); + for (prev = 0, cur = __sig.queue; cur; cur = next) { + next = cur->next; + if (sig == cur->sig) { + if (cur == __sig.queue) { + __sig.queue = cur->next; + } else if (prev) { + prev->next = cur->next; + } + __sig_handle(false, cur->sig, cur->si_code, 0); + __sig_free(cur); + } else { + prev = cur; + } + } + __sig_unlock(); + } +} diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 27cd0f162..b760356db 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -258,6 +258,7 @@ static int __sigaction(int sig, const struct sigaction *act, if (act) { __sighandrvas[sig] = rva; __sighandflags[sig] = act->sa_flags; + __sig_check_ignore(sig, rva); } } return rc; diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 8beb9e316..b5d4f79db 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -156,3 +156,22 @@ noubsan void ubsanTrumpsSystemsEngineering(void) { TEST(sigaction, sigFpe_handlerCanEditProcessStateAndRecoverExecution) { ubsanTrumpsSystemsEngineering(); } + +static unsigned OnSignalCnt = 0; +void OnSignal(int sig, siginfo_t *si, void *ctx) { + OnSignalCnt++; +} + +TEST(sigaction, ignoringSignalDiscardsSignal) { + 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)); + ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_IGN)); + ASSERT_EQ(0, sigaction(SIGUSR1, &sa, NULL)); + ASSERT_EQ(0, sigprocmask(SIG_UNBLOCK, &blocked, NULL)); + EXPECT_EQ(0, OnSignalCnt); +}