From: "David S. Miller" <da...@davemloft.net>

3.12-stable review patch.  If anyone has any objections, please let me know.

===============

[ Upstream commit d11c2a0de2824395656cf8ed15811580c9dd38aa ]

All signal frames must be at least 16-byte aligned, because that is
the alignment we explicitly create when we build signal return stack
frames.

All stack pointers must be at least 8-byte aligned.

Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Jiri Slaby <jsl...@suse.cz>
---
 arch/sparc/kernel/signal32.c   | 46 +++++++++++++++++++++++++++---------------
 arch/sparc/kernel/signal_32.c  | 41 +++++++++++++++++++++++--------------
 arch/sparc/kernel/signal_64.c  | 31 ++++++++++++++++++----------
 arch/sparc/kernel/sigutil_32.c |  9 ++++++++-
 arch/sparc/kernel/sigutil_64.c | 10 +++++++--
 5 files changed, 92 insertions(+), 45 deletions(-)

diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index b524f91dd0e5..d45b112908c1 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -137,12 +137,24 @@ int copy_siginfo_from_user32(siginfo_t *to, 
compat_siginfo_t __user *from)
        return 0;
 }
 
+/* Checks if the fp is valid.  We always build signal frames which are
+ * 16-byte aligned, therefore we can always enforce that the restore
+ * frame has that property as well.
+ */
+static bool invalid_frame_pointer(void __user *fp, int fplen)
+{
+       if ((((unsigned long) fp) & 15) ||
+           ((unsigned long)fp) > 0x100000000ULL - fplen)
+               return true;
+       return false;
+}
+
 void do_sigreturn32(struct pt_regs *regs)
 {
        struct signal_frame32 __user *sf;
        compat_uptr_t fpu_save;
        compat_uptr_t rwin_save;
-       unsigned int psr;
+       unsigned int psr, ufp;
        unsigned pc, npc;
        sigset_t set;
        unsigned seta[_COMPAT_NSIG_WORDS];
@@ -157,11 +169,16 @@ void do_sigreturn32(struct pt_regs *regs)
        sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP];
 
        /* 1. Make sure we are not getting garbage from the user */
-       if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
-           (((unsigned long) sf) & 3))
+       if (invalid_frame_pointer(sf, sizeof(*sf)))
+               goto segv;
+
+       if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
+               goto segv;
+
+       if (ufp & 0x7)
                goto segv;
 
-       if (get_user(pc, &sf->info.si_regs.pc) ||
+       if (__get_user(pc, &sf->info.si_regs.pc) ||
            __get_user(npc, &sf->info.si_regs.npc))
                goto segv;
 
@@ -230,7 +247,7 @@ segv:
 asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
 {
        struct rt_signal_frame32 __user *sf;
-       unsigned int psr, pc, npc;
+       unsigned int psr, pc, npc, ufp;
        compat_uptr_t fpu_save;
        compat_uptr_t rwin_save;
        sigset_t set;
@@ -245,11 +262,16 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
        sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP];
 
        /* 1. Make sure we are not getting garbage from the user */
-       if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
-           (((unsigned long) sf) & 3))
+       if (invalid_frame_pointer(sf, sizeof(*sf)))
                goto segv;
 
-       if (get_user(pc, &sf->regs.pc) || 
+       if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
+               goto segv;
+
+       if (ufp & 0x7)
+               goto segv;
+
+       if (__get_user(pc, &sf->regs.pc) || 
            __get_user(npc, &sf->regs.npc))
                goto segv;
 
@@ -315,14 +337,6 @@ segv:
        force_sig(SIGSEGV, current);
 }
 
-/* Checks if the fp is valid */
-static int invalid_frame_pointer(void __user *fp, int fplen)
-{
-       if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x100000000ULL 
- fplen)
-               return 1;
-       return 0;
-}
-
 static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, 
unsigned long framesize)
 {
        unsigned long sp;
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c
index 7d5d8e1f8415..e751dbc527e2 100644
--- a/arch/sparc/kernel/signal_32.c
+++ b/arch/sparc/kernel/signal_32.c
@@ -59,10 +59,22 @@ struct rt_signal_frame {
 #define SF_ALIGNEDSZ  (((sizeof(struct signal_frame) + 7) & (~7)))
 #define RT_ALIGNEDSZ  (((sizeof(struct rt_signal_frame) + 7) & (~7)))
 
+/* Checks if the fp is valid.  We always build signal frames which are
+ * 16-byte aligned, therefore we can always enforce that the restore
+ * frame has that property as well.
+ */
+static inline bool invalid_frame_pointer(void __user *fp, int fplen)
+{
+       if ((((unsigned long) fp) & 15) || !__access_ok((unsigned long)fp, 
fplen))
+               return true;
+
+       return false;
+}
+
 asmlinkage void do_sigreturn(struct pt_regs *regs)
 {
+       unsigned long up_psr, pc, npc, ufp;
        struct signal_frame __user *sf;
-       unsigned long up_psr, pc, npc;
        sigset_t set;
        __siginfo_fpu_t __user *fpu_save;
        __siginfo_rwin_t __user *rwin_save;
@@ -76,10 +88,13 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)
        sf = (struct signal_frame __user *) regs->u_regs[UREG_FP];
 
        /* 1. Make sure we are not getting garbage from the user */
-       if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
+       if (!invalid_frame_pointer(sf, sizeof(*sf)))
+               goto segv_and_exit;
+
+       if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
                goto segv_and_exit;
 
-       if (((unsigned long) sf) & 3)
+       if (ufp & 0x7)
                goto segv_and_exit;
 
        err = __get_user(pc,  &sf->info.si_regs.pc);
@@ -126,7 +141,7 @@ segv_and_exit:
 asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
 {
        struct rt_signal_frame __user *sf;
-       unsigned int psr, pc, npc;
+       unsigned int psr, pc, npc, ufp;
        __siginfo_fpu_t __user *fpu_save;
        __siginfo_rwin_t __user *rwin_save;
        sigset_t set;
@@ -134,8 +149,13 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
 
        synchronize_user_stack();
        sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP];
-       if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
-           (((unsigned long) sf) & 0x03))
+       if (!invalid_frame_pointer(sf, sizeof(*sf)))
+               goto segv;
+
+       if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
+               goto segv;
+
+       if (ufp & 0x7)
                goto segv;
 
        err = __get_user(pc, &sf->regs.pc);
@@ -177,15 +197,6 @@ segv:
        force_sig(SIGSEGV, current);
 }
 
