[PATCH v3 4/4] selftests: riscv: extend vector tests for sigreturn and ptrace
Andy Chiu
tchiu at tenstorrent.com
Thu May 21 09:25:20 PDT 2026
Add new test cases to verify the vector state restorations at syscall
stops for ptrace and signal interfaces. Specifically:
1. Signal handler should read all ones at syscall stop and modifying
context should success.
2. Ptrace should read all ones but any modification to NT_RISCV_VECTOR
is silently dropped.
Signed-off-by: Andy Chiu <tchiu at tenstorrent.com>
---
Changelog v3:
- new patch since v3
---
.../selftests/riscv/sigreturn/sigreturn.c | 78 ++++++++++++++
.../selftests/riscv/vector/vstate_ptrace.c | 100 +++++++++++++++++-
2 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/riscv/sigreturn/sigreturn.c b/tools/testing/selftests/riscv/sigreturn/sigreturn.c
index e10873d95fed..c7d8cac80efb 100644
--- a/tools/testing/selftests/riscv/sigreturn/sigreturn.c
+++ b/tools/testing/selftests/riscv/sigreturn/sigreturn.c
@@ -3,10 +3,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
+#include <sys/time.h>
#include <linux/ptrace.h>
#include "kselftest_harness.h"
#define RISCV_V_MAGIC 0x53465457
+#define END_MAGIC 0
#define DEFAULT_VALUE 2
#define SIGNAL_HANDLER_OVERRIDE 3
@@ -61,6 +63,82 @@ static int vector_sigreturn(int data, void (*handler)(int, siginfo_t *, void *))
return after_sigreturn;
}
+#define V_TEST_PATTERN_SIGNAL 0x98
+int nulled_val;
+static void sigalrm_handler(int sig, siginfo_t *info, void *vcontext)
+{
+ ucontext_t *context = vcontext;
+ struct __riscv_extra_ext_header *ext;
+ struct __riscv_ctx_hdr *hdr;
+ uint8_t *ext_ptr;
+
+ /* Find the vector context */
+ ext = (void *)(&context->uc_mcontext.__fpregs);
+ ext_ptr = (uint8_t *)ext;
+ hdr = &ext->hdr;
+
+ while (hdr->magic != END_MAGIC) {
+ if (hdr->magic == RISCV_V_MAGIC) {
+ struct __riscv_v_ext_state *v_state = (struct __riscv_v_ext_state *)(hdr + 1);
+ /* Assume a valid datap */
+ nulled_val = *(int *)v_state->datap;
+ /* Fill all vector registers with magic pattern */
+ memset(v_state->datap, V_TEST_PATTERN_SIGNAL, v_state->vlenb * 32);
+ /*
+ * We must also set the vector configuration so that when
+ * userspace reads v0, it uses a valid element width (e8).
+ */
+ v_state->vl = v_state->vlenb;
+ v_state->vtype = 0; /* e8, m1, tu, mu */
+ break;
+ }
+ /* Move to the next extension header */
+ ext_ptr += hdr->size;
+ hdr = (struct __riscv_ctx_hdr *)ext_ptr;
+ }
+}
+
+TEST(test_signal_syscall_ucontext) {
+ struct sigaction sa;
+
+ /* Make sure we get V in ucontext by executing vsetvli */
+ asm volatile (
+ ".option push\n\t"
+ ".option arch, +v\n\t"
+ "vsetivli x0, 1, e32, m1, ta, ma\n\t"
+ ".option pop\n\t" : : :);
+
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = sigalrm_handler;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGALRM, &sa, NULL) == -1)
+ ksft_exit_fail_msg("Failed to register signal handler\n");
+
+ /* Setup a 10ms timer to unblock us from pause() */
+ struct itimerval itv = {{0, 0}, {0, 10000}};
+
+ setitimer(ITIMER_REAL, &itv, NULL);
+
+ pause();
+
+ /*
+ * If the kernel successfully parsed and restored our modified ucontext,
+ * v0 will contain V_TEST_PATTERN_SIGNAL.
+ */
+ unsigned char v0_val;
+
+ asm volatile(
+ ".option push\n\t"
+ ".option arch, +zve32x\n\t"
+ "vmv.x.s %0, v0\n\t"
+ ".option pop\n\t"
+ : "=r" (v0_val)
+ );
+
+ EXPECT_EQ(v0_val, V_TEST_PATTERN_SIGNAL);
+ EXPECT_EQ(nulled_val, -1);
+}
+
TEST(vector_restore)
{
int result;
diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
index 1479abc0c9cb..7952885a04c7 100644
--- a/tools/testing/selftests/riscv/vector/vstate_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
@@ -21,6 +21,41 @@ static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t siz
return ptrace(op, pid, type, &v_iovec);
}
+static int do_child_syscall_stop(void)
+{
+ int out;
+
+ if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
+ ksft_perror("PTRACE_TRACEME failed\n");
+ return EXIT_FAILURE;
+ }
+
+ raise(SIGSTOP);
+
+ asm volatile (".option push\n\t"
+ ".option arch, +v\n\t"
+ "vsetivli x0, 1, e32, m1, ta, ma\n\t"
+ "vmv.s.x v31, %[in]\n\t"
+ ".option pop\n\t"
+ :
+ : [in] "r" (child_set_val));
+
+ getpid();
+
+ asm volatile (".option push\n\t"
+ ".option arch, +v\n\t"
+ "vsetivli x0, 1, e32, m1, ta, ma\n\t"
+ "vmv.x.s %[out], v31\n\t"
+ ".option pop\n\t"
+ : [out] "=r" (out)
+ :);
+
+ if (out != -1)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
static int do_child(void)
{
int out;
@@ -109,11 +144,62 @@ static void do_parent(pid_t child)
free(data);
}
+static void do_parent_syscall_stop(pid_t child)
+{
+ int status;
+ void *data = NULL;
+
+ while (waitpid(child, &status, 0)) {
+ if (WIFEXITED(status)) {
+ ksft_test_result(WEXITSTATUS(status) == 0,
+ "SETREGSET vector at syscall stop\n");
+ goto out;
+ } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSTOP)) {
+ /* Attach to the child at syscall stop */
+ ptrace(PTRACE_SYSCALL, child, NULL, NULL);
+ } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
+ size_t size;
+ void *data, *v31;
+ struct __riscv_v_regset_state *v_regset_hdr;
+
+ size = sizeof(*v_regset_hdr);
+ data = malloc(size);
+ if (!data)
+ goto out;
+ v_regset_hdr = (struct __riscv_v_regset_state *)data;
+
+ if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
+ goto out;
+
+ ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb);
+ data = realloc(data, size + v_regset_hdr->vlenb * 32);
+ if (!data)
+ goto out;
+ v_regset_hdr = (struct __riscv_v_regset_state *)data;
+ v31 = (void *)(data + size + v_regset_hdr->vlenb * 31);
+ size += v_regset_hdr->vlenb * 32;
+
+ if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
+ goto out;
+
+ ksft_test_result(*(int *)v31 == -1, "GETREGSET vector at syscall stop\n");
+
+ *(int *)v31 = parent_set_val;
+ if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
+ goto out;
+ }
+ ptrace(PTRACE_CONT, child, NULL, NULL);
+ }
+
+out:
+ free(data);
+}
+
int main(void)
{
pid_t child;
- ksft_set_plan(2);
+ ksft_set_plan(4);
if (!is_vector_supported() && !is_xtheadvector_supported())
ksft_exit_skip("Vector not supported\n");
@@ -130,5 +216,17 @@ int main(void)
do_parent(child);
+ parent_set_val = 0x53355457;
+ child_set_val = 0x49504F21;
+
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_msg("Fork failed %d\n", child);
+
+ if (!child)
+ return do_child_syscall_stop();
+
+ do_parent_syscall_stop(child);
+
ksft_finished();
}
--
2.43.0
More information about the linux-riscv
mailing list