From edbffb3c7a60cdcdddffe81555c142868f4dc10e Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Thu, 30 Jun 2022 13:36:03 +0200 Subject: [PATCH] Kernel: Unblock SignalBlocker if a signal was just unmarked as pending When updating the signal mask, there is a small frame where we might set up the receiving process for handing the signal and therefore remove that signal from the list of pending signals before SignalBlocker has a chance to block. In turn, this might cause SignalBlocker to never notice that the signal arrives and it will never unblock once blocked. Track the currently handled signal separately and include it when determining if SignalBlocker should be unblocking. --- Kernel/Syscalls/sigaction.cpp | 1 + Kernel/Thread.cpp | 2 ++ Kernel/Thread.h | 1 + Kernel/ThreadBlockers.cpp | 10 +++++++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp index c5a94d27c134ff..2226e816bd00c9 100644 --- a/Kernel/Syscalls/sigaction.cpp +++ b/Kernel/Syscalls/sigaction.cpp @@ -104,6 +104,7 @@ ErrorOr Process::sys$sigreturn([[maybe_unused]] RegisterState& register auto saved_ax = TRY(copy_typed_from_user(stack_ptr)); Thread::current()->m_signal_mask = ucontext.uc_sigmask; + Thread::current()->m_currently_handled_signal = 0; #if ARCH(X86_64) auto sp = registers.rsp; #elif ARCH(I386) diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 5c1306b477766c..2d84d761af9b2e 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -1052,6 +1052,8 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal) ScopedAddressSpaceSwitcher switcher(m_process); + m_currently_handled_signal = signal; + u32 old_signal_mask = m_signal_mask; u32 new_signal_mask = m_signal_action_masks[signal].value_or(action.mask); if ((action.flags & SA_NODEFER) == SA_NODEFER) diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 42de69c8ab65b7..b94bb226615ed1 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -1210,6 +1210,7 @@ class Thread u32 m_ticks_in_user { 0 }; u32 m_ticks_in_kernel { 0 }; u32 m_pending_signals { 0 }; + u8 m_currently_handled_signal { 0 }; u32 m_signal_mask { 0 }; FlatPtr m_alternative_signal_stack { 0 }; FlatPtr m_alternative_signal_stack_size { 0 }; diff --git a/Kernel/ThreadBlockers.cpp b/Kernel/ThreadBlockers.cpp index e05a94fed1d0aa..5ef39243152d4c 100644 --- a/Kernel/ThreadBlockers.cpp +++ b/Kernel/ThreadBlockers.cpp @@ -474,7 +474,15 @@ bool Thread::SignalBlocker::check_pending_signals(bool from_add_blocker) if (m_did_unblock) return false; - auto matching_pending_signal = bit_scan_forward(thread().pending_signals() & m_pending_set); + auto pending_signals = thread().pending_signals() & m_pending_set; + + // Also unblock if we have just "handled" that signal and are in the procecss + // of running their signal handler (i.e. we just unmarked the signal as pending). + if (thread().m_currently_handled_signal) + pending_signals |= (1 << (thread().m_currently_handled_signal - 1)) & m_pending_set; + + auto matching_pending_signal = bit_scan_forward(pending_signals); + if (matching_pending_signal == 0) return false;