Upgrade to 4.4.50-rt62
[kvmfornfv.git] / kernel / arch / arm64 / kernel / ptrace.c
index ff7f132..55909b2 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/elf.h>
 
 #include <asm/compat.h>
+#include <asm/cpufeature.h>
 #include <asm/debug-monitors.h>
 #include <asm/pgtable.h>
 #include <asm/syscall.h>
@@ -449,6 +450,8 @@ static int hw_break_set(struct task_struct *target,
        /* (address, ctrl) registers */
        limit = regset->n * regset->size;
        while (count && offset < limit) {
+               if (count < PTRACE_HBP_ADDR_SZ)
+                       return -EINVAL;
                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
                                         offset, offset + PTRACE_HBP_ADDR_SZ);
                if (ret)
@@ -458,6 +461,8 @@ static int hw_break_set(struct task_struct *target,
                        return ret;
                offset += PTRACE_HBP_ADDR_SZ;
 
+               if (!count)
+                       break;
                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
                                         offset, offset + PTRACE_HBP_CTRL_SZ);
                if (ret)
@@ -494,13 +499,13 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,
                   const void *kbuf, const void __user *ubuf)
 {
        int ret;
-       struct user_pt_regs newregs;
+       struct user_pt_regs newregs = task_pt_regs(target)->user_regs;
 
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
        if (ret)
                return ret;
 
-       if (!valid_user_regs(&newregs))
+       if (!valid_user_regs(&newregs, target))
                return -EINVAL;
 
        task_pt_regs(target)->user_regs = newregs;
@@ -524,7 +529,8 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
                   const void *kbuf, const void __user *ubuf)
 {
        int ret;
-       struct user_fpsimd_state newstate;
+       struct user_fpsimd_state newstate =
+               target->thread.fpsimd_state.user_fpsimd;
 
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);
        if (ret)
@@ -548,7 +554,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset,
                   const void *kbuf, const void __user *ubuf)
 {
        int ret;
-       unsigned long tls;
+       unsigned long tls = target->thread.tp_value;
 
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
        if (ret)
@@ -574,7 +580,8 @@ static int system_call_set(struct task_struct *target,
                           unsigned int pos, unsigned int count,
                           const void *kbuf, const void __user *ubuf)
 {
-       int syscallno, ret;
+       int syscallno = task_pt_regs(target)->syscallno;
+       int ret;
 
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1);
        if (ret)
@@ -770,7 +777,7 @@ static int compat_gpr_set(struct task_struct *target,
 
        }
 
-       if (valid_user_regs(&newregs.user_regs))
+       if (valid_user_regs(&newregs.user_regs, target))
                *task_pt_regs(target) = newregs;
        else
                ret = -EINVAL;
@@ -846,7 +853,7 @@ static int compat_tls_set(struct task_struct *target,
                          const void __user *ubuf)
 {
        int ret;
-       compat_ulong_t tls;
+       compat_ulong_t tls = target->thread.tp_value;
 
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
        if (ret)
@@ -1272,3 +1279,79 @@ asmlinkage void syscall_trace_exit(struct pt_regs *regs)
        if (test_thread_flag(TIF_SYSCALL_TRACE))
                tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT);
 }
+
+/*
+ * Bits which are always architecturally RES0 per ARM DDI 0487A.h
+ * Userspace cannot use these until they have an architectural meaning.
+ * We also reserve IL for the kernel; SS is handled dynamically.
+ */
+#define SPSR_EL1_AARCH64_RES0_BITS \
+       (GENMASK_ULL(63,32) | GENMASK_ULL(27, 22) | GENMASK_ULL(20, 10) | \
+        GENMASK_ULL(5, 5))
+#define SPSR_EL1_AARCH32_RES0_BITS \
+       (GENMASK_ULL(63,32) | GENMASK_ULL(24, 22) | GENMASK_ULL(20,20))
+
+static int valid_compat_regs(struct user_pt_regs *regs)
+{
+       regs->pstate &= ~SPSR_EL1_AARCH32_RES0_BITS;
+
+       if (!system_supports_mixed_endian_el0()) {
+               if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+                       regs->pstate |= COMPAT_PSR_E_BIT;
+               else
+                       regs->pstate &= ~COMPAT_PSR_E_BIT;
+       }
+
+       if (user_mode(regs) && (regs->pstate & PSR_MODE32_BIT) &&
+           (regs->pstate & COMPAT_PSR_A_BIT) == 0 &&
+           (regs->pstate & COMPAT_PSR_I_BIT) == 0 &&
+           (regs->pstate & COMPAT_PSR_F_BIT) == 0) {
+               return 1;
+       }
+
+       /*
+        * Force PSR to a valid 32-bit EL0t, preserving the same bits as
+        * arch/arm.
+        */
+       regs->pstate &= COMPAT_PSR_N_BIT | COMPAT_PSR_Z_BIT |
+                       COMPAT_PSR_C_BIT | COMPAT_PSR_V_BIT |
+                       COMPAT_PSR_Q_BIT | COMPAT_PSR_IT_MASK |
+                       COMPAT_PSR_GE_MASK | COMPAT_PSR_E_BIT |
+                       COMPAT_PSR_T_BIT;
+       regs->pstate |= PSR_MODE32_BIT;
+
+       return 0;
+}
+
+static int valid_native_regs(struct user_pt_regs *regs)
+{
+       regs->pstate &= ~SPSR_EL1_AARCH64_RES0_BITS;
+
+       if (user_mode(regs) && !(regs->pstate & PSR_MODE32_BIT) &&
+           (regs->pstate & PSR_D_BIT) == 0 &&
+           (regs->pstate & PSR_A_BIT) == 0 &&
+           (regs->pstate & PSR_I_BIT) == 0 &&
+           (regs->pstate & PSR_F_BIT) == 0) {
+               return 1;
+       }
+
+       /* Force PSR to a valid 64-bit EL0t */
+       regs->pstate &= PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT;
+
+       return 0;
+}
+
+/*
+ * Are the current registers suitable for user mode? (used to maintain
+ * security in signal handlers)
+ */
+int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task)
+{
+       if (!test_tsk_thread_flag(task, TIF_SINGLESTEP))
+               regs->pstate &= ~DBG_SPSR_SS;
+
+       if (is_compat_thread(task_thread_info(task)))
+               return valid_compat_regs(regs);
+       else
+               return valid_native_regs(regs);
+}