linux-mips
[Top] [All Lists]

Re: fpu_emulator can lose fpu on get_user/put_user

To: linux-mips@linux-mips.org
Subject: Re: fpu_emulator can lose fpu on get_user/put_user
From: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Date: Thu, 07 Oct 2004 15:20:17 +0900 (JST)
Cc: ralf@linux-mips.org
In-reply-to: <20041006.101920.126571873.nemoto@toshiba-tops.co.jp>
Original-recipient: rfc822;linux-mips@linux-mips.org
References: <20041006.101920.126571873.nemoto@toshiba-tops.co.jp>
Sender: linux-mips-bounce@linux-mips.org
>>>>> On Wed, 06 Oct 2004 10:19:20 +0900 (JST), Atsushi Nemoto 
>>>>> <anemo@mba.ocn.ne.jp> said:
anemo> I found a potential problem in math emulation.  The math-emu
anemo> uses put_user/get_user to fetch the instruction or to emulate
anemo> load/store fp-regs.  The put_user/get_user can sleep then we
anemo> can lose fpu ownership on it.  It it happened, subsequent
anemo> restore_fp will cause CpU exception which not allowed in
anemo> kernel.

And there are similar potential problem in setup/restore sigcontext.
save_fp_context/restore_fp_context might sleep on put_user/get_user.

This is a quick fix for 2.6 kernel.  Another possible fix is rewriting
restore_fp_context/save_fp_context to copy to/from current
thread_struct and use them with restore_fp/save_fp.  Is this better?
Any comment are welcome.


diff -up linux-mips/arch/mips/kernel/signal.c linux/arch/mips/kernel/signal.c
--- linux-mips/arch/mips/kernel/signal.c        Wed Sep 22 13:27:59 2004
+++ linux/arch/mips/kernel/signal.c     Thu Oct  7 14:47:23 2004
@@ -182,9 +182,14 @@ asmlinkage int restore_sigcontext(struct
        err |= __get_user(current->used_math, &sc->sc_used_math);
 
        if (current->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);
+               err |= __get_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
                /* 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. */
                lose_fpu();
@@ -291,6 +296,7 @@ badframe:
 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);
        err |= __put_user(regs->cp0_status, &sc->sc_status);
@@ -327,7 +333,12 @@ inline int setup_sigcontext(struct pt_re
                own_fpu();
                restore_fp(current);
        }
-       err |= save_fp_context(sc);
+       /* make sure save_fp_context not sleep */
+       err |= save_fp_context(&tmpsc);
+       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);
+       err |= __put_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
 
 out:
        return err;
diff -up linux-mips/arch/mips/kernel/signal32.c 
linux/arch/mips/kernel/signal32.c
--- linux-mips/arch/mips/kernel/signal32.c      Wed Sep 22 13:27:59 2004
+++ linux/arch/mips/kernel/signal32.c   Thu Oct  7 14:47:39 2004
@@ -365,9 +365,14 @@ static asmlinkage int restore_sigcontext
        err |= __get_user(current->used_math, &sc->sc_used_math);
 
        if (current->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);
+               err |= __get_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
                /* 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. */
                lose_fpu();
@@ -526,6 +531,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);
@@ -562,7 +568,12 @@ 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);
+       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);
+       err |= __put_user(tmpsc.sc_fpc_eir, &sc->sc_fpc_eir);
 
 out:
        return err;

---
Atsushi Nemoto

<Prev in Thread] Current Thread [Next in Thread>