On Thu, 08 Feb 2007 12:02:19 +0900 (JST), Atsushi Nemoto <anemo@xxxxxxxxxxxxx> wrote: > So we can do it in C, but it will conflicts with Franck's signal > cleanup patchset. I hope his patchset applied first. > > Also I wonder SIGSEGV is correct behavior on this case. SIGFPE? And I found that if signal handler set FPU_CSR_UNI_X, kernel complains "FP exception in kernel code" ... Here is a first cut. Changes in r4k_fpu.S can be reverted, and after Franck's patchset applied, this patch can be a bit smaller. Please review. Note that calling __get_user(), __put_user() might lose FPU ownership. But restore_fp_context() already has this breakage. I already sent two alternative patches to fix this longstanding issue ("[PATCH] rewrite restore_fp_context/save_fp_context" and "[PATCH 2/3] Allow CpU exception in kernel partially"). diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h index b1f09d5..0811298 100644 --- a/arch/mips/kernel/signal-common.h +++ b/arch/mips/kernel/signal-common.h @@ -66,6 +66,38 @@ out: } static inline int +fpcsr_pending(unsigned int __user *fpcsr) +{ + int err, sig = 0; + unsigned int csr, enabled; + + err = __get_user(csr, fpcsr); + enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5); + /* + * If the signal handler set some FPU exceptions, clear it and + * send SIGFPE. + */ + if (csr & enabled) { + csr &= ~enabled; + err |= __put_user(csr, fpcsr); + sig = SIGFPE; + } + return err ?: sig; +} + +static inline int +check_and_restore_fp_context(struct sigcontext __user *sc) +{ + int err, sig; + + err = sig = fpcsr_pending(&sc->sc_fpc_csr); + if (err > 0) + err = 0; + err |= restore_fp_context(sc); + return err ?: sig; +} + +static inline int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { unsigned int used_math; @@ -112,7 +144,8 @@ restore_sigcontext(struct pt_regs *regs, if (used_math()) { /* restore fpu context if we have used it before */ own_fpu(); - err |= restore_fp_context(sc); + if (!err) + err = check_and_restore_fp_context(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(); diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 9a44053..31d8ec0 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -190,6 +190,7 @@ _sys_sigreturn(nabi_no_regargs struct pt { struct sigframe __user *frame; sigset_t blocked; + int sig; frame = (struct sigframe __user *) regs.regs[29]; if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -203,8 +204,12 @@ _sys_sigreturn(nabi_no_regargs struct pt recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(®s, &frame->sf_sc)) + sig = restore_sigcontext(®s, &frame->sf_sc); goto badframe; + if (sig < 0) + goto badframe; + else if (sig) + force_sig(sig, current); /* * Don't let your children do this ... @@ -228,6 +233,7 @@ _sys_rt_sigreturn(nabi_no_regargs struct struct rt_sigframe __user *frame; sigset_t set; stack_t st; + int sig; frame = (struct rt_sigframe __user *) regs.regs[29]; if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -241,8 +247,11 @@ _sys_rt_sigreturn(nabi_no_regargs struct recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) + sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); + if (sig < 0) goto badframe; + else if (sig) + force_sig(sig, current); if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) goto badframe; diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index c86a5dd..51eb21d 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -326,6 +326,38 @@ asmlinkage int sys32_sigaltstack(nabi_no return ret; } +static inline int +fpcsr_pending(unsigned int __user *fpcsr) +{ + int err, sig = 0; + unsigned int csr, enabled; + + err = __get_user(csr, fpcsr); + enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5); + /* + * If the signal handler set some FPU exceptions, clear it and + * send SIGFPE. + */ + if (csr & enabled) { + csr &= ~enabled; + err |= __put_user(csr, fpcsr); + sig = SIGFPE; + } + return err ?: sig; +} + +static inline int +check_and_restore_fp_context32(struct sigcontext32 __user *sc) +{ + int err, sig; + + sig = fpcsr_pending(&sc->sc_fpc_csr); + if (sig < 0) + err = sig; + err |= restore_fp_context32(sc); + return err ?: sig; +} + static int restore_sigcontext32(struct pt_regs *regs, struct sigcontext32 __user *sc) { u32 used_math; @@ -372,7 +404,8 @@ static int restore_sigcontext32(struct p if (used_math()) { /* restore fpu context if we have used it before */ own_fpu(); - err |= restore_fp_context32(sc); + if (!err) + err = check_and_restore_fp_context(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(); @@ -469,6 +502,7 @@ _sys32_sigreturn(nabi_no_regargs struct { struct sigframe __user *frame; sigset_t blocked; + int sig; frame = (struct sigframe __user *) regs.regs[29]; if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -482,8 +516,11 @@ _sys32_sigreturn(nabi_no_regargs struct recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext32(®s, &frame->sf_sc)) + sig = restore_sigcontext32(®s, &frame->sf_sc); + if (sig < 0) goto badframe; + else if (sig) + force_sig(sig, current); /* * Don't let your children do this ...