-/* Checks if the fp is valid */
-static inline int invalid_frame_pointer(void __user *fp, int fplen)
-{
-       if ((((unsigned long) fp) & 7) || !__access_ok((unsigned long)fp, 
fplen))
-               return 1;
-
-       return 0;
-}
-
 static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs 
*regs, unsigned long framesize)
 {
        unsigned long sp = regs->u_regs[UREG_FP];
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index 90f08055b7d2..5a2e50bcc3b8 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -226,6 +226,17 @@ do_sigsegv:
        force_sig(SIGSEGV, current);
 }
 
+/* Checks if the fp is valid.  We always build rt signal frames which
+ * are 16-byte aligned, therefore we can always enforce that the
+ * restore frame has that property as well.
+ */
+static bool invalid_frame_pointer(void __user *fp)
+{
+       if (((unsigned long) fp) & 15)
+               return true;
+       return false;
+}
+
 struct rt_signal_frame {
        struct sparc_stackf     ss;
        siginfo_t               info;
@@ -238,8 +249,8 @@ struct rt_signal_frame {
 
 void do_rt_sigreturn(struct pt_regs *regs)
 {
+       unsigned long tpc, tnpc, tstate, ufp;
        struct rt_signal_frame __user *sf;
-       unsigned long tpc, tnpc, tstate;
        __siginfo_fpu_t __user *fpu_save;
        __siginfo_rwin_t __user *rwin_save;
        sigset_t set;
@@ -253,10 +264,16 @@ void do_rt_sigreturn(struct pt_regs *regs)
                (regs->u_regs [UREG_FP] + STACK_BIAS);
 
        /* 1. Make sure we are not getting garbage from the user */
-       if (((unsigned long) sf) & 3)
+       if (invalid_frame_pointer(sf))
+               goto segv;
+
+       if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
                goto segv;
 
-       err = get_user(tpc, &sf->regs.tpc);
+       if ((ufp + STACK_BIAS) & 0x7)
+               goto segv;
+
+       err = __get_user(tpc, &sf->regs.tpc);
        err |= __get_user(tnpc, &sf->regs.tnpc);
        if (test_thread_flag(TIF_32BIT)) {
                tpc &= 0xffffffff;
@@ -300,14 +317,6 @@ segv:
        force_sig(SIGSEGV, current);
 }
 
-/* Checks if the fp is valid */
-static int invalid_frame_pointer(void __user *fp)
-{
-       if (((unsigned long) fp) & 15)
-               return 1;
-       return 0;
-}
-
 static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs 
*regs, unsigned long framesize)
 {
        unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS;
diff --git a/arch/sparc/kernel/sigutil_32.c b/arch/sparc/kernel/sigutil_32.c
index 0f6eebe71e6c..e5fe8cef9a69 100644
--- a/arch/sparc/kernel/sigutil_32.c
+++ b/arch/sparc/kernel/sigutil_32.c
@@ -48,6 +48,10 @@ int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t 
__user *fpu)
 int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
 {
        int err;
+
+       if (((unsigned long) fpu) & 3)
+               return -EFAULT;
+
 #ifdef CONFIG_SMP
        if (test_tsk_thread_flag(current, TIF_USEDFPU))
                regs->psr &= ~PSR_EF;
@@ -97,7 +101,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp)
        struct thread_info *t = current_thread_info();
        int i, wsaved, err;
 
-       __get_user(wsaved, &rp->wsaved);
+       if (((unsigned long) rp) & 3)
+               return -EFAULT;
+
+       get_user(wsaved, &rp->wsaved);
        if (wsaved > NSWINS)
                return -EFAULT;
 
diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c
index 387834a9c56a..36aadcbeac69 100644
--- a/arch/sparc/kernel/sigutil_64.c
+++ b/arch/sparc/kernel/sigutil_64.c
@@ -37,7 +37,10 @@ int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t 
__user *fpu)
        unsigned long fprs;
        int err;
 
-       err = __get_user(fprs, &fpu->si_fprs);
+       if (((unsigned long) fpu) & 7)
+               return -EFAULT;
+
+       err = get_user(fprs, &fpu->si_fprs);
        fprs_write(0);
        regs->tstate &= ~TSTATE_PEF;
        if (fprs & FPRS_DL)
@@ -72,7 +75,10 @@ int restore_rwin_state(__siginfo_rwin_t __user *rp)
        struct thread_info *t = current_thread_info();
        int i, wsaved, err;
 
-       __get_user(wsaved, &rp->wsaved);
+       if (((unsigned long) rp) & 7)
+               return -EFAULT;
+
+       get_user(wsaved, &rp->wsaved);
        if (wsaved > NSWINS)
                return -EFAULT;
 
-- 
2.9.1

Reply via email to