Commit e0f0ce88eb for qemu.org
commit e0f0ce88eb9e526cf8271f124478143fc3ae388a
Author: Matt Turner <mattst88@gmail.com>
Date: Wed Jun 3 12:18:51 2026 -0400
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>
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);
}
env->pc = pc;