Commit 0b1bcb00fb2baf5f3227dd9cd849fa69bf50d7a8

Authored by pbrook
1 parent e4474235

MIPS signal handling fixes.

Also fixes a register corruption bug in do_sigreturn. When "returning"
from sigreturn we are actually restoring the virtual cpu state from the
signal frame.  This is actually surprisingly hard to observe in practice.

Typically an thread be blocked in a FUTEX_WAIT call when the signal arrives,
so the effect is a spurious syscall success and the introduction of a
subtle race condition.

On x86/arm a syscall modifies a single word sized register, so
do_sigreturn can just return that value.  On MIPS a syscall clobbers
multiple registers, so we need additional smarts.  My solution is to
invent a magic errno value that means "don't touch CPU state".


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7194 c046a42c-6fe2-441c-8c8c-71466251a162
linux-user/main.c
@@ -1858,6 +1858,11 @@ void cpu_loop(CPUMIPSState *env) @@ -1858,6 +1858,11 @@ void cpu_loop(CPUMIPSState *env)
1858 env->active_tc.gpr[7], 1858 env->active_tc.gpr[7],
1859 arg5, arg6/*, arg7, arg8*/); 1859 arg5, arg6/*, arg7, arg8*/);
1860 } 1860 }
  1861 + if (ret == -TARGET_QEMU_ESIGRETURN) {
  1862 + /* Returning from a successful sigreturn syscall.
  1863 + Avoid clobbering register state. */
  1864 + break;
  1865 + }
