xtensa: fix coprocessor part of ptrace_{get,set}xregs
Subsystems (3)
Max Filippov
maintainerPATCH 1/1 xtensa: fix coprocessor part of ptrace_{get,set}xregs
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.
Signed-off-by Max Filippov <[email protected]>
--- arch/xtensa/kernel/ptrace.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-)
arch/xtensa/kernel/ptrace.c +38 -4
@@ -283,12 +283,37 @@ static int ptrace_setregs(struct task_struct *child, void __user *uregs) } +#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 ;
@@ -296,8 +321,13 @@ static int ptrace_getxregs(struct task_struct *child, void __user *uregs)#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, ®s->xtregs_opt,sizeof (xtregs->opt));
@@ -313,6 +343,7 @@ static int ptrace_setxregs(struct task_struct *child, void __user *uregs)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 ;
@@ -322,8 +353,11 @@ static int ptrace_setxregs(struct task_struct *child, void __user *uregs)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 (®s->xtregs_opt, &xtregs->opt,sizeof (xtregs->opt));
Signature · 1 line
2.11.0