summary refs log tree commit diff stats
path: root/linux-user/signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/signal.c')
-rw-r--r--linux-user/signal.c170
1 files changed, 128 insertions, 42 deletions
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 04638e2ead..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,
@@ -370,7 +419,8 @@ void signal_init(void)
 
 static inline struct sigqueue *alloc_sigqueue(CPUArchState *env)
 {
-    TaskState *ts = env->opaque;
+    CPUState *cpu = ENV_GET_CPU(env);
+    TaskState *ts = cpu->opaque;
     struct sigqueue *q = ts->first_free;
     if (!q)
         return NULL;
@@ -380,7 +430,9 @@ static inline struct sigqueue *alloc_sigqueue(CPUArchState *env)
 
 static inline void free_sigqueue(CPUArchState *env, struct sigqueue *q)
 {
-    TaskState *ts = env->opaque;
+    CPUState *cpu = ENV_GET_CPU(env);
+    TaskState *ts = cpu->opaque;
+
     q->next = ts->first_free;
     ts->first_free = q;
 }
@@ -388,8 +440,9 @@ static inline void free_sigqueue(CPUArchState *env, struct sigqueue *q)
 /* abort execution with signal */
 static void QEMU_NORETURN force_sig(int target_sig)
 {
-    CPUArchState *env = thread_cpu->env_ptr;
-    TaskState *ts = (TaskState *)env->opaque;
+    CPUState *cpu = thread_cpu;
+    CPUArchState *env = cpu->env_ptr;
+    TaskState *ts = (TaskState *)cpu->opaque;
     int host_sig, core_dumped = 0;
     struct sigaction act;
     host_sig = target_to_host_signal(target_sig);
@@ -440,7 +493,8 @@ static void QEMU_NORETURN force_sig(int target_sig)
    as possible */
 int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info)
 {
-    TaskState *ts = env->opaque;
+    CPUState *cpu = ENV_GET_CPU(env);
+    TaskState *ts = cpu->opaque;
     struct emulated_sigtable *k;
     struct sigqueue *q, **pq;
     abi_ulong handler;
@@ -453,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);
@@ -774,8 +841,9 @@ static int
 setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
 		 CPUX86State *env, abi_ulong mask, abi_ulong fpstate_addr)
 {
-	int err = 0;
-        uint16_t magic;
+    CPUState *cs = CPU(x86_env_get_cpu(env));
+    int err = 0;
+    uint16_t magic;
 
 	/* already locked in setup_frame() */
 	err |= __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
@@ -790,7 +858,7 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
 	err |= __put_user(env->regs[R_EDX], &sc->edx);
 	err |= __put_user(env->regs[R_ECX], &sc->ecx);
 	err |= __put_user(env->regs[R_EAX], &sc->eax);
-	err |= __put_user(env->exception_index, &sc->trapno);
+    err |= __put_user(cs->exception_index, &sc->trapno);
 	err |= __put_user(env->error_code, &sc->err);
 	err |= __put_user(env->eip, &sc->eip);
 	err |= __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
@@ -1050,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))
@@ -1075,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;
@@ -1214,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]);
@@ -1233,8 +1301,14 @@ static int target_restore_sigframe(CPUARMState *env,
         return 1;
     }
 
-    for (i = 0; i < 32 * 2; i++) {
-        __get_user(env->vfp.regs[i], &aux->fpsimd.vregs[i]);
+    for (i = 0; i < 32; i++) {
+#ifdef TARGET_WORDS_BIGENDIAN
+        __get_user(env->vfp.regs[i * 2], &aux->fpsimd.vregs[i * 2 + 1]);
+        __get_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2]);
+#else
+        __get_user(env->vfp.regs[i * 2], &aux->fpsimd.vregs[i * 2]);
+        __get_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2 + 1]);
+#endif
     }
     __get_user(fpsr, &aux->fpsimd.fpsr);
     vfp_set_fpsr(env, fpsr);
@@ -1267,7 +1341,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
                                CPUARMState *env)
 {
     struct target_rt_sigframe *frame;
-    abi_ulong frame_addr;
+    abi_ulong frame_addr, return_addr;
 
     frame_addr = get_sigframe(ka, env);
     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
@@ -1284,15 +1358,19 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
     __put_user(target_sigaltstack_used.ss_size,
                       &frame->uc.tuc_stack.ss_size);
     target_setup_sigframe(frame, env, set);
-    /* mov x8,#__NR_rt_sigreturn; svc #0 */
-    __put_user(0xd2801168, &frame->tramp[0]);
-    __put_user(0xd4000001, &frame->tramp[1]);
+    if (ka->sa_flags & TARGET_SA_RESTORER) {
+        return_addr = ka->sa_restorer;
+    } else {
+        /* mov x8,#__NR_rt_sigreturn; svc #0 */
+        __put_user(0xd2801168, &frame->tramp[0]);
+        __put_user(0xd4000001, &frame->tramp[1]);
+        return_addr = frame_addr + offsetof(struct target_rt_sigframe, tramp);
+    }
     env->xregs[0] = usig;
     env->xregs[31] = frame_addr;
     env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp);
     env->pc = ka->_sa_handler;
-    env->xregs[30] = env->xregs[31] +
-        offsetof(struct target_rt_sigframe, tramp);
+    env->xregs[30] = return_addr;
     if (info) {
         if (copy_siginfo_to_user(&frame->info, info)) {
             goto give_sigsegv;
@@ -1324,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) {
@@ -1845,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;
@@ -1926,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;
@@ -2017,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;
@@ -2428,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;
@@ -2551,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;
@@ -2640,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],
@@ -2975,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;
@@ -3079,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;
@@ -3369,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;
@@ -3398,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;
@@ -3628,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
@@ -3803,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);
@@ -4334,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;
@@ -4362,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;
@@ -4890,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;
@@ -4934,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;
 
@@ -5308,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 */
 
@@ -5336,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 */
 
@@ -5583,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;
@@ -5606,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;
@@ -5665,7 +5743,7 @@ void process_pending_signals(CPUArchState *cpu_env)
     struct emulated_sigtable *k;
     struct target_sigaction *sa;
     struct sigqueue *q;
-    TaskState *ts = cpu_env->opaque;
+    TaskState *ts = cpu->opaque;
 
     if (!ts->signal_pending)
         return;
@@ -5700,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) {
@@ -5723,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);