Commit 38a35a78 authored by Max Filippov's avatar Max Filippov
Browse files

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.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
parent 03bc996a
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -127,12 +127,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;
@@ -140,8 +165,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, &regs->xtregs_opt,
			      sizeof(xtregs->opt));
@@ -157,6 +187,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;
@@ -166,8 +197,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(&regs->xtregs_opt, &xtregs->opt,
				sizeof(xtregs->opt));