Add support for tracking the larger target signal mask in system calls sigprocmask()/rt_sigprocmask(), sigsuspend()/rt_sigsuspend(), sgetmask()/ssetmask() and in functions do_sigreturn(), do_rt_sigreturn(), handle_pending_signal(), process_pending_signals(). Add a new function do_target_sigprocmask() which is based on do_sigprocmask() and extends its functionallity.
It can happen that the host machine has a smaller range of signals compared to the target machine that it's emulating. Currently the signals that are in the target range but out of the host range are treated like faulty signals and can't be used. In this patch, support is added for tracking the target signal mask, alongside and in the same manner as the host signal mask. Signed-off-by: Miloš Stojanović <milos.stojano...@rt-rk.com> --- linux-user/qemu.h | 5 ++ linux-user/signal.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++- linux-user/syscall.c | 94 ++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 1 deletion(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 6ce0811..bf13db6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -395,6 +395,11 @@ 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 TRACK_TARGET_SIGMASK +int do_target_sigprocmask(int how, const target_sigset_t *target_set, + target_sigset_t *target_oldset, + const sigset_t *set, sigset_t *oldset); +#endif /** * block_signals: block all signals while handling this guest syscall * diff --git a/linux-user/signal.c b/linux-user/signal.c index 0c1e231..9d4ad43 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -296,17 +296,85 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) return 0; } +#ifdef TRACK_TARGET_SIGMASK +int do_target_sigprocmask(int how, const target_sigset_t *target_set, + target_sigset_t *target_oldset, + const sigset_t *set, sigset_t *oldset) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + + if (target_oldset) { + *target_oldset = ts->target_signal_mask; + } + if (oldset) { + *oldset = ts->signal_mask; + } + + if (target_set && set) { + int i; + + if (block_signals()) { + return -TARGET_ERESTARTSYS; + } + + switch (how) { + case SIG_BLOCK: + target_sigorset(&ts->target_signal_mask, &ts->target_signal_mask, + target_set); + sigorset(&ts->signal_mask, &ts->signal_mask, set); + break; + case SIG_UNBLOCK: + for (i = 1; i <= TARGET_NSIG; ++i) { + if (target_sigismember(target_set, i) == 1) { + target_sigdelset(&ts->target_signal_mask, i); + } + } + for (i = 1; i <= NSIG; ++i) { + if (sigismember(set, i) == 1) { + sigdelset(&ts->signal_mask, i); + } + } + break; + case SIG_SETMASK: + ts->target_signal_mask = *target_set; + ts->signal_mask = *set; + break; + default: + g_assert_not_reached(); + } + + /* Silently ignore attempts to change blocking status of KILL or STOP */ + target_sigdelset(&ts->target_signal_mask, SIGKILL); + target_sigdelset(&ts->target_signal_mask, SIGSTOP); + sigdelset(&ts->signal_mask, SIGKILL); + sigdelset(&ts->signal_mask, SIGSTOP); + } + return 0; +} +#endif + #if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \ !defined(TARGET_NIOS2) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ +#ifndef TRACK_TARGET_SIGMASK static void set_sigmask(const sigset_t *set) { TaskState *ts = (TaskState *)thread_cpu->opaque; ts->signal_mask = *set; } +#else +static void target_set_sigmask(const sigset_t *set, + const target_sigset_t *target_set) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + + ts->signal_mask = *set; + ts->target_signal_mask = *target_set; +} +#endif #endif /* siginfo conversion */ @@ -3315,6 +3383,9 @@ long do_sigreturn(CPUMIPSState *regs) abi_ulong frame_addr; sigset_t blocked; target_sigset_t target_set; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_blocked; +#endif int i; frame_addr = regs->active_tc.gpr[29]; @@ -3327,7 +3398,12 @@ long do_sigreturn(CPUMIPSState *regs) } target_to_host_sigset_internal(&blocked, &target_set); +#ifdef TRACK_TARGET_SIGMASK + tswapal_target_sigset(&target_blocked, &target_set); + target_set_sigmask(&blocked, &target_blocked); +#else set_sigmask(&blocked); +#endif restore_sigcontext(regs, &frame->sf_sc); @@ -3423,6 +3499,9 @@ long do_rt_sigreturn(CPUMIPSState *env) struct target_rt_sigframe *frame; abi_ulong frame_addr; sigset_t blocked; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_blocked; +#endif frame_addr = env->active_tc.gpr[29]; trace_user_do_rt_sigreturn(env, frame_addr); @@ -3431,7 +3510,12 @@ long do_rt_sigreturn(CPUMIPSState *env) } target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); +#ifdef TRACK_TARGET_SIGMASK + tswapal_target_sigset(&target_blocked, &frame->rs_uc.tuc_sigmask); + target_set_sigmask(&blocked, &target_blocked); +#else set_sigmask(&blocked); +#endif restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); @@ -6574,6 +6658,9 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, abi_ulong handler; sigset_t set; target_sigset_t target_old_set; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_set; +#endif struct target_sigaction *sa; TaskState *ts = cpu->opaque; @@ -6611,21 +6698,39 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, } else { /* compute the blocked signals during the handler execution */ sigset_t *blocked_set; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t *target_blocked_set; + tswapal_target_sigset(&target_set, &sa->sa_mask); +#endif target_to_host_sigset(&set, &sa->sa_mask); /* SA_NODEFER indicates that the current signal should not be blocked during the handler */ - if (!(sa->sa_flags & TARGET_SA_NODEFER)) + if (!(sa->sa_flags & TARGET_SA_NODEFER)) { +#ifdef TRACK_TARGET_SIGMASK + target_sigaddset(&target_set, sig); +#endif sigaddset(&set, target_to_host_signal(sig)); + } /* save the previous blocked signal state to restore it at the end of the signal execution (see do_sigreturn) */ +#ifdef TRACK_TARGET_SIGMASK + target_old_set = ts->target_signal_mask; +#else host_to_target_sigset_internal(&target_old_set, &ts->signal_mask); +#endif /* block signals in the handler */ blocked_set = ts->in_sigsuspend ? &ts->sigsuspend_mask : &ts->signal_mask; sigorset(&ts->signal_mask, blocked_set, &set); +#ifdef TRACK_TARGET_SIGMASK + target_blocked_set = ts->in_sigsuspend ? + &ts->target_sigsuspend_mask : &ts->target_signal_mask; + target_sigorset(&ts->target_signal_mask, target_blocked_set, + &target_set); +#endif ts->in_sigsuspend = 0; /* if the CPU is in VM86 mode, we restore the 32 bit values */ @@ -6661,7 +6766,11 @@ void process_pending_signals(CPUArchState *cpu_env) int sig; TaskState *ts = cpu->opaque; sigset_t set; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t *target_blocked_set; +#else sigset_t *blocked_set; +#endif while (atomic_read(&ts->signal_pending)) { /* FIXME: This is not threadsafe. */ @@ -6679,22 +6788,42 @@ void process_pending_signals(CPUArchState *cpu_env) * to block a synchronous signal since it could then just end up * looping round and round indefinitely. */ +#ifdef TRACK_TARGET_SIGMASK + if (sigismember(&ts->signal_mask, target_to_host_signal(sig)) == 1 + || target_sigismember(&ts->target_signal_mask, sig) == 1 + || sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) { + sigdelset(&ts->signal_mask, target_to_host_signal(sig)); + target_sigdelset(&ts->target_signal_mask, sig); + sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL; + } +#else if (sigismember(&ts->signal_mask, target_to_host_signal_table[sig]) || sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) { sigdelset(&ts->signal_mask, target_to_host_signal_table[sig]); sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL; } +#endif handle_pending_signal(cpu_env, sig, &ts->sync_signal); } for (sig = 1; sig <= TARGET_NSIG; sig++) { +#ifdef TRACK_TARGET_SIGMASK + target_blocked_set = ts->in_sigsuspend ? + &ts->target_sigsuspend_mask : &ts->target_signal_mask; +#else blocked_set = ts->in_sigsuspend ? &ts->sigsuspend_mask : &ts->signal_mask; +#endif +#ifdef TRACK_TARGET_SIGMASK + if (ts->sigtab[sig - 1].pending && + (!target_sigismember(target_blocked_set, sig))) { +#else if (ts->sigtab[sig - 1].pending && (!sigismember(blocked_set, target_to_host_signal_table[sig]))) { +#endif handle_pending_signal(cpu_env, sig, &ts->sigtab[sig - 1]); /* Restart scan from the beginning, as handle_pending_signal * might have resulted in a new synchronous signal (eg SIGSEGV). diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 94ecae3..f4ce6a8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8585,6 +8585,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_sgetmask /* not on alpha */ case TARGET_NR_sgetmask: { +#ifdef TRACK_TARGET_SIGMASK + sigset_t cur_set; + target_sigset_t target_set_mask; + abi_ulong target_set; + ret = do_target_sigprocmask(0, NULL, &target_set_mask, + NULL, &cur_set); + if (!ret) { + target_to_abi_ulong_old_sigset(&target_set, &target_set_mask); + ret = target_set; + } +#else sigset_t cur_set; abi_ulong target_set; ret = do_sigprocmask(0, NULL, &cur_set); @@ -8592,12 +8603,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_to_target_old_sigset(&target_set, &cur_set); ret = target_set; } +#endif } break; #endif #ifdef TARGET_NR_ssetmask /* not on alpha */ case TARGET_NR_ssetmask: { +#ifdef TRACK_TARGET_SIGMASK + sigset_t set, oset; + target_sigset_t target_set_mask, target_oset; + abi_ulong target_set = arg1; + target_to_host_old_sigset(&set, &target_set); + abi_ulong_to_target_old_sigset(&target_set_mask, &target_set); + ret = do_target_sigprocmask(SIG_SETMASK, &target_set_mask, + &target_oset, &set, &oset); + if (!ret) { + target_to_abi_ulong_old_sigset(&target_set, &target_oset); + ret = target_set; + } +#else sigset_t set, oset; abi_ulong target_set = arg1; target_to_host_old_sigset(&set, &target_set); @@ -8606,6 +8631,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_to_target_old_sigset(&target_set, &oset); ret = target_set; } +#endif } break; #endif @@ -8614,6 +8640,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { #if defined(TARGET_ALPHA) sigset_t set, oldset; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_set, target_oldset; +#endif abi_ulong mask; int how; @@ -8634,14 +8663,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, mask = arg2; target_to_host_old_sigset(&set, &mask); +#ifdef TRACK_TARGET_SIGMASK + abi_ulong_to_target_old_sigset(&target_set, &mask); + + ret = do_target_sigprocmask(how, &target_set, &target_oldset, + &set, &oldset); + if (!is_error(ret)) { + target_to_abi_ulong_old_sigset(&mask, &target_oldset); +#else ret = do_sigprocmask(how, &set, &oldset); if (!is_error(ret)) { host_to_target_old_sigset(&mask, &oldset); +#endif ret = mask; ((CPUAlphaState *)cpu_env)->ir[IR_V0] = 0; /* force no error */ } #else sigset_t set, oldset, *set_ptr; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_set, target_oldset, *target_set_ptr; +#endif int how; if (arg2) { @@ -8662,17 +8703,32 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) goto efault; target_to_host_old_sigset(&set, p); +#ifdef TRACK_TARGET_SIGMASK + abi_ulong_to_target_old_sigset(&target_set, p); + target_set_ptr = &target_set; +#endif unlock_user(p, arg2, 0); set_ptr = &set; } else { how = 0; set_ptr = NULL; +#ifdef TRACK_TARGET_SIGMASK + target_set_ptr = NULL; + } + ret = do_target_sigprocmask(how, target_set_ptr, &target_oldset, + set_ptr, &oldset); +#else } ret = do_sigprocmask(how, set_ptr, &oldset); +#endif if (!is_error(ret) && arg3) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0))) goto efault; +#ifdef TRACK_TARGET_SIGMASK + target_to_abi_ulong_old_sigset(p, &target_oldset); +#else host_to_target_old_sigset(p, &oldset); +#endif unlock_user(p, arg3, sizeof(target_sigset_t)); } #endif @@ -8683,6 +8739,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { int how = arg1; sigset_t set, oldset, *set_ptr; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_set, target_oldset, *target_set_ptr; +#endif if (arg4 != sizeof(target_sigset_t)) { ret = -TARGET_EINVAL; @@ -8707,17 +8766,32 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) goto efault; target_to_host_sigset(&set, p); +#ifdef TRACK_TARGET_SIGMASK + tswapal_target_sigset(&target_set, p); + target_set_ptr = &target_set; +#endif unlock_user(p, arg2, 0); set_ptr = &set; } else { how = 0; set_ptr = NULL; +#ifdef TRACK_TARGET_SIGMASK + target_set_ptr = NULL; + } + ret = do_target_sigprocmask(how, target_set_ptr, &target_oldset, + set_ptr, &oldset); +#else } ret = do_sigprocmask(how, set_ptr, &oldset); +#endif if (!is_error(ret) && arg3) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0))) goto efault; +#ifdef TRACK_TARGET_SIGMASK + tswapal_target_sigset(p, &target_oldset); +#else host_to_target_sigset(p, &oldset); +#endif unlock_user(p, arg3, sizeof(target_sigset_t)); } } @@ -8766,10 +8840,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_ALPHA) abi_ulong mask = arg1; target_to_host_old_sigset(&ts->sigsuspend_mask, &mask); +#ifdef TRACK_TARGET_SIGMASK + abi_ulong_to_target_old_sigset(&ts->target_sigsuspend_mask, &mask); +#endif #else if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) goto efault; target_to_host_old_sigset(&ts->sigsuspend_mask, p); +#ifdef TRACK_TARGET_SIGMASK + abi_ulong_to_target_old_sigset(&ts->target_sigsuspend_mask, p); +#endif unlock_user(p, arg1, 0); #endif ret = get_errno(safe_rt_sigsuspend(&ts->sigsuspend_mask, @@ -8791,6 +8871,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) goto efault; target_to_host_sigset(&ts->sigsuspend_mask, p); +#ifdef TRACK_TARGET_SIGMASK + tswapal_target_sigset(&ts->target_sigsuspend_mask, p); +#endif unlock_user(p, arg1, 0); ret = get_errno(safe_rt_sigsuspend(&ts->sigsuspend_mask, SIGSET_T_SIZE)); @@ -11030,6 +11113,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_ulong mask; int how; sigset_t set, oldset; +#ifdef TRACK_TARGET_SIGMASK + target_sigset_t target_set, target_oldset; +#endif switch(arg1) { case TARGET_SIG_BLOCK: @@ -11047,9 +11133,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } mask = arg2; target_to_host_old_sigset(&set, &mask); +#ifdef TRACK_TARGET_SIGMASK + abi_ulong_to_target_old_sigset(&target_set, &mask); + ret = do_target_sigprocmask(how, &target_set, &target_oldset, + &set, &oldset); + if (!ret) { + target_to_abi_ulong_old_sigset(&mask, &target_oldset); +#else ret = do_sigprocmask(how, &set, &oldset); if (!ret) { host_to_target_old_sigset(&mask, &oldset); +#endif ret = mask; } } -- 1.9.1