qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] linux-user: Don't allow guest to block SIGSEGV
@ 2014-03-14 14:36 Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] linux-user: Add wrapper for guest uses of sigprocmask function Peter Maydell
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Peter Maydell @ 2014-03-14 14:36 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Matz, patches, Dann Frazier, Riku Voipio, Alexander Graf,
	Alex Bennée

QEMU uses SIGSEGV for its own purposes (notably for detection of guest
writes to pages marked read-only because we have cached translated code
from them), so allowing the guest to block SIGSEGV is a bad idea.
Instead we wrap sigprocmask() uses for guest-derived signal masks and
just track whether the guest thinks SIGSEGV is blocked rather than
really blocking it.

This is an updated version of a patchset sent by Alex Barcelo a
year or so ago; changes are:
 * use the wrapper for sigprocmask uses in signal.c where we set
   the signal mask on entry and exit from a guest signal handler
 * rather than just dropping SIGSEGV from the signal mask, track
   the guest state in a TaskState flag

Although the need for this patchset is more noticable with AArch64
guests (because at the moment we use trampoline code on the stack
for handling return from signal, which means that pages in the stack
are often marked read-only because we translate the trampoline code
and then written to in the normal course of guest execution) it
is generally applicable to all architectures.

[Eventually we should implement emulation of the vdso for AArch64,
at which point we can put the signal-return trampoline in that,
as the real kernel does; then there will be less of these SEGVs.]

Alex Barcelo (1):
  linux-user: Add wrapper for guest uses of sigprocmask function

Peter Maydell (1):
  linux-user: Don't allow guest to block SIGSEGV

 linux-user/qemu.h    |   2 +
 linux-user/signal.c  | 118 ++++++++++++++++++++++++++++++++++++++++-----------
 linux-user/syscall.c |  14 +++---
 3 files changed, 103 insertions(+), 31 deletions(-)

