[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 18/30] bsd-user/signal.c: Implement host_signal_handler
From: |
Warner Losh |
Subject: |
[PATCH 18/30] bsd-user/signal.c: Implement host_signal_handler |
Date: |
Sun, 9 Jan 2022 09:19:11 -0700 |
Implement host_signal_handler to handle signals generated by the host
and to do safe system calls.
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@freebsd.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
---
bsd-user/signal.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index b1331f63d61..a6e07277fb2 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -142,6 +142,111 @@ void force_sig_fault(int sig, int code, abi_ulong addr)
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
{
+ CPUState *cpu = thread_cpu;
+ CPUArchState *env = cpu->env_ptr;
+ int sig;
+ target_siginfo_t tinfo;
+ ucontext_t *uc = puc;
+ uintptr_t pc = 0;
+ bool sync_sig = false;
+
+ /*
+ * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
+ * handling wrt signal blocking and unwinding.
+ */
+ if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) {
+ MMUAccessType access_type;
+ uintptr_t host_addr;
+ abi_ptr guest_addr;
+ bool is_write;
+
+ host_addr = (uintptr_t)info->si_addr;
+
+ /*
+ * Convert forcefully to guest address space: addresses outside
+ * reserved_va are still valid to report via SEGV_MAPERR.
+ */
+ guest_addr = h2g_nocheck(host_addr);
+
+ pc = host_signal_pc(uc);
+ is_write = host_signal_write(info, uc);
+ access_type = adjust_signal_pc(&pc, is_write);
+
+ if (host_sig == SIGSEGV) {
+ bool maperr = true;
+
+ if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) {
+ /* If this was a write to a TB protected page, restart. */
+ if (is_write &&
+ handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask,
+ pc, guest_addr)) {
+ return;
+ }
+
+ /*
+ * With reserved_va, the whole address space is PROT_NONE,
+ * which means that we may get ACCERR when we want MAPERR.
+ */
+ if (page_get_flags(guest_addr) & PAGE_VALID) {
+ maperr = false;
+ } else {
+ info->si_code = SEGV_MAPERR;
+ }
+ }
+
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
+ } else {
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ if (info->si_code == BUS_ADRALN) {
+ cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
+ }
+ }
+
+ sync_sig = true;
+ }
+
+ /* Get the target signal number. */
+ sig = host_to_target_signal(host_sig);
+ if (sig < 1 || sig > TARGET_NSIG) {
+ return;
+ }
+ trace_user_host_signal(cpu, host_sig, sig);
+
+ host_to_target_siginfo_noswap(&tinfo, info);
+
+ queue_signal(env, sig, &tinfo); /* XXX how to cope with failure? */
+ /*
+ * Linux does something else here -> the queue signal may be wrong, but
+ * maybe not. And then it does the rewind_if_in_safe_syscall
+ */
+
+ /*
+ * For synchronous signals, unwind the cpu state to the faulting
+ * insn and then exit back to the main loop so that the signal
+ * is delivered immediately.
+ XXXX Should this be in queue_signal?
+ */
+ if (sync_sig) {
+ cpu->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit_restore(cpu, pc);
+ }
+
+ rewind_if_in_safe_syscall(puc);
+
+ /*
+ * Block host signals until target signal handler entered. We
+ * can't block SIGSEGV or SIGBUS while we're executing guest
+ * code in case the guest code provokes one in the window between
+ * now and it getting out to the main loop. Signals will be
+ * unblocked again in process_pending_signals().
+ */
+ sigfillset(&uc->uc_sigmask);
+ sigdelset(&uc->uc_sigmask, SIGSEGV);
+ sigdelset(&uc->uc_sigmask, SIGBUS);
+
+ /* Interrupt the virtual CPU as soon as possible. */
+ cpu_exit(thread_cpu);
}
void signal_init(void)
--
2.33.1