Merge tag 'powerpc-5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[linux-2.6-microblaze.git] / arch / powerpc / kernel / signal_32.c
index f651b99..8f05ed0 100644 (file)
  * implementation that makes things simple for little endian only)
  */
 #define unsafe_put_sigset_t    unsafe_put_compat_sigset
-
-static inline int get_sigset_t(sigset_t *set,
-                              const compat_sigset_t __user *uset)
-{
-       return get_compat_sigset(set, uset);
-}
+#define unsafe_get_sigset_t    unsafe_get_compat_sigset
 
 #define to_user_ptr(p)         ptr_to_compat(p)
 #define from_user_ptr(p)       compat_ptr(p)
 
 static __always_inline int
-save_general_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame)
+__unsafe_save_general_regs(struct pt_regs *regs, struct mcontext __user *frame)
 {
        elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
        int val, i;
 
-       WARN_ON(!FULL_REGS(regs));
-
        for (i = 0; i <= PT_RESULT; i ++) {
                /* Force usr to alway see softe as 1 (interrupts enabled) */
                if (i == PT_SOFTE)
@@ -116,8 +109,8 @@ failed:
        return 1;
 }
 
-static inline int restore_general_regs(struct pt_regs *regs,
-               struct mcontext __user *sr)
+static __always_inline int
+__unsafe_restore_general_regs(struct pt_regs *regs, struct mcontext __user *sr)
 {
        elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
        int i;
@@ -125,10 +118,12 @@ static inline int restore_general_regs(struct pt_regs *regs,
        for (i = 0; i <= PT_RESULT; i++) {
                if ((i == PT_MSR) || (i == PT_SOFTE))
                        continue;
-               if (__get_user(gregs[i], &sr->mc_gregs[i]))
-                       return -EFAULT;
+               unsafe_get_user(gregs[i], &sr->mc_gregs[i], failed);
        }
        return 0;
+
+failed:
+       return 1;
 }
 
 #else /* CONFIG_PPC64 */
@@ -142,18 +137,14 @@ static inline int restore_general_regs(struct pt_regs *regs,
        unsafe_copy_to_user(__us, __s, sizeof(*__us), label);           \
 } while (0)
 
-static inline int get_sigset_t(sigset_t *set, const sigset_t __user *uset)
-{
-       return copy_from_user(set, uset, sizeof(*uset));
-}
+#define unsafe_get_sigset_t    unsafe_get_user_sigset
 
 #define to_user_ptr(p)         ((unsigned long)(p))
 #define from_user_ptr(p)       ((void __user *)(p))
 
 static __always_inline int
-save_general_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame)
+__unsafe_save_general_regs(struct pt_regs *regs, struct mcontext __user *frame)
 {
-       WARN_ON(!FULL_REGS(regs));
        unsafe_copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE, failed);
        return 0;
 
@@ -161,23 +152,30 @@ failed:
        return 1;
 }
 
-static inline int restore_general_regs(struct pt_regs *regs,
-               struct mcontext __user *sr)
+static __always_inline
+int __unsafe_restore_general_regs(struct pt_regs *regs, struct mcontext __user *sr)
 {
        /* copy up to but not including MSR */
-       if (__copy_from_user(regs, &sr->mc_gregs,
-                               PT_MSR * sizeof(elf_greg_t)))
-               return -EFAULT;
+       unsafe_copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t), failed);
+
        /* copy from orig_r3 (the word after the MSR) up to the end */
-       if (__copy_from_user(&regs->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
-                               GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
-               return -EFAULT;
+       unsafe_copy_from_user(&regs->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
+                             GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t), failed);
+
        return 0;
+
+failed:
+       return 1;
 }
 #endif
 
 #define unsafe_save_general_regs(regs, frame, label) do {      \
-       if (save_general_regs_unsafe(regs, frame))      \
+       if (__unsafe_save_general_regs(regs, frame))            \
+               goto label;                                     \
+} while (0)
+
+#define unsafe_restore_general_regs(regs, frame, label) do {   \
+       if (__unsafe_restore_general_regs(regs, frame))         \
                goto label;                                     \
 } while (0)
 
@@ -260,8 +258,8 @@ static void prepare_save_user_regs(int ctx_has_vsx_region)
 #endif
 }
 
