[PATCH v3 3/5] selftests: futex: Add support for aarch64 in robust_list_critical

André Almeida andrealmeid at igalia.com
Fri May 29 09:33:55 PDT 2026


Add support for aarch64 for robust_list_critical test.

Apart from the arch specific registers, as the cmpxchg is implemented
using LL/SC, doing single steps clears the exclusive monitor and then the
store fails.

To avoid that, insert a breakpoint just after the store and let it run
continuously until there.

Signed-off-by: André Almeida <andrealmeid at igalia.com>
---
 .../futex/functional/robust_list_critical.c        | 92 ++++++++++++++++++++--
 1 file changed, 87 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/futex/functional/robust_list_critical.c b/tools/testing/selftests/futex/functional/robust_list_critical.c
index b9490d24eb10..a4fe69a21297 100644
--- a/tools/testing/selftests/futex/functional/robust_list_critical.c
+++ b/tools/testing/selftests/futex/functional/robust_list_critical.c
@@ -65,8 +65,9 @@ static bool pc_is_within(struct user_regs_struct *regs, uint64_t start, uint64_t
 
 #if defined(__x86_64__)
 	pc = regs->rip;
-#elif defined(__riscv)
-	pc = reg->pc;
+#elif defined(__riscv) || defined(__aarch64__)
+	pc = regs->pc;
+#else
 # error Missing ptrace support
 #endif
 	if (pc >= (long) start && pc < end)
@@ -219,12 +220,79 @@ enum trace_state {
 	STATE_LEAVE_VDSO,
 };
 
+static int ptrace_get_regs(struct user_regs_struct *regs, pid_t child)
+{
+#if defined(__x86_64__)
+
+	return ptrace(PTRACE_GETREGS, child, 0, regs);
+
+#elif defined(__aarch64__)
+
+	struct iovec io;
+	io.iov_base = regs;
+	io.iov_len = sizeof(struct user_regs_struct);
+
+	return ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &io);
+
+#endif
+}
+
+#if defined(__aarch64__)
+/*
+ * A cmpxchg in Arm64 implemented using LL/SC can't single step on every
+ * instruction. This clears the exclusive monitor and invalidates the store.
+ * Instead, we need to add a breakpoint just after the store (on the label
+ * __futex_list64_try_unlock_cs_success) and let it be executed continuously.
+ */
+
+#define AARCH64_BRK 0xD4200000
+static void set_breakpoint(pid_t child, uint64_t *inst)
+{
+
+	long ret;
+
+	ret = ptrace(PTRACE_PEEKDATA, child, __futex_list64_try_unlock_cs_success, 0);
+	if (ret == -1)
+		err(1, "PTRACE_PEEKDATA");
+
+	*inst = (uint64_t) ret;
+
+	ret = ptrace(PTRACE_POKEDATA, child, __futex_list64_try_unlock_cs_success, AARCH64_BRK);
+	if (ret == -1)
+		err(1, "PTRACE_POKEDATA");
+}
+
+static void remove_breakpoint(pid_t child, struct user_regs_struct *regs, uint64_t inst)
+{
+	uint64_t addr = regs->pc;
+	int ret;
+
+	ret = ptrace(PTRACE_POKEDATA, child, addr, inst);
+	if (ret == -1)
+		err(1, "PTRACE_POKEDATA");
+}
+
+#define ptrace_cont()				\
+	if (ptrace(PTRACE_CONT, child, 0, 0))	\
+		err(1, "PTRACE_CONT");		\
+	continue;
+
+#else
+
+static void set_breakpoint(pid_t child, uint64_t *inst) {}
+static void remove_breakpoint(pid_t child, struct user_regs_struct *regs, uint64_t inst) {}
+
+#define ptrace_cont() {}
+
+#endif
+
 static void trace_child(struct __test_metadata *_metadata, pid_t child, bool is_32bit)
 {
 	int state = STATE_WAIT;
 	struct robust_list_head *rhead;
 	size_t sz;
-	bool do_end = false;
+	bool do_end = false, enter_cs = false;
+	uint64_t inst = 0;
 
 	syscall(SYS_get_robust_list, 0, &rhead, &sz);
 	do {
@@ -235,6 +303,7 @@ static void trace_child(struct __test_metadata *_metadata, pid_t child, bool is_
 		pid_t rpid;
 
 		rpid = waitpid(child, &wstatus, 0);
+
 		if (rpid != child)
 			errx(1, "waitpid");
 		if (!do_end) {
@@ -244,11 +313,15 @@ static void trace_child(struct __test_metadata *_metadata, pid_t child, bool is_
 			if (!WIFEXITED(wstatus))
 				errx(1, "Did not exit, but we are done");
 			ASSERT_EQ(WEXITSTATUS(wstatus), 0);
+
+			/* check if the code really reached the critical section */
+			ASSERT_EQ(enter_cs, true);
+
 			return;
 		}
 
-		if (ptrace(PTRACE_GETREGS, child, 0, &regs) != 0)
-			errx(1, "PTRACE_GETREGS");
+		if (ptrace_get_regs(&regs, child))
+			errx(1, "ptrace_get_regs");
 
 		if (is_32bit) {
 			in_vdso = pc_is_within(&regs, (long)frtu32, frtu32_end);
@@ -261,6 +334,11 @@ static void trace_child(struct __test_metadata *_metadata, pid_t child, bool is_
 			if (state == STATE_WAIT) {
 				state = STATE_ENTER_VDSO;
 
+				/* set a bp first time we enter vdso */
+				set_breakpoint(child, &inst);
+
+				/* and let the execution continue until it reaches the CS */
+				ptrace_cont();
 			} else {
 				if (is_32bit) {
 					if (pc_is_within(&regs, __futex_list32_try_unlock_cs_start,
@@ -301,8 +379,12 @@ static void trace_child(struct __test_metadata *_metadata, pid_t child, bool is_
 				if (is_32bit)
 					rhead_val &= 0xffffffff;
 
+				enter_cs = true;
 				ASSERT_EQ(rhead_val, 0);
 				ASSERT_EQ(lock_val, 0);
+
+				/* remove the breakpoint and continue execution */
+				remove_breakpoint(child, &regs, inst);
 			}
 
 			if (ptrace(PTRACE_SINGLESTEP, child, 0, 0))

-- 
2.54.0




More information about the linux-arm-kernel mailing list