-- 
1.9.0

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH 1/2] linux-user: Add wrapper for guest uses of sigprocmask function
  2014-03-14 14:36 [Qemu-devel] [PATCH 0/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
@ 2014-03-14 14:36 ` Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] signal: added a wrapper for " Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 2/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2014-03-14 14:36 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Matz, patches, Dann Frazier, Riku Voipio, Alexander Graf,
	Alex Bennée

From: Alex Barcelo <abarcelo@ac.upc.edu>

Create a wrapper for signal mask changes initiated by the guest;
(this includes syscalls and also the sigreturns from signal.c)
this will give us a place to put code which prevents the guest
from changing the handling of signals used by QEMU itself
internally.

The wrapper is called from all the guest-initiated sigprocmask, but
is not called from internal qemu sigprocmask calls.

Signed-off-by: Alex Barcelo <abarcelo@ac.upc.edu>
[PMM: Added calls to wrapper for sigprocmask uses in signal.c
when setting the signal mask on entry and exit from signal
handlers, since these also are guest-provided signal masks.]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 linux-user/qemu.h    |  1 +
 linux-user/signal.c  | 58 ++++++++++++++++++++++++++++++----------------------
 linux-user/syscall.c | 14 ++++++-------
 3 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index c2f74f3..4d24e74 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -235,6 +235,7 @@ int host_to_target_signal(int sig);
 long do_sigreturn(CPUArchState *env);
 long do_rt_sigreturn(CPUArchState *env);
 abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
+int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
 
 #ifdef TARGET_I386
 /* vm86.c */
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 24c91f3..c8df584 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -197,6 +197,16 @@ 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)
+{
+    return sigprocmask(how, set, oldset);
+}
+
 /* siginfo conversion */
 
 static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
@@ -1056,7 +1066,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 +1091,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 +1230,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]);
@@ -1861,7 +1871,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 +1952,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 +2043,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 +2454,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 +2577,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 +2666,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 +3001,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 +3105,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 +3395,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 +3424,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 +3654,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 +3829,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 +4360,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 +4388,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 +4916,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 +4960,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 +5334,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 +5362,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 +5609,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 +5632,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;
@@ -5739,7 +5749,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);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ffc11de..2a8b66c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5993,7 +5993,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         {
             sigset_t cur_set;
             abi_ulong target_set;
-            sigprocmask(0, NULL, &cur_set);
+            do_sigprocmask(0, NULL, &cur_set);
             host_to_target_old_sigset(&target_set, &cur_set);
             ret = target_set;
         }
@@ -6004,10 +6004,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         {
             sigset_t set, oset, cur_set;
             abi_ulong target_set = arg1;
-            sigprocmask(0, NULL, &cur_set);
+            do_sigprocmask(0, NULL, &cur_set);
             target_to_host_old_sigset(&set, &target_set);
             sigorset(&set, &set, &cur_set);
-            sigprocmask(SIG_SETMASK, &set, &oset);
+            do_sigprocmask(SIG_SETMASK, &set, &oset);
             host_to_target_old_sigset(&target_set, &oset);
             ret = target_set;
         }
@@ -6038,7 +6038,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             mask = arg2;
             target_to_host_old_sigset(&set, &mask);
 
-            ret = get_errno(sigprocmask(how, &set, &oldset));
+            ret = get_errno(do_sigprocmask(how, &set, &oldset));
             if (!is_error(ret)) {
                 host_to_target_old_sigset(&mask, &oldset);
                 ret = mask;
@@ -6072,7 +6072,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 how = 0;
                 set_ptr = NULL;
             }
-            ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+            ret = get_errno(do_sigprocmask(how, set_ptr, &oldset));
             if (!is_error(ret) && arg3) {
                 if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
                     goto efault;
@@ -6112,7 +6112,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 how = 0;
                 set_ptr = NULL;
             }
-            ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+            ret = get_errno(do_sigprocmask(how, set_ptr, &oldset));
             if (!is_error(ret) && arg3) {
                 if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
                     goto efault;
@@ -8125,7 +8125,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             }
             mask = arg2;
             target_to_host_old_sigset(&set, &mask);
-            sigprocmask(how, &set, &oldset);
+            do_sigprocmask(how, &set, &oldset);
             host_to_target_old_sigset(&mask, &oldset);
             ret = mask;
         }
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH 1/2] signal: added a wrapper for sigprocmask function
  2014-03-14 14:36 [Qemu-devel] [PATCH 0/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] linux-user: Add wrapper for guest uses of sigprocmask function Peter Maydell
@ 2014-03-14 14:36 ` Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 2/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2014-03-14 14:36 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Matz, patches, Dann Frazier, Riku Voipio, Alexander Graf,
	Alex Bennée

From: Alex Barcelo <abarcelo@ac.upc.edu>

Create a wrapper for signal mask changes initiated by the guest;
(this includes syscalls and also the sigreturns from signal.c)
this will give us a place to put code which prevents the guest
from changing the handling of signals used by QEMU itself
internally.

The wrapper is called from all the guest-initiated sigprocmask, but
is not called from internal qemu sigprocmask calls.

Signed-off-by: Alex Barcelo <abarcelo@ac.upc.edu>
[PMM: Added calls to wrapper for sigprocmask uses in signal.c
when setting the signal mask on entry and exit from signal
handlers, since these also are guest-provided signal masks.]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 linux-user/qemu.h    |  1 +
 linux-user/signal.c  | 58 ++++++++++++++++++++++++++++++----------------------
 linux-user/syscall.c | 14 ++++++-------
 3 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index c2f74f3..4d24e74 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -235,6 +235,7 @@ int host_to_target_signal(int sig);
 long do_sigreturn(CPUArchState *env);
 long do_rt_sigreturn(CPUArchState *env);
 abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
+int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
 
 #ifdef TARGET_I386
 /* vm86.c */
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 24c91f3..c8df584 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -197,6 +197,16 @@ 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)
+{
+    return sigprocmask(how, set, oldset);
+}
+
 /* siginfo conversion */
 
 static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
@@ -1056,7 +1066,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 +1091,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 +1230,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]);
@@ -1861,7 +1871,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 +1952,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 +2043,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 +2454,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 +2577,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 +2666,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 +3001,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 +3105,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 +3395,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 +3424,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 +3654,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 +3829,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 +4360,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 +4388,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 +4916,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 +4960,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 +5334,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 +5362,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 +5609,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 +5632,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;
@@ -5739,7 +5749,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);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ffc11de..2a8b66c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5993,7 +5993,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         {
             sigset_t cur_set;
             abi_ulong target_set;
-            sigprocmask(0, NULL, &cur_set);
+            do_sigprocmask(0, NULL, &cur_set);
             host_to_target_old_sigset(&target_set, &cur_set);
             ret = target_set;
         }
