@samitouri / QOSamiQemu / commits / e0f0ce88eb

linux-user/sparc: call block_signals() before set_sigmask() in setcontext

sparc64_set_context() emulates the kernel's `ta 0x6f` trap by calling set_sigmask() to install the mask supplied via the user's ucontext_t. The contract of set_sigmask() (see its comment in linux-user/signal.c) is that the caller must have first called block_signals(), which sets TaskState::signal_pending. Without block_signals(), if a guest signal is pending-and-blocked at the time setcontext is invoked and the new mask unblocks it, signal_pending stays 0 and the post-trap process_pending_signals() call in linux-user/sparc/cpu_loop.c never enters its while loop, so the now-deliverable signal is left undelivered indefinitely. This affects programs that use getcontext/setcontext to swap signal masks, including libunwind's unw_resume() out of a signal handler: without this fix, the test program below loops forever printing "calling setcontext" instead of delivering the pending SIGUSR2. #define _GNU_SOURCE #include <ucontext.h> #include <signal.h> #include <stdio.h> #include <unistd.h> static int got; static void h(int s) { got = 1; } int main(void) { signal(SIGUSR2, h); sigset_t m; sigemptyset(&m); sigaddset(&m, SIGUSR2); sigprocmask(SIG_BLOCK, &m, NULL); kill(getpid(), SIGUSR2); ucontext_t uc; getcontext(&uc); if (got) return 0; uc.uc_sigmask.__val[0] = 0; setcontext(&uc); return 1; } The 32-bit sparc do_sigreturn / do_rt_sigreturn paths already get block_signals() from the rt_sigreturn syscall wrapper in linux-user/syscall.c, so only sparc64_set_context (invoked directly from cpu_loop) needs the addition. Signed-off-by: Matt Turner <mattst88@gmail.com> Cc: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Helge Deller <deller@gmx.de>

Matt Turner committed Jun 3, 2026 at 12:18 UTC e0f0ce88eb9e526cf8271f124478143fc3ae388a
1 file changed +9
linux-user/sparc/signal.c
+9
@@ -619,6 +619,15 @@ void sparc64_set_context(CPUSPARCState *env)
619 }
620 }
621 target_to_host_sigset_internal(&set, &target_set);
622 + /*
623 + * set_sigmask() requires the caller to have first called
624 + * block_signals() so that process_pending_signals() is guaranteed
625 + * to run after the mask change. Without this, a guest signal that
626 + * is pending-and-blocked at setcontext time is left undelivered
627 + * even after its mask bit is cleared, because signal_pending stays
628 + * 0 and the post-trap process_pending_signals() loop never enters.
629 + */
630 + block_signals();
631 set_sigmask(&set);
632 }
633 env->pc = pc;