[PATCH v2] xtensa: fix register access using PTRACE_GETREGS API

Max Filippov jcmvbkbc at gmail.com
Mon Mar 28 19:07:41 UTC 2022


The commit 2429c69961e2 ("xtensa: switch to PTRACE_GETREGS API") changed
the way xtensa registers are accessed, but the change is only correct
in case when the xtensa_regs.windowbase is 0 (i.e. the current register
window starts at the physical registr 0). In other cases decoded syscall
arguments are bogus. This issue is currently observed on noMMU xtensa
linux because it uses PTRACE_GETREGS API, while configurations with MMU
default to using ptrace_get_syscall_info.

Fix that by taking xtensa_registers.windowbase into an account.
This is trivial for the first 4 registers in the current window, because
windowbase indicates 4-register groups and is always valid, so registers
a0...a3 are guaranteed to be available in the physical registers array
at indices windowbase * 4 + 0...3.
Position of the registers with higher numbers may wrap around the end of
the physical register file, so e.g. with 32 physical resgisters and
windowbase = 7 the register a5 is stored in the xtensa_registers.a[1].

This is further complicated by the fact that the size of the physical
register file may vary and there's no explicit way to inquire it from
the kernel. Probe for it by trying to set windowbase to ascending powers
of 2 until it fails.

	* src/linux/xtensa/arch_regs.c (ARCH_SP_REG): Account for
	xtensa_regs.windowbase in xtensa_regs.a indexing.
	* src/linux/xtensa/get_error.c (arch_get_error): New variable
	status_reg that accounts for xtensa_regs.windowbase, use it
	for xtensa_regs.a indexing.
	* src/linux/xtensa/get_syscall_args.c (xtensa_probe_naregs): New
	function.
	(arch_get_syscall_args): New variable naregs_mask, initialize it
	using xtensa_probe_naregs, use it for xtensa_regs.a indexing.
	* src/linux/xtensa/set_error.c (arch_set_error)
	(arch_set_success): Account for xtensa_regs.windowbase in
	xtensa_regs.a indexing.

Signed-off-by: Max Filippov <jcmvbkbc at gmail.com>
---
Changes v1->v2:

- limit number of iterations in the xtensa_probe_naregs by the space
  reserved for the physical registers array.

 src/linux/xtensa/arch_regs.c        |  2 +-
 src/linux/xtensa/get_error.c        |  8 +++++---
 src/linux/xtensa/get_syscall_args.c | 22 +++++++++++++++++++++-
 src/linux/xtensa/set_error.c        |  4 ++--
 4 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/src/linux/xtensa/arch_regs.c b/src/linux/xtensa/arch_regs.c
index 84ea7cfa70f1..d21f9c778704 100644
--- a/src/linux/xtensa/arch_regs.c
+++ b/src/linux/xtensa/arch_regs.c
@@ -8,4 +8,4 @@
 static struct user_pt_regs xtensa_regs;
 #define ARCH_REGS_FOR_GETREGS xtensa_regs
 #define ARCH_PC_REG xtensa_regs.pc
-#define ARCH_SP_REG xtensa_regs.a[1]
+#define ARCH_SP_REG xtensa_regs.a[xtensa_regs.windowbase * 4 + 1]
diff --git a/src/linux/xtensa/get_error.c b/src/linux/xtensa/get_error.c
index d201a44c141b..e2877c43083e 100644
--- a/src/linux/xtensa/get_error.c
+++ b/src/linux/xtensa/get_error.c
@@ -10,10 +10,12 @@
 static void
 arch_get_error(struct tcb *tcp, const bool check_errno)
 {
-	if (check_errno && is_negated_errno(xtensa_regs.a[2])) {
+	unsigned int status_reg = xtensa_regs.windowbase * 4 + 2;
+
+	if (check_errno && is_negated_errno(xtensa_regs.a[status_reg])) {
 		tcp->u_rval = -1;
-		tcp->u_error = -xtensa_regs.a[2];
+		tcp->u_error = -xtensa_regs.a[status_reg];
 	} else {
-		tcp->u_rval = xtensa_regs.a[2];
+		tcp->u_rval = xtensa_regs.a[status_reg];
 	}
 }
diff --git a/src/linux/xtensa/get_syscall_args.c b/src/linux/xtensa/get_syscall_args.c
index ee190dfb9e48..6e22844b4c12 100644
--- a/src/linux/xtensa/get_syscall_args.c
+++ b/src/linux/xtensa/get_syscall_args.c
@@ -5,13 +5,33 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
+static unsigned int xtensa_probe_naregs(struct tcb *tcp)
+{
+	struct user_pt_regs tmp = xtensa_regs;
+	unsigned int n = 8;
+
+	do {
+		n *= 2;
+		xtensa_regs.windowbase = n / 4;
+	} while (n < ARRAY_SIZE(xtensa_regs.a) &&
+		 set_regs(tcp->pid) == 0);
+	xtensa_regs = tmp;
+	set_regs(tcp->pid);
+	return n;
+}
+
 /* Return -1 on error or 1 on success (never 0!). */
 static int
 arch_get_syscall_args(struct tcb *tcp)
 {
 	static const unsigned int syscall_regs[MAX_ARGS] = { 6, 3, 4, 5, 8, 9 };
+	static unsigned int naregs_mask;
+
+	if (naregs_mask == 0)
+		naregs_mask = xtensa_probe_naregs(tcp) - 1;
 
 	for (unsigned int i = 0; i < n_args(tcp); ++i)
-		tcp->u_arg[i] = xtensa_regs.a[syscall_regs[i]];
+		tcp->u_arg[i] = xtensa_regs.a[(xtensa_regs.windowbase * 4 +
+					       syscall_regs[i]) & naregs_mask];
 	return 1;
 }
diff --git a/src/linux/xtensa/set_error.c b/src/linux/xtensa/set_error.c
index 37202fd88a6a..c6a05d438b00 100644
--- a/src/linux/xtensa/set_error.c
+++ b/src/linux/xtensa/set_error.c
@@ -8,13 +8,13 @@
 static int
 arch_set_error(struct tcb *tcp)
 {
-	xtensa_regs.a[2] = -tcp->u_error;
+	xtensa_regs.a[xtensa_regs.windowbase * 4 + 2] = -tcp->u_error;
 	return set_regs(tcp->pid);
 }
 
 static int
 arch_set_success(struct tcb *tcp)
 {
-	xtensa_regs.a[2] = tcp->u_rval;
+	xtensa_regs.a[xtensa_regs.windowbase * 4 + 2] = tcp->u_rval;
 	return set_regs(tcp->pid);
 }
-- 
2.30.2



More information about the Strace-devel mailing list