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

------------------

From: Max Filippov <jcmvb...@gmail.com>

commit 38a35a78c5e270cbe53c4fef6b0d3c2da90dd849 upstream.

Layout of coprocessor registers in the elf_xtregs_t and
xtregs_coprocessor_t may be different due to alignment. Thus it is not
always possible to copy data between the xtregs_coprocessor_t structure
and the elf_xtregs_t and get correct values for all registers.
Use a table of offsets and sizes of individual coprocessor register
groups to do coprocessor context copying in the ptrace_getxregs and
ptrace_setxregs.
This fixes incorrect coprocessor register values reading from the user
process by the native gdb on an xtensa core with multiple coprocessors
and registers with high alignment requirements.

Cc: sta...@vger.kernel.org
Signed-off-by: Max Filippov <jcmvb...@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 arch/xtensa/kernel/ptrace.c |   42 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 4 deletions(-)

--- a/arch/xtensa/kernel/ptrace.c
+++ b/arch/xtensa/kernel/ptrace.c
@@ -127,12 +127,37 @@ static int ptrace_setregs(struct task_st
 }
 
 
+#if XTENSA_HAVE_COPROCESSORS
+#define CP_OFFSETS(cp) \
+       { \
+               .elf_xtregs_offset = offsetof(elf_xtregs_t, cp), \
+               .ti_offset = offsetof(struct thread_info, xtregs_cp.cp), \
+               .sz = sizeof(xtregs_ ## cp ## _t), \
+       }
+
+static const struct {
+       size_t elf_xtregs_offset;
+       size_t ti_offset;
+       size_t sz;
+} cp_offsets[] = {
+       CP_OFFSETS(cp0),
+       CP_OFFSETS(cp1),
+       CP_OFFSETS(cp2),
+       CP_OFFSETS(cp3),
+       CP_OFFSETS(cp4),
+       CP_OFFSETS(cp5),
+       CP_OFFSETS(cp6),
+       CP_OFFSETS(cp7),
+};
+#endif
+
 static int ptrace_getxregs(struct task_struct *child, void __user *uregs)
 {
        struct pt_regs *regs = task_pt_regs(child);
        struct thread_info *ti = task_thread_info(child);
        elf_xtregs_t __user *xtregs = uregs;
        int ret = 0;
+       int i __maybe_unused;
 
        if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
                return -EIO;
@@ -140,8 +165,13 @@ static int ptrace_getxregs(struct task_s
 #if XTENSA_HAVE_COPROCESSORS
        /* Flush all coprocessor registers to memory. */
        coprocessor_flush_all(ti);
-       ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
-                             sizeof(xtregs_coprocessor_t));
+
+       for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i)
+               ret |= __copy_to_user((char __user *)xtregs +
+                                     cp_offsets[i].elf_xtregs_offset,
+                                     (const char *)ti +
+                                     cp_offsets[i].ti_offset,
+                                     cp_offsets[i].sz);
 #endif
        ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
                              sizeof(xtregs->opt));
@@ -157,6 +187,7 @@ static int ptrace_setxregs(struct task_s
        struct pt_regs *regs = task_pt_regs(child);
        elf_xtregs_t *xtregs = uregs;
        int ret = 0;
+       int i __maybe_unused;
 
        if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
                return -EFAULT;
@@ -166,8 +197,11 @@ static int ptrace_setxregs(struct task_s
        coprocessor_flush_all(ti);
        coprocessor_release_all(ti);
 
-       ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
-                               sizeof(xtregs_coprocessor_t));
+       for (i = 0; i < ARRAY_SIZE(cp_offsets); ++i)
+               ret |= __copy_from_user((char *)ti + cp_offsets[i].ti_offset,
+                                       (const char __user *)xtregs +
+                                       cp_offsets[i].elf_xtregs_offset,
+                                       cp_offsets[i].sz);
 #endif
        ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
                                sizeof(xtregs->opt));


Reply via email to