-static int save_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user *frame,
-                                struct mcontext __user *tm_frame, int ctx_has_vsx_region)
+static int __unsafe_save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
+                                  struct mcontext __user *tm_frame, int ctx_has_vsx_region)
 {
        unsigned long msr = regs->msr;
 
@@ -338,7 +336,7 @@ failed:
 }
 
 #define unsafe_save_user_regs(regs, frame, tm_frame, has_vsx, label) do { \
-       if (save_user_regs_unsafe(regs, frame, tm_frame, has_vsx))      \
+       if (__unsafe_save_user_regs(regs, frame, tm_frame, has_vsx))    \
                goto label;                                             \
 } while (0)
 
@@ -350,7 +348,7 @@ failed:
  * We also save the transactional registers to a second ucontext in the
  * frame.
  *
- * See save_user_regs_unsafe() and signal_64.c:setup_tm_sigcontexts().
+ * See __unsafe_save_user_regs() and signal_64.c:setup_tm_sigcontexts().
  */
 static void prepare_save_tm_user_regs(void)
 {
@@ -441,7 +439,7 @@ static int save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user
 #endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
        /* SPE regs are not checkpointed with TM, so this section is
-        * simply the same as in save_user_regs_unsafe().
+        * simply the same as in __unsafe_save_user_regs().
         */
        if (current->thread.used_spe) {
                unsafe_copy_to_user(&frame->mc_vregs, current->thread.evr,
@@ -485,26 +483,25 @@ static int save_tm_user_regs_unsafe(struct pt_regs *regs, struct mcontext __user
 static long restore_user_regs(struct pt_regs *regs,
                              struct mcontext __user *sr, int sig)
 {
-       long err;
        unsigned int save_r2 = 0;
        unsigned long msr;
 #ifdef CONFIG_VSX
        int i;
 #endif
 
+       if (!user_read_access_begin(sr, sizeof(*sr)))
+               return 1;
        /*
         * restore general registers but not including MSR or SOFTE. Also
         * take care of keeping r2 (TLS) intact if not a signal
         */
        if (!sig)
                save_r2 = (unsigned int)regs->gpr[2];
-       err = restore_general_regs(regs, sr);
+       unsafe_restore_general_regs(regs, sr, failed);
        set_trap_norestart(regs);
-       err |= __get_user(msr, &sr->mc_gregs[PT_MSR]);
+       unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
        if (!sig)
                regs->gpr[2] = (unsigned long) save_r2;
-       if (err)
-               return 1;
 
        /* if doing signal return, restore the previous little-endian mode */
        if (sig)
@@ -518,22 +515,19 @@ static long restore_user_regs(struct pt_regs *regs,
        regs->msr &= ~MSR_VEC;
        if (msr & MSR_VEC) {
                /* restore altivec registers from the stack */
-               if (__copy_from_user(&current->thread.vr_state, &sr->mc_vregs,
-                                    sizeof(sr->mc_vregs)))
-                       return 1;
+               unsafe_copy_from_user(&current->thread.vr_state, &sr->mc_vregs,
+                                     sizeof(sr->mc_vregs), failed);
                current->thread.used_vr = true;
        } else if (current->thread.used_vr)
                memset(&current->thread.vr_state, 0,
                       ELF_NVRREG * sizeof(vector128));
 
        /* Always get VRSAVE back */
-       if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32]))
-               return 1;
+       unsafe_get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32], failed);
        if (cpu_has_feature(CPU_FTR_ALTIVEC))
                mtspr(SPRN_VRSAVE, current->thread.vrsave);
 #endif /* CONFIG_ALTIVEC */
-       if (copy_fpr_from_user(current, &sr->mc_fregs))
-               return 1;
+       unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
 
 #ifdef CONFIG_VSX
        /*
@@ -546,8 +540,7 @@ static long restore_user_regs(struct pt_regs *regs,
                 * Restore altivec registers from the stack to a local
                 * buffer, then write this out to the thread_struct
                 */
-               if (copy_vsx_from_user(current, &sr->mc_vsregs))
-                       return 1;
+               unsafe_copy_vsx_from_user(current, &sr->mc_vsregs, failed);
                current->thread.used_vsr = true;
        } else if (current->thread.used_vsr)
                for (i = 0; i < 32 ; i++)