1861 if ((unsigned int)ret >= (unsigned int)(-1133)) { 1866 if ((unsigned int)ret >= (unsigned int)(-1133)) {
1862 env->active_tc.gpr[7] = 1; /* error flag */ 1867 env->active_tc.gpr[7] = 1; /* error flag */
1863 ret = -ret; 1868 ret = -ret;
linux-user/mips/syscall.h
@@ -221,4 +221,7 @@ struct target_pt_regs { @@ -221,4 +221,7 @@ struct target_pt_regs {
221 221
222 222
223 223
  224 +/* Nasty hack: define a fake errno value for use by sigreturn. */
  225 +#define TARGET_QEMU_ESIGRETURN 255
  226 +
224 #define UNAME_MACHINE "mips" 227 #define UNAME_MACHINE "mips"
linux-user/signal.c
@@ -2313,6 +2313,21 @@ struct sigframe { @@ -2313,6 +2313,21 @@ struct sigframe {
2313 target_sigset_t sf_mask; 2313 target_sigset_t sf_mask;
2314 }; 2314 };
2315 2315
  2316 +struct target_ucontext {
  2317 + target_ulong uc_flags;
  2318 + target_ulong uc_link;
  2319 + target_stack_t uc_stack;
  2320 + struct target_sigcontext uc_mcontext;
  2321 + target_sigset_t uc_sigmask;
  2322 +};
  2323 +
  2324 +struct target_rt_sigframe {
  2325 + uint32_t rs_ass[4]; /* argument save space for o32 */
  2326 + uint32_t rs_code[2]; /* signal trampoline */
  2327 + struct target_siginfo rs_info;
  2328 + struct target_ucontext rs_uc;
  2329 +};
  2330 +
2316 /* Install trampoline to jump back from signal handler */ 2331 /* Install trampoline to jump back from signal handler */
2317 static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) 2332 static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
2318 { 2333 {
@@ -2592,7 +2607,7 @@ long do_sigreturn(CPUState *regs) @@ -2592,7 +2607,7 @@ long do_sigreturn(CPUState *regs)
2592 /* I am not sure this is right, but it seems to work 2607 /* I am not sure this is right, but it seems to work
2593 * maybe a problem with nested signals ? */ 2608 * maybe a problem with nested signals ? */
2594 regs->CP0_EPC = 0; 2609 regs->CP0_EPC = 0;
2595 - return 0; 2610 + return -TARGET_QEMU_ESIGRETURN;
2596 2611
2597 badframe: 2612 badframe:
2598 force_sig(TARGET_SIGSEGV/*, current*/); 2613 force_sig(TARGET_SIGSEGV/*, current*/);
@@ -2603,13 +2618,95 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -2603,13 +2618,95 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka,
2603 target_siginfo_t *info, 2618 target_siginfo_t *info,
2604 target_sigset_t *set, CPUState *env) 2619 target_sigset_t *set, CPUState *env)
2605 { 2620 {
2606 - fprintf(stderr, "setup_rt_frame: not implemented\n"); 2621 + struct target_rt_sigframe *frame;
  2622 + abi_ulong frame_addr;
  2623 + int i;
  2624 +
  2625 + frame_addr = get_sigframe(ka, env, sizeof(*frame));
  2626 + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
  2627 + goto give_sigsegv;
  2628 +
  2629 + install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
  2630 +
  2631 + copy_siginfo_to_user(&frame->rs_info, info);
  2632 +
  2633 + __put_user(0, &frame->rs_uc.uc_flags);
  2634 + __put_user(0, &frame->rs_uc.uc_link);
  2635 + __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.uc_stack.ss_sp);
  2636 + __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.uc_stack.ss_size);
  2637 + __put_user(sas_ss_flags(get_sp_from_cpustate(env)),
  2638 + &frame->rs_uc.uc_stack.ss_flags);
  2639 +
  2640 + setup_sigcontext(env, &frame->rs_uc.uc_mcontext);
  2641 +
  2642 + for(i = 0; i < TARGET_NSIG_WORDS; i++) {
  2643 + __put_user(set->sig[i], &frame->rs_uc.uc_sigmask.sig[i]);
  2644 + }
  2645 +
  2646 + /*
  2647 + * Arguments to signal handler:
  2648 + *
  2649 + * a0 = signal number
  2650 + * a1 = pointer to struct siginfo
  2651 + * a2 = pointer to struct ucontext
  2652 + *
  2653 + * $25 and PC point to the signal handler, $29 points to the
  2654 + * struct sigframe.
  2655 + */
  2656 + env->active_tc.gpr[ 4] = sig;
  2657 + env->active_tc.gpr[ 5] = frame_addr
  2658 + + offsetof(struct target_rt_sigframe, rs_info);
  2659 + env->active_tc.gpr[ 6] = frame_addr
  2660 + + offsetof(struct target_rt_sigframe, rs_uc);
  2661 + env->active_tc.gpr[29] = frame_addr;
  2662 + env->active_tc.gpr[31] = frame_addr
  2663 + + offsetof(struct target_rt_sigframe, rs_code);
  2664 + /* The original kernel code sets CP0_EPC to the handler
  2665 + * since it returns to userland using eret
  2666 + * we cannot do this here, and we must set PC directly */
  2667 + env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
  2668 + unlock_user_struct(frame, frame_addr, 1);
  2669 + return;
  2670 +
  2671 +give_sigsegv:
  2672 + unlock_user_struct(frame, frame_addr, 1);
  2673 + force_sig(TARGET_SIGSEGV/*, current*/);
  2674 + return;
2607 } 2675 }
2608 2676
2609 long do_rt_sigreturn(CPUState *env) 2677 long do_rt_sigreturn(CPUState *env)
2610 { 2678 {
2611 - fprintf(stderr, "do_rt_sigreturn: not implemented\n");  
2612 - return -TARGET_ENOSYS; 2679 + struct target_rt_sigframe *frame;
  2680 + abi_ulong frame_addr;
  2681 + sigset_t blocked;
  2682 +
  2683 +#if defined(DEBUG_SIGNAL)
  2684 + fprintf(stderr, "do_rt_sigreturn\n");
  2685 +#endif
  2686 + frame_addr = env->active_tc.gpr[29];
  2687 + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
  2688 + goto badframe;
  2689 +
  2690 + target_to_host_sigset(&blocked, &frame->rs_uc.uc_sigmask);
  2691 + sigprocmask(SIG_SETMASK, &blocked, NULL);
  2692 +
  2693 + if (restore_sigcontext(env, &frame->rs_uc.uc_mcontext))
  2694 + goto badframe;
  2695 +
  2696 + if (do_sigaltstack(frame_addr +
  2697 + offsetof(struct target_rt_sigframe, rs_uc.uc_stack),
  2698 + 0, get_sp_from_cpustate(env)) == -EFAULT)
  2699 + goto badframe;
  2700 +
  2701 + env->active_tc.PC = env->CP0_EPC;
  2702 + /* I am not sure this is right, but it seems to work
  2703 + * maybe a problem with nested signals ? */
  2704 + env->CP0_EPC = 0;
  2705 + return -TARGET_QEMU_ESIGRETURN;
  2706 +
  2707 +badframe:
  2708 + force_sig(TARGET_SIGSEGV/*, current*/);
  2709 + return 0;
2613 } 2710 }
2614 2711
2615 #elif defined(TARGET_SH4) 2712 #elif defined(TARGET_SH4)