From 7f72cd235fa33f2fc7a8d1cc4d621bf7db61e9eb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2014 13:06:00 +0000 Subject: [PATCH 1/7] linux-user/signal.c: Correct error path for AArch64 do_rt_sigreturn The error path in AArch64 do_rt_sigreturn() which fails before attempting lock_user_struct() was doing an unlock_user_struct() on an uninitialized variable. Initialize frame to NULL so we can use the same error-exit path in all cases (unlock of NULL is permitted and does nothing). Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 24c91f37d9..209855efcb 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1340,7 +1340,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) { From 3b899ea7d405dc7634ac629aa7b0e7639d506d9f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 12 Mar 2014 13:06:01 +0000 Subject: [PATCH 2/7] linux-user: Don't return uninitialized value for atomic_barrier syscall QEMU's implementation of the m68k atomic_barrier syscall, like the kernel's, is just a no-op. However we still need to return a result code from it. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ffc11de4b7..b5eadb14b0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9148,6 +9148,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_atomic_barrier: { /* Like the kernel implementation and the qemu arm barrier, no-op this? */ + ret = 0; break; } #endif From 8d5d30046b9aadfeee60e09fe79c063f419993db Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Fri, 7 Mar 2014 15:24:08 +0100 Subject: [PATCH 3/7] linux-user: implement F_[GS]ETOWN_EX F_GETOWN is replaced by F_GETOWN_EX inside the glibc fcntl wrapper Signed-off-by: Andreas Schwab Signed-off-by: Riku Voipio --- linux-user/syscall.c | 36 ++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 7 +++++++ 2 files changed, 43 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b5eadb14b0..b8086b883f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4421,6 +4421,14 @@ static int target_to_host_fcntl_cmd(int cmd) #endif case TARGET_F_NOTIFY: return F_NOTIFY; +#ifdef F_GETOWN_EX + case TARGET_F_GETOWN_EX: + return F_GETOWN_EX; +#endif +#ifdef F_SETOWN_EX + case TARGET_F_SETOWN_EX: + return F_SETOWN_EX; +#endif default: return -TARGET_EINVAL; } @@ -4443,6 +4451,10 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) struct target_flock *target_fl; struct flock64 fl64; struct target_flock64 *target_fl64; +#ifdef F_GETOWN_EX + struct f_owner_ex fox; + struct target_f_owner_ex *target_fox; +#endif abi_long ret; int host_cmd = target_to_host_fcntl_cmd(cmd); @@ -4536,6 +4548,30 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) ret = get_errno(fcntl(fd, host_cmd, target_to_host_bitmask(arg, fcntl_flags_tbl))); break; +#ifdef F_GETOWN_EX + case TARGET_F_GETOWN_EX: + ret = get_errno(fcntl(fd, host_cmd, &fox)); + if (ret >= 0) { + if (!lock_user_struct(VERIFY_WRITE, target_fox, arg, 0)) + return -TARGET_EFAULT; + target_fox->type = tswap32(fox.type); + target_fox->pid = tswap32(fox.pid); + unlock_user_struct(target_fox, arg, 1); + } + break; +#endif + +#ifdef F_SETOWN_EX + case TARGET_F_SETOWN_EX: + if (!lock_user_struct(VERIFY_READ, target_fox, arg, 1)) + return -TARGET_EFAULT; + fox.type = tswap32(target_fox->type); + fox.pid = tswap32(target_fox->pid); + unlock_user_struct(target_fox, arg, 0); + ret = get_errno(fcntl(fd, host_cmd, &fox)); + break; +#endif + case TARGET_F_SETOWN: case TARGET_F_GETOWN: case TARGET_F_SETSIG: diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 732c9e3dbb..2a7d1db10b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2123,6 +2123,8 @@ struct target_statfs64 { #define TARGET_F_SETOWN 8 /* for sockets. */ #define TARGET_F_GETOWN 9 /* for sockets. */ #endif +#define TARGET_F_SETOWN_EX 15 +#define TARGET_F_GETOWN_EX 16 #ifndef TARGET_F_RDLCK #define TARGET_F_RDLCK 0 @@ -2305,6 +2307,11 @@ struct target_eabi_flock64 { } QEMU_PACKED; #endif +struct target_f_owner_ex { + int type; /* Owner type of ID. */ + int pid; /* ID of owner. */ +}; + /* soundcard defines */ /* XXX: convert them all to arch indepedent entries */ #define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, int); From 6b1275ff15b205a7738aa8ba96fe355f097b5879 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 10 Mar 2014 12:59:17 +0000 Subject: [PATCH 4/7] linux-user: Don't reserve space for commpage for AArch64 AArch64 Linux, unlike AArch32, doesn't use a commpage. This means we should not be reserving room in the guest address space for one. Fixes LP:1287195. Reported-by: Amanieu d'Antras Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/elfload.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6bc799957b..99a2c586eb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -352,6 +352,9 @@ enum ARM_HWCAP_ARM_VFPv3D16 = 1 << 13, }; +#ifndef TARGET_AARCH64 +/* The commpage only exists for 32 bit kernels */ + #define TARGET_HAS_VALIDATE_GUEST_SPACE /* Return 1 if the proposed guest space is suitable for the guest. * Return 0 if the proposed guest space isn't suitable, but another @@ -411,7 +414,7 @@ static int validate_guest_space(unsigned long guest_base, return 1; /* All good */ } - +#endif #define ELF_HWCAP get_elf_hwcap() From 1c275925bfbbc2de84a8f0e09d1dd70bbefb6da3 Mon Sep 17 00:00:00 2001 From: Alex Barcelo Date: Fri, 14 Mar 2014 14:36:55 +0000 Subject: [PATCH 5/7] signal: added a wrapper for sigprocmask function 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 [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 Signed-off-by: Riku Voipio --- 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 c2f74f33d6..4d24e74775 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 209855efcb..9e6a4952a7 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 b8086b883f..e404a32c0f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6029,7 +6029,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; } @@ -6040,10 +6040,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; } @@ -6074,7 +6074,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; @@ -6108,7 +6108,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; @@ -6148,7 +6148,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; @@ -8161,7 +8161,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; } From a7ec0f98e3a37a4d31c832cfa14dc2c1c0890421 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 14 Mar 2014 14:36:56 +0000 Subject: [PATCH 6/7] linux-user: Don't allow guest to block SIGSEGV 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 Reported-by: Alex Barcelo Signed-off-by: Riku Voipio --- 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 4d24e74775..36d4a738ea 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 9e6a4952a7..e5fb9332e3 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) { From e0eb210ec0c1cd262e3f642133ee93acdaf60aa0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2014 12:15:35 +0000 Subject: [PATCH 7/7] linux-user: Implement capget, capset Implement the capget and capset syscalls. This is useful because simple programs like 'ls' try to use it in AArch64, and otherwise we emit a lot of noise about it being unimplemented. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 75 +++++++++++++++++++++++++++++++++++++-- linux-user/syscall_defs.h | 11 ++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e404a32c0f..366b6951e9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #ifdef __ia64__ @@ -243,6 +244,10 @@ _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); _syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg); +_syscall2(int, capget, struct __user_cap_header_struct *, header, + struct __user_cap_data_struct *, data); +_syscall2(int, capset, struct __user_cap_header_struct *, header, + struct __user_cap_data_struct *, data); static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -7677,9 +7682,75 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, ret); break; case TARGET_NR_capget: - goto unimplemented; case TARGET_NR_capset: - goto unimplemented; + { + struct target_user_cap_header *target_header; + struct target_user_cap_data *target_data = NULL; + struct __user_cap_header_struct header; + struct __user_cap_data_struct data[2]; + struct __user_cap_data_struct *dataptr = NULL; + int i, target_datalen; + int data_items = 1; + + if (!lock_user_struct(VERIFY_WRITE, target_header, arg1, 1)) { + goto efault; + } + header.version = tswap32(target_header->version); + header.pid = tswap32(target_header->pid); + + if (header.version != _LINUX_CAPABILITY_VERSION_1) { + /* Version 2 and up takes pointer to two user_data structs */ + data_items = 2; + } + + target_datalen = sizeof(*target_data) * data_items; + + if (arg2) { + if (num == TARGET_NR_capget) { + target_data = lock_user(VERIFY_WRITE, arg2, target_datalen, 0); + } else { + target_data = lock_user(VERIFY_READ, arg2, target_datalen, 1); + } + if (!target_data) { + unlock_user_struct(target_header, arg1, 0); + goto efault; + } + + if (num == TARGET_NR_capset) { + for (i = 0; i < data_items; i++) { + data[i].effective = tswap32(target_data[i].effective); + data[i].permitted = tswap32(target_data[i].permitted); + data[i].inheritable = tswap32(target_data[i].inheritable); + } + } + + dataptr = data; + } + + if (num == TARGET_NR_capget) { + ret = get_errno(capget(&header, dataptr)); + } else { + ret = get_errno(capset(&header, dataptr)); + } + + /* The kernel always updates version for both capget and capset */ + target_header->version = tswap32(header.version); + unlock_user_struct(target_header, arg1, 1); + + if (arg2) { + if (num == TARGET_NR_capget) { + for (i = 0; i < data_items; i++) { + target_data[i].effective = tswap32(data[i].effective); + target_data[i].permitted = tswap32(data[i].permitted); + target_data[i].inheritable = tswap32(data[i].inheritable); + } + unlock_user(target_data, arg2, target_datalen); + } else { + unlock_user(target_data, arg2, 0); + } + } + break; + } case TARGET_NR_sigaltstack: #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2a7d1db10b..fdf9a47595 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2566,3 +2566,14 @@ struct target_sigevent { } _sigev_thread; } _sigev_un; }; + +struct target_user_cap_header { + uint32_t version; + int pid; +}; + +struct target_user_cap_data { + uint32_t effective; + uint32_t permitted; + uint32_t inheritable; +};