@@ -565,19 +558,22 @@ static long restore_user_regs(struct pt_regs *regs,
        regs->msr &= ~MSR_SPE;
        if (msr & MSR_SPE) {
                /* restore spe registers from the stack */
-               if (__copy_from_user(current->thread.evr, &sr->mc_vregs,
-                                    ELF_NEVRREG * sizeof(u32)))
-                       return 1;
+               unsafe_copy_from_user(current->thread.evr, &sr->mc_vregs,
+                                     ELF_NEVRREG * sizeof(u32), failed);
                current->thread.used_spe = true;
        } else if (current->thread.used_spe)
                memset(current->thread.evr, 0, ELF_NEVRREG * sizeof(u32));
 
        /* Always get SPEFSCR back */
-       if (__get_user(current->thread.spefscr, (u32 __user *)&sr->mc_vregs + ELF_NEVRREG))
-               return 1;
+       unsafe_get_user(current->thread.spefscr, (u32 __user *)&sr->mc_vregs + ELF_NEVRREG, failed);
 #endif /* CONFIG_SPE */
 
+       user_read_access_end();
        return 0;
+
+failed:
+       user_read_access_end();
+       return 1;
 }
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
@@ -590,7 +586,6 @@ static long restore_tm_user_regs(struct pt_regs *regs,
                                 struct mcontext __user *sr,
                                 struct mcontext __user *tm_sr)
 {
-       long err;
        unsigned long msr, msr_hi;
 #ifdef CONFIG_VSX
        int i;
@@ -605,15 +600,13 @@ static long restore_tm_user_regs(struct pt_regs *regs,
         * TFHAR is restored from the checkpointed NIP; TEXASR and TFIAR
         * were set by the signal delivery.
         */
-       err = restore_general_regs(regs, tm_sr);
-       err |= restore_general_regs(&current->thread.ckpt_regs, sr);
-
-       err |= __get_user(current->thread.tm_tfhar, &sr->mc_gregs[PT_NIP]);
-
-       err |= __get_user(msr, &sr->mc_gregs[PT_MSR]);
-       if (err)
+       if (!user_read_access_begin(sr, sizeof(*sr)))
                return 1;
 
+       unsafe_restore_general_regs(&current->thread.ckpt_regs, sr, failed);
+       unsafe_get_user(current->thread.tm_tfhar, &sr->mc_gregs[PT_NIP], failed);
+       unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
+
        /* Restore the previous little-endian mode */
        regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
 
@@ -621,12 +614,8 @@ static long restore_tm_user_regs(struct pt_regs *regs,
        regs->msr &= ~MSR_VEC;
        if (msr & MSR_VEC) {
                /* restore altivec registers from the stack */
-               if (__copy_from_user(&current->thread.ckvr_state, &sr->mc_vregs,
-                                    sizeof(sr->mc_vregs)) ||
-                   __copy_from_user(&current->thread.vr_state,
-                                    &tm_sr->mc_vregs,
-                                    sizeof(sr->mc_vregs)))
-                       return 1;
+               unsafe_copy_from_user(&current->thread.ckvr_state, &sr->mc_vregs,
+                                     sizeof(sr->mc_vregs), failed);
                current->thread.used_vr = true;
        } else if (current->thread.used_vr) {
                memset(&current->thread.vr_state, 0,
@@ -636,20 +625,15 @@ static long restore_tm_user_regs(struct pt_regs *regs,
        }
 
        /* Always get VRSAVE back */
-       if (__get_user(current->thread.ckvrsave,
-                      (u32 __user *)&sr->mc_vregs[32]) ||
-           __get_user(current->thread.vrsave,
-                      (u32 __user *)&tm_sr->mc_vregs[32]))
-               return 1;
+       unsafe_get_user(current->thread.ckvrsave,
+                       (u32 __user *)&sr->mc_vregs[32], failed);
        if (cpu_has_feature(CPU_FTR_ALTIVEC))
                mtspr(SPRN_VRSAVE, current->thread.ckvrsave);
 #endif /* CONFIG_ALTIVEC */
 
        regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1);
 
-       if (copy_fpr_from_user(current, &sr->mc_fregs) ||
-           copy_ckfpr_from_user(current, &tm_sr->mc_fregs))
-               return 1;
+       unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
 
 #ifdef CONFIG_VSX
        regs->msr &= ~MSR_VSX;
@@ -658,9 +642,7 @@ static long restore_tm_user_regs(struct pt_regs *regs,
                 * Restore altivec registers from the stack to a local
                 * buffer, then write this out to the thread_struct
                 */
-               if (copy_vsx_from_user(current, &tm_sr->mc_vsregs) ||
-                   copy_ckvsx_from_user(current, &sr->mc_vsregs))
-                       return 1;
+               unsafe_copy_ckvsx_from_user(current, &sr->mc_vsregs, failed);
                current->thread.used_vsr = true;
        } else if (current->thread.used_vsr)
                for (i = 0; i < 32 ; i++) {
@@ -675,23 +657,54 @@ static long restore_tm_user_regs(struct pt_regs *regs,
         */
        regs->msr &= ~MSR_SPE;
        if (msr & MSR_SPE) {
-               if (__copy_from_user(current->thread.evr, &sr->mc_vregs,
-                                    ELF_NEVRREG * sizeof(u32)))
-                       return 1;
+               unsafe_copy_from_user(current->thread.evr, &sr->mc_vregs,
+                                     ELF_NEVRREG * sizeof(u32), failed);
                current->thread.used_spe = true;
        } else if (current->thread.used_spe)
                memset(current->thread.evr, 0, ELF_NEVRREG * sizeof(u32));
 
        /* Always get SPEFSCR back */
-       if (__get_user(current->thread.spefscr, (u32 __user *)&sr->mc_vregs
-                      + ELF_NEVRREG))
-               return 1;
+       unsafe_get_user(current->thread.spefscr,
+                       (u32 __user *)&sr->mc_vregs + ELF_NEVRREG, failed);
 #endif /* CONFIG_SPE */
 
-       /* Get the top half of the MSR from the user context */
-       if (__get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR]))
+       user_read_access_end();
+
+       if (!user_read_access_begin(tm_sr, sizeof(*tm_sr)))
                return 1;
+
+       unsafe_restore_general_regs(regs, tm_sr, failed);
+
+#ifdef CONFIG_ALTIVEC
+       /* restore altivec registers from the stack */
+       if (msr & MSR_VEC)
+               unsafe_copy_from_user(&current->thread.vr_state, &tm_sr->mc_vregs,
+                                     sizeof(sr->mc_vregs), failed);
+
+       /* Always get VRSAVE back */
+       unsafe_get_user(current->thread.vrsave,
+                       (u32 __user *)&tm_sr->mc_vregs[32], failed);
+#endif /* CONFIG_ALTIVEC */
+
+       unsafe_copy_ckfpr_from_user(current, &tm_sr->mc_fregs, failed);
+
+#ifdef CONFIG_VSX
+       if (msr & MSR_VSX) {
+               /*
+                * Restore altivec registers from the stack to a local
+                * buffer, then write this out to the thread_struct
+                */
+               unsafe_copy_vsx_from_user(current, &tm_sr->mc_vsregs, failed);
+               current->thread.used_vsr = true;
+       }
+#endif /* CONFIG_VSX */
+
+       /* Get the top half of the MSR from the user context */
+       unsafe_get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR], failed);
        msr_hi <<= 32;
+
+       user_read_access_end();
+
        /* If TM bits are set to the reserved value, it's an invalid context */
        if (MSR_TM_RESV(msr_hi))
                return 1;
@@ -739,6 +752,16 @@ static long restore_tm_user_regs(struct pt_regs *regs,
        preempt_enable();
 
        return 0;
+
+failed:
+       user_read_access_end();
+       return 1;
+}
+#else
+static long restore_tm_user_regs(struct pt_regs *regs, struct mcontext __user *sr,
+                                struct mcontext __user *tm_sr)
+{
+       return 0;
 }
 #endif
 
@@ -944,28 +967,31 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int
        sigset_t set;
        struct mcontext __user *mcp;
 
-       if (get_sigset_t(&set, &ucp->uc_sigmask))
+       if (!user_read_access_begin(ucp, sizeof(*ucp)))
                return -EFAULT;
+
+       unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
 #ifdef CONFIG_PPC64
        {
                u32 cmcp;
 
-               if (__get_user(cmcp, &ucp->uc_regs))
-                       return -EFAULT;
+               unsafe_get_user(cmcp, &ucp->uc_regs, failed);
                mcp = (struct mcontext __user *)(u64)cmcp;
-               /* no need to check access_ok(mcp), since mcp < 4GB */
        }
 #else
-       if (__get_user(mcp, &ucp->uc_regs))
-               return -EFAULT;
-       if (!access_ok(mcp, sizeof(*mcp)))
-               return -EFAULT;
+       unsafe_get_user(mcp, &ucp->uc_regs, failed);
 #endif
+       user_read_access_end();
+
        set_current_blocked(&set);
        if (restore_user_regs(regs, mcp, sig))
                return -EFAULT;
 
        return 0;
+
+failed:
+       user_read_access_end();
+       return -EFAULT;
 }
 
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
@@ -979,11 +1005,15 @@ static int do_setcontext_tm(struct ucontext __user *ucp,
        u32 cmcp;
        u32 tm_cmcp;
 
-       if (get_sigset_t(&set, &ucp->uc_sigmask))
+       if (!user_read_access_begin(ucp, sizeof(*ucp)))
                return -EFAULT;
 
-       if (__get_user(cmcp, &ucp->uc_regs) ||
-           __get_user(tm_cmcp, &tm_ucp->uc_regs))
+       unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
+       unsafe_get_user(cmcp, &ucp->uc_regs, failed);
+
+       user_read_access_end();
+
+       if (__get_user(tm_cmcp, &tm_ucp->uc_regs))
                return -EFAULT;
        mcp = (struct mcontext __user *)(u64)cmcp;
        tm_mcp = (struct mcontext __user *)(u64)tm_cmcp;
@@ -994,6 +1024,10 @@ static int do_setcontext_tm(struct ucontext __user *ucp,
                return -EFAULT;
 
        return 0;
+
+failed:
+       user_read_access_end();
+       return -EFAULT;
 }
 #endif
 
@@ -1311,19 +1345,16 @@ SYSCALL_DEFINE0(sigreturn)
        struct sigcontext __user *sc;
        struct sigcontext sigctx;
        struct mcontext __user *sr;
-       void __user *addr;
        sigset_t set;
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-       struct mcontext __user *mcp, *tm_mcp;
-       unsigned long msr_hi;
-#endif
+       struct mcontext __user *mcp;
+       struct mcontext __user *tm_mcp = NULL;
+       unsigned long long msr_hi = 0;
 
        /* Always make any pending restarted system calls return -EINTR */
        current->restart_block.fn = do_no_restart_syscall;
 
        sf = (struct sigframe __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
        sc = &sf->sctx;
-       addr = sc;
        if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
                goto badframe;
 
@@ -1339,31 +1370,32 @@ SYSCALL_DEFINE0(sigreturn)
 #endif
        set_current_blocked(&set);
 
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        mcp = (struct mcontext __user *)&sf->mctx;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        tm_mcp = (struct mcontext __user *)&sf->mctx_transact;
        if (__get_user(msr_hi, &tm_mcp->mc_gregs[PT_MSR]))
                goto badframe;
+#endif
        if (MSR_TM_ACTIVE(msr_hi<<32)) {
                if (!cpu_has_feature(CPU_FTR_TM))
                        goto badframe;
                if (restore_tm_user_regs(regs, mcp, tm_mcp))
                        goto badframe;
-       } else
-#endif
-       {
+       } else {
                sr = (struct mcontext __user *)from_user_ptr(sigctx.regs);
-               addr = sr;
-               if (!access_ok(sr, sizeof(*sr))
-                   || restore_user_regs(regs, sr, 1))
-                       goto badframe;
+               if (restore_user_regs(regs, sr, 1)) {
+                       signal_fault(current, regs, "sys_sigreturn", sr);
+
+                       force_sig(SIGSEGV);
+                       return 0;
+               }
        }
 
        set_thread_flag(TIF_RESTOREALL);
        return 0;
 
 badframe:
-       signal_fault(current, regs, "sys_sigreturn", addr);
+       signal_fault(current, regs, "sys_sigreturn", sc);
 
        force_sig(SIGSEGV);
        return 0;