diff options
Diffstat (limited to 'linux-user/signal.c')
-rw-r--r-- | linux-user/signal.c | 120 |
1 files changed, 95 insertions, 25 deletions
diff --git a/linux-user/signal.c b/linux-user/signal.c index 24c91f37d9..e5fb9332e3 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -197,6 +197,55 @@ void target_to_host_old_sigset(sigset_t *sigset, target_to_host_sigset(sigset, &d); } +/* Wrapper for sigprocmask function + * Emulates a sigprocmask in a safe way for the guest. Note that set and oldset + * are host signal set, not guest ones. This wraps the sigprocmask host calls + * that should be protected (calls originated from guest) + */ +int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ + int ret; + sigset_t val; + sigset_t *temp = NULL; + CPUState *cpu = thread_cpu; + TaskState *ts = (TaskState *)cpu->opaque; + bool segv_was_blocked = ts->sigsegv_blocked; + + if (set) { + bool has_sigsegv = sigismember(set, SIGSEGV); + val = *set; + temp = &val; + + sigdelset(temp, SIGSEGV); + + switch (how) { + case SIG_BLOCK: + if (has_sigsegv) { + ts->sigsegv_blocked = true; + } + break; + case SIG_UNBLOCK: + if (has_sigsegv) { + ts->sigsegv_blocked = false; + } + break; + case SIG_SETMASK: + ts->sigsegv_blocked = has_sigsegv; + break; + default: + g_assert_not_reached(); + } + } + + ret = sigprocmask(how, temp, oldset); + + if (oldset && segv_was_blocked) { + sigaddset(oldset, SIGSEGV); + } + + return ret; +} + /* siginfo conversion */ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, @@ -458,6 +507,19 @@ int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) k = &ts->sigtab[sig - 1]; queue = gdb_queuesig (); handler = sigact_table[sig - 1]._sa_handler; + + if (ts->sigsegv_blocked && sig == TARGET_SIGSEGV) { + /* Guest has blocked SIGSEGV but we got one anyway. Assume this + * is a forced SIGSEGV (ie one the kernel handles via force_sig_info + * because it got a real MMU fault). A blocked SIGSEGV in that + * situation is treated as if using the default handler. This is + * not correct if some other process has randomly sent us a SIGSEGV + * via kill(), but that is not easy to distinguish at this point, + * so we assume it doesn't happen. + */ + handler = TARGET_SIG_DFL; + } + if (!queue && handler == TARGET_SIG_DFL) { if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) { kill(getpid(),SIGSTOP); @@ -1056,7 +1118,7 @@ long do_sigreturn(CPUX86State *env) } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); /* restore registers */ if (restore_sigcontext(env, &frame->sc, &eax)) @@ -1081,7 +1143,7 @@ long do_rt_sigreturn(CPUX86State *env) if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax)) goto badframe; @@ -1220,7 +1282,7 @@ static int target_restore_sigframe(CPUARMState *env, uint64_t pstate; target_to_host_sigset(&set, &sf->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); for (i = 0; i < 31; i++) { __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); @@ -1340,7 +1402,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, long do_rt_sigreturn(CPUARMState *env) { - struct target_rt_sigframe *frame; + struct target_rt_sigframe *frame = NULL; abi_ulong frame_addr = env->xregs[31]; if (frame_addr & 15) { @@ -1861,7 +1923,7 @@ static long do_sigreturn_v1(CPUARMState *env) } target_to_host_sigset_internal(&host_set, &set); - sigprocmask(SIG_SETMASK, &host_set, NULL); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); if (restore_sigcontext(env, &frame->sc)) goto badframe; @@ -1942,7 +2004,7 @@ static int do_sigframe_return_v2(CPUARMState *env, target_ulong frame_addr, abi_ulong *regspace; target_to_host_sigset(&host_set, &uc->tuc_sigmask); - sigprocmask(SIG_SETMASK, &host_set, NULL); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); if (restore_sigcontext(env, &uc->tuc_mcontext)) return 1; @@ -2033,7 +2095,7 @@ static long do_rt_sigreturn_v1(CPUARMState *env) goto badframe; target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &host_set, NULL); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) goto badframe; @@ -2444,7 +2506,7 @@ long do_sigreturn(CPUSPARCState *env) } target_to_host_sigset_internal(&host_set, &set); - sigprocmask(SIG_SETMASK, &host_set, NULL); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); if (err) goto segv_and_exit; @@ -2567,7 +2629,7 @@ void sparc64_set_context(CPUSPARCState *env) goto do_sigsegv; } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); } env->pc = pc; env->npc = npc; @@ -2656,7 +2718,7 @@ void sparc64_get_context(CPUSPARCState *env) err = 0; - sigprocmask(0, NULL, &set); + do_sigprocmask(0, NULL, &set); host_to_target_sigset_internal(&target_set, &set); if (TARGET_NSIG_WORDS == 1) { err |= __put_user(target_set.sig[0], @@ -2991,7 +3053,7 @@ long do_sigreturn(CPUMIPSState *regs) } target_to_host_sigset_internal(&blocked, &target_set); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (restore_sigcontext(regs, &frame->sf_sc)) goto badframe; @@ -3095,7 +3157,7 @@ long do_rt_sigreturn(CPUMIPSState *env) goto badframe; target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (restore_sigcontext(env, &frame->rs_uc.tuc_mcontext)) goto badframe; @@ -3385,7 +3447,7 @@ long do_sigreturn(CPUSH4State *regs) goto badframe; target_to_host_sigset_internal(&blocked, &target_set); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (restore_sigcontext(regs, &frame->sc, &r0)) goto badframe; @@ -3414,7 +3476,7 @@ long do_rt_sigreturn(CPUSH4State *regs) goto badframe; target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0)) goto badframe; @@ -3644,7 +3706,7 @@ long do_sigreturn(CPUMBState *env) goto badframe; } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); restore_sigcontext(&frame->uc.tuc_mcontext, env); /* We got here through a sigreturn syscall, our path back is via an @@ -3819,7 +3881,7 @@ long do_sigreturn(CPUCRISState *env) goto badframe; } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); restore_sigcontext(&frame->sc, env); unlock_user_struct(frame, frame_addr, 0); @@ -4350,7 +4412,7 @@ long do_sigreturn(CPUS390XState *env) } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ + do_sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ if (restore_sigregs(env, &frame->sregs)) { goto badframe; @@ -4378,7 +4440,7 @@ long do_rt_sigreturn(CPUS390XState *env) } target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ + do_sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { goto badframe; @@ -4906,7 +4968,7 @@ long do_sigreturn(CPUPPCState *env) goto sigsegv; #endif target_to_host_sigset_internal(&blocked, &set); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (__get_user(sr_addr, &sc->regs)) goto sigsegv; @@ -4950,7 +5012,7 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) return 1; target_to_host_sigset_internal(&blocked, &set); - sigprocmask(SIG_SETMASK, &blocked, NULL); + do_sigprocmask(SIG_SETMASK, &blocked, NULL); if (restore_user_regs(env, mcp, sig)) goto sigsegv; @@ -5324,7 +5386,7 @@ long do_sigreturn(CPUM68KState *env) } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); /* restore registers */ @@ -5352,7 +5414,7 @@ long do_rt_sigreturn(CPUM68KState *env) goto badframe; target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); /* restore registers */ @@ -5599,7 +5661,7 @@ long do_sigreturn(CPUAlphaState *env) } target_to_host_sigset_internal(&set, &target_set); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); if (restore_sigcontext(env, sc)) { goto badframe; @@ -5622,7 +5684,7 @@ long do_rt_sigreturn(CPUAlphaState *env) goto badframe; } target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - sigprocmask(SIG_SETMASK, &set, NULL); + do_sigprocmask(SIG_SETMASK, &set, NULL); if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { goto badframe; @@ -5716,6 +5778,14 @@ void process_pending_signals(CPUArchState *cpu_env) handler = sa->_sa_handler; } + if (ts->sigsegv_blocked && sig == TARGET_SIGSEGV) { + /* Guest has blocked SIGSEGV but we got one anyway. Assume this + * is a forced SIGSEGV (ie one the kernel handles via force_sig_info + * because it got a real MMU fault), and treat as if default handler. + */ + handler = TARGET_SIG_DFL; + } + if (handler == TARGET_SIG_DFL) { /* default handler : ignore some signal. The other are job control or fatal */ if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) { @@ -5739,7 +5809,7 @@ void process_pending_signals(CPUArchState *cpu_env) sigaddset(&set, target_to_host_signal(sig)); /* block signals in the handler using Linux */ - sigprocmask(SIG_BLOCK, &set, &old_set); + do_sigprocmask(SIG_BLOCK, &set, &old_set); /* save the previous blocked signal state to restore it at the end of the signal execution (see do_sigreturn) */ host_to_target_sigset_internal(&target_old_set, &old_set); |