@@ -6004,10 +6004,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         {
             sigset_t set, oset, cur_set;
             abi_ulong target_set = arg1;
-            sigprocmask(0, NULL, &cur_set);
+            do_sigprocmask(0, NULL, &cur_set);
             target_to_host_old_sigset(&set, &target_set);
             sigorset(&set, &set, &cur_set);
-            sigprocmask(SIG_SETMASK, &set, &oset);
+            do_sigprocmask(SIG_SETMASK, &set, &oset);
             host_to_target_old_sigset(&target_set, &oset);
             ret = target_set;
         }
@@ -6038,7 +6038,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             mask = arg2;
             target_to_host_old_sigset(&set, &mask);
 
-            ret = get_errno(sigprocmask(how, &set, &oldset));
+            ret = get_errno(do_sigprocmask(how, &set, &oldset));
             if (!is_error(ret)) {
                 host_to_target_old_sigset(&mask, &oldset);
                 ret = mask;
@@ -6072,7 +6072,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 how = 0;
                 set_ptr = NULL;
             }
-            ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+            ret = get_errno(do_sigprocmask(how, set_ptr, &oldset));
             if (!is_error(ret) && arg3) {
                 if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
                     goto efault;
@@ -6112,7 +6112,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                 how = 0;
                 set_ptr = NULL;
             }
-            ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+            ret = get_errno(do_sigprocmask(how, set_ptr, &oldset));
             if (!is_error(ret) && arg3) {
                 if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
                     goto efault;
@@ -8125,7 +8125,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             }
             mask = arg2;
             target_to_host_old_sigset(&set, &mask);
-            sigprocmask(how, &set, &oldset);
+            do_sigprocmask(how, &set, &oldset);
             host_to_target_old_sigset(&mask, &oldset);
             ret = mask;
         }
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [Qemu-devel] [PATCH 2/2] linux-user: Don't allow guest to block SIGSEGV
  2014-03-14 14:36 [Qemu-devel] [PATCH 0/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] linux-user: Add wrapper for guest uses of sigprocmask function Peter Maydell
  2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] signal: added a wrapper for " Peter Maydell
@ 2014-03-14 14:36 ` Peter Maydell
  2 siblings, 0 replies; 4+ messages in thread
From: Peter Maydell @ 2014-03-14 14:36 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Matz, patches, Dann Frazier, Riku Voipio, Alexander Graf,
	Alex Bennée

Don't allow the linux-user guest to block SIGSEGV -- QEMU needs this
signal to detect accesses to pages which it has marked read-only
because it has cached translated code from them.

We implement this by making the do_sigprocmask() wrapper suppress
SIGSEGV when doing the host process signal mask manipulation; instead
we store the current state of SIGSEGV in the TaskState struct.

If we get a SIGSEGV for the guest when the guest has blocked the
signal, we treat it as if the default SEGV handler was in place,
as the kernel does for forced SIGSEGV delivery.

This patch is based on an idea by Alex Barcelo, but rather than
simply lying to the guest about the SIGSEGV state we track it.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reported-by: Alex Barcelo <abarcelo@ac.upc.edu>
---
 linux-user/qemu.h   |  1 +
 linux-user/signal.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 4d24e74..36d4a73 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -126,6 +126,7 @@ typedef struct TaskState {
 #endif
     uint32_t stack_base;
     int used; /* non zero if used */
+    bool sigsegv_blocked; /* SIGSEGV blocked by guest */
     struct image_info *info;
     struct linux_binprm *bprm;
 
diff --git a/linux-user/signal.c b/linux-user/signal.c
index c8df584..f18f3d3 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -204,7 +204,46 @@ void target_to_host_old_sigset(sigset_t *sigset,
  */
 int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
 {
-    return sigprocmask(how, set, 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 */
@@ -468,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);
@@ -5726,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) {
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-03-14 14:37 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-14 14:36 [Qemu-devel] [PATCH 0/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell
2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] linux-user: Add wrapper for guest uses of sigprocmask function Peter Maydell
2014-03-14 14:36 ` [Qemu-devel] [PATCH 1/2] signal: added a wrapper for " Peter Maydell
2014-03-14 14:36 ` [Qemu-devel] [PATCH 2/2] linux-user: Don't allow guest to block SIGSEGV Peter Maydell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).