The setup_sigcontect/restore_sigcontext might sleep on put_user/get_user with preemption disabled (i.e. atomic context). Sleeping in atomic context is not allowed. This patch fixes this problem using temporary variable (struct sigcontext tmpsc). Another possible fix might be rewriting restore_fp_context/save_fp_context to copy to/from current thread_struct and use them with restore_fp/save_fp. Signed-off-by: Atsushi Nemoto <anemo@xxxxxxxxxxxxx> diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h --- a/arch/mips/kernel/signal-common.h +++ b/arch/mips/kernel/signal-common.h @@ -14,6 +14,7 @@ static inline int setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { int err = 0; + struct sigcontext tmpsc; err |= __put_user(regs->cp0_epc, &sc->sc_pc); @@ -73,10 +74,15 @@ setup_sigcontext(struct pt_regs *regs, s own_fpu(); restore_fp(current); } - err |= save_fp_context(sc); + /* make sure save_fp_context not sleep */ + err |= save_fp_context(&tmpsc); preempt_enable(); + err |= __copy_to_user(&sc->sc_fpregs, &tmpsc.sc_fpregs, + sizeof(tmpsc.sc_fpregs)); + err |= __put_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr); + out: return err; } @@ -138,14 +144,18 @@ restore_sigcontext(struct pt_regs *regs, err |= __get_user(used_math, &sc->sc_used_math); conditional_used_math(used_math); - preempt_disable(); - if (used_math()) { + /* make sure restore_fp_context not sleep */ + struct sigcontext tmpsc; + err |= __copy_from_user(&tmpsc.sc_fpregs, &sc->sc_fpregs, sizeof(tmpsc.sc_fpregs)); + err |= __get_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr); + preempt_disable(); /* restore fpu context if we have used it before */ own_fpu(); - err |= restore_fp_context(sc); + err |= restore_fp_context(&tmpsc); } else { /* signal handler may have used FPU. Give it up. */ + preempt_disable(); lose_fpu(); } diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -376,14 +376,18 @@ static int restore_sigcontext32(struct p err |= __get_user(used_math, &sc->sc_used_math); conditional_used_math(used_math); - preempt_disable(); - if (used_math()) { + struct sigcontext32 tmpsc; + /* make sure restore_fp_context32 not sleep */ + err |= __copy_from_user(&tmpsc.sc_fpregs, &sc->sc_fpregs, sizeof(tmpsc.sc_fpregs)); + err |= __get_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr); + preempt_disable(); /* restore fpu context if we have used it before */ own_fpu(); - err |= restore_fp_context32(sc); + err |= restore_fp_context32(&tmpsc); } else { /* signal handler may have used FPU. Give it up. */ + preempt_disable(); lose_fpu(); } @@ -569,6 +573,7 @@ static inline int setup_sigcontext32(str struct sigcontext32 *sc) { int err = 0; + struct sigcontext32 tmpsc; err |= __put_user(regs->cp0_epc, &sc->sc_pc); err |= __put_user(regs->cp0_status, &sc->sc_status); @@ -614,10 +619,15 @@ static inline int setup_sigcontext32(str own_fpu(); restore_fp(current); } - err |= save_fp_context32(sc); + /* make sure save_fp_context32 not sleep */ + err |= save_fp_context32(&tmpsc); preempt_enable(); + err |= __copy_to_user(&sc->sc_fpregs, &tmpsc.sc_fpregs, + sizeof(tmpsc.sc_fpregs)); + err |= __put_user(tmpsc.sc_fpc_csr, &sc->sc_fpc_csr); + out: return err; }