OSDN Git Service

powerpc/ptrace: Convert gpr32_set_common() to user access block
authorChristophe Leroy <christophe.leroy@csgroup.eu>
Wed, 10 Mar 2021 17:57:07 +0000 (17:57 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 26 Mar 2021 12:19:43 +0000 (23:19 +1100)
Use user access block in gpr32_set_common() instead of
repetitive __get_user() which imply repetitive KUAP open/close.

To get it clean, force inlining of the small set of tiny functions
called inside the block.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/bdcb8652c3bb4ab5b8b3bfd08147434be8fc04c9.1615398498.git.christophe.leroy@csgroup.eu
arch/powerpc/include/asm/ptrace.h
arch/powerpc/kernel/ptrace/ptrace-view.c

index 1499e92..bedbca0 100644 (file)
@@ -222,7 +222,7 @@ do {                                                                              \
 } while (0)
 #endif /* __powerpc64__ */
 
-static inline void set_trap(struct pt_regs *regs, unsigned long val)
+static __always_inline void set_trap(struct pt_regs *regs, unsigned long val)
 {
        regs->trap = (regs->trap & TRAP_FLAGS_MASK) | (val & ~TRAP_FLAGS_MASK);
 }
index 2bad806..0923c94 100644 (file)
@@ -111,7 +111,7 @@ static unsigned long get_user_msr(struct task_struct *task)
        return task->thread.regs->msr | task->thread.fpexc_mode;
 }
 
-static int set_user_msr(struct task_struct *task, unsigned long msr)
+static __always_inline int set_user_msr(struct task_struct *task, unsigned long msr)
 {
        task->thread.regs->msr &= ~MSR_DEBUGCHANGE;
        task->thread.regs->msr |= msr & MSR_DEBUGCHANGE;
@@ -147,7 +147,7 @@ static int set_user_dscr(struct task_struct *task, unsigned long dscr)
  * We prevent mucking around with the reserved area of trap
  * which are used internally by the kernel.
  */
-static int set_user_trap(struct task_struct *task, unsigned long trap)
+static __always_inline int set_user_trap(struct task_struct *task, unsigned long trap)
 {
        set_trap(task->thread.regs, trap);
        return 0;
@@ -661,6 +661,9 @@ int gpr32_set_common(struct task_struct *target,
        const compat_ulong_t __user *u = ubuf;
        compat_ulong_t reg;
 
+       if (!kbuf && !user_read_access_begin(u, count))
+               return -EFAULT;
+
        pos /= sizeof(reg);
        count /= sizeof(reg);
 
@@ -669,8 +672,7 @@ int gpr32_set_common(struct task_struct *target,
                        regs[pos++] = *k++;
        else
                for (; count > 0 && pos < PT_MSR; --count) {
-                       if (__get_user(reg, u++))
-                               return -EFAULT;
+                       unsafe_get_user(reg, u++, Efault);
                        regs[pos++] = reg;
                }
 
@@ -678,8 +680,8 @@ int gpr32_set_common(struct task_struct *target,
        if (count > 0 && pos == PT_MSR) {
                if (kbuf)
                        reg = *k++;
-               else if (__get_user(reg, u++))
-                       return -EFAULT;
+               else
+                       unsafe_get_user(reg, u++, Efault);
                set_user_msr(target, reg);
                ++pos;
                --count;
@@ -692,24 +694,24 @@ int gpr32_set_common(struct task_struct *target,
                        ++k;
        } else {
                for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) {
-                       if (__get_user(reg, u++))
-                               return -EFAULT;
+                       unsafe_get_user(reg, u++, Efault);
                        regs[pos++] = reg;
                }
                for (; count > 0 && pos < PT_TRAP; --count, ++pos)
-                       if (__get_user(reg, u++))
-                               return -EFAULT;
+                       unsafe_get_user(reg, u++, Efault);
        }
 
        if (count > 0 && pos == PT_TRAP) {
                if (kbuf)
                        reg = *k++;
-               else if (__get_user(reg, u++))
-                       return -EFAULT;
+               else
+                       unsafe_get_user(reg, u++, Efault);
                set_user_trap(target, reg);
                ++pos;
                --count;
        }
+       if (!kbuf)
+               user_read_access_end();
 
        kbuf = k;
        ubuf = u;
@@ -717,6 +719,10 @@ int gpr32_set_common(struct task_struct *target,
        count *= sizeof(reg);
        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
                                         (PT_TRAP + 1) * sizeof(reg), -1);
+
+Efault:
+       user_read_access_end();
+       return -EFAULT;
 }
 
 static int gpr32_get(struct task_struct *target,