On Wed, 10 Jun 2026 at 17:46, Helge Deller <[email protected]> wrote:
>
> From: Matt Turner <[email protected]>
>
> 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 <[email protected]>
> Cc: Mark Cave-Ayland <[email protected]>
> Signed-off-by: Helge Deller <[email protected]>
> ---
>  linux-user/sparc/signal.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c
> index fda5508c48..ba692c3123 100644
> --- a/linux-user/sparc/signal.c
> +++ b/linux-user/sparc/signal.c
> @@ -619,6 +619,15 @@ void sparc64_set_context(CPUSPARCState *env)
>              }
>          }
>          target_to_host_sigset_internal(&set, &target_set);
> +        /*
> +         * set_sigmask() requires the caller to have first called
> +         * block_signals() so that process_pending_signals() is guaranteed
> +         * to run after the mask change.  Without this, a guest signal that
> +         * is pending-and-blocked at setcontext time is left undelivered
> +         * even after its mask bit is cleared, because signal_pending stays
> +         * 0 and the post-trap process_pending_signals() loop never enters.
> +         */
> +        block_signals();
>          set_sigmask(&set);

Hi; Coverity points out (CID 1660058) that every other callsite of
block_signals() checks its return value and does something if it
returns non-zero (indicating that a signal is pending and typically
that we should return -TARGET_ERESTARTSYS to take the pending signal).
Is it OK to ignore the return value here? If so, saying why in
the comment would probably be useful.

thanks
-- PMM

Reply via email to