* [PATCH v3 1/9] selftests: riscv: test ptrace vector interface
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 2/9] riscv: ptrace: return ENODATA for inactive vector extension Sergey Matyukevich
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add a test case to check ptrace behavior in the case when vector
extension is supported by the system, but vector context is not
yet enabled for the traced process.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
.../testing/selftests/riscv/vector/.gitignore | 1 +
tools/testing/selftests/riscv/vector/Makefile | 5 +-
.../testing/selftests/riscv/vector/v_ptrace.c | 85 +++++++++++++++++++
3 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/riscv/vector/v_ptrace.c
diff --git a/tools/testing/selftests/riscv/vector/.gitignore b/tools/testing/selftests/riscv/vector/.gitignore
index 7d9c87cd0649..d21c03c3ee0e 100644
--- a/tools/testing/selftests/riscv/vector/.gitignore
+++ b/tools/testing/selftests/riscv/vector/.gitignore
@@ -2,3 +2,4 @@ vstate_exec_nolibc
vstate_prctl
v_initval
v_exec_initval_nolibc
+v_ptrace
diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
index 6f7497f4e7b3..c14ad127e7fb 100644
--- a/tools/testing/selftests/riscv/vector/Makefile
+++ b/tools/testing/selftests/riscv/vector/Makefile
@@ -2,7 +2,7 @@
# Copyright (C) 2021 ARM Limited
# Originally tools/testing/arm64/abi/Makefile
-TEST_GEN_PROGS := v_initval vstate_prctl
+TEST_GEN_PROGS := v_initval vstate_prctl v_ptrace
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
include ../../lib.mk
@@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
$(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
$(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
-Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
+
+$(OUTPUT)/v_ptrace: v_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
+ $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/vector/v_ptrace.c b/tools/testing/selftests/riscv/vector/v_ptrace.c
new file mode 100644
index 000000000000..6a4b5a2ab4a2
--- /dev/null
+++ b/tools/testing/selftests/riscv/vector/v_ptrace.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/ptrace.h>
+#include <linux/elf.h>
+
+#include "../../kselftest_harness.h"
+#include "v_helpers.h"
+
+volatile unsigned long chld_lock;
+
+TEST(ptrace_v_not_enabled)
+{
+ pid_t pid;
+
+ if (!is_vector_supported())
+ SKIP(return, "Vector not supported");
+
+ chld_lock = 1;
+ pid = fork();
+ ASSERT_LE(0, pid)
+ TH_LOG("fork: %m");
+
+ if (pid == 0) {
+ while (chld_lock == 1)
+ asm volatile("" : : "g"(chld_lock) : "memory");
+
+ asm volatile ("ebreak" : : : );
+ } else {
+ struct __riscv_v_regset_state *regset_data;
+ unsigned long vlenb;
+ size_t regset_size;
+ struct iovec iov;
+ int status;
+ int ret;
+
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+
+ ASSERT_GT(vlenb, 0)
+ TH_LOG("vlenb is not valid: %lu\n", vlenb);
+
+ /* attach */
+
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* unlock */
+
+ ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
+
+ /* resume and wait for ebreak */
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* try to read vector registers from the tracee */
+
+ regset_size = sizeof(*regset_data) + vlenb * 32;
+ regset_data = calloc(1, regset_size);
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ /* V extension is available, but not yet enabled for the tracee */
+
+ errno = 0;
+ ret = ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov);
+ ASSERT_EQ(ENODATA, errno);
+ ASSERT_EQ(-1, ret);
+
+ /* cleanup */
+
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+ }
+}
+
+TEST_HARNESS_MAIN
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 2/9] riscv: ptrace: return ENODATA for inactive vector extension
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 1/9] selftests: riscv: test ptrace vector interface Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 3/9] selftests: riscv: verify initial vector state with ptrace Sergey Matyukevich
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich, Ilya Mamay
From: Ilya Mamay <mmamayka01@gmail.com>
Currently, ptrace returns EINVAL when the vector extension is supported
but not yet activated for the traced process. This error code is not
always appropriate since the ptrace arguments may be valid.
Debug tools like gdbserver expect ENODATA when the requested register
set is not active, e.g. see [1]. This expectation seems to be more
appropriate, so modify the vector ptrace implementation to return:
- EINVAL when V extension is not supported
- ENODATA when V extension is supported but not active
[1] https://github.com/bminor/binutils-gdb/blob/637f25e88675fa47e47f9cc5e2cf37384836b8a2/gdbserver/linux-low.cc#L5020
Signed-off-by: Ilya Mamay <mmamayka01@gmail.com>
---
arch/riscv/kernel/ptrace.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index 8e86305831ea..906cf1197edc 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -95,9 +95,12 @@ static int riscv_vr_get(struct task_struct *target,
struct __riscv_v_ext_state *vstate = &target->thread.vstate;
struct __riscv_v_regset_state ptrace_vstate;
- if (!riscv_v_vstate_query(task_pt_regs(target)))
+ if (!has_vector())
return -EINVAL;
+ if (!riscv_v_vstate_query(task_pt_regs(target)))
+ return -ENODATA;
+
/*
* Ensure the vector registers have been saved to the memory before
* copying them to membuf.
@@ -130,9 +133,12 @@ static int riscv_vr_set(struct task_struct *target,
struct __riscv_v_ext_state *vstate = &target->thread.vstate;
struct __riscv_v_regset_state ptrace_vstate;
- if (!riscv_v_vstate_query(task_pt_regs(target)))
+ if (!has_vector())
return -EINVAL;
+ if (!riscv_v_vstate_query(task_pt_regs(target)))
+ return -ENODATA;
+
/* Copy rest of the vstate except datap */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0,
sizeof(struct __riscv_v_regset_state));
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 3/9] selftests: riscv: verify initial vector state with ptrace
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 1/9] selftests: riscv: test ptrace vector interface Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 2/9] riscv: ptrace: return ENODATA for inactive vector extension Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 4/9] riscv: vector: init vector context with proper vlenb Sergey Matyukevich
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add a test case that attaches to a traced process immediately after its
first executed vector instructions to verify the initial vector context.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
.../testing/selftests/riscv/vector/v_ptrace.c | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/tools/testing/selftests/riscv/vector/v_ptrace.c b/tools/testing/selftests/riscv/vector/v_ptrace.c
index 6a4b5a2ab4a2..9fea29f7b686 100644
--- a/tools/testing/selftests/riscv/vector/v_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/v_ptrace.c
@@ -82,4 +82,105 @@ TEST(ptrace_v_not_enabled)
}
}
+TEST(ptrace_v_early_debug)
+{
+ static volatile unsigned long vstart;
+ static volatile unsigned long vtype;
+ static volatile unsigned long vlenb;
+ static volatile unsigned long vcsr;
+ static volatile unsigned long vl;
+ pid_t pid;
+
+ if (!is_vector_supported())
+ SKIP(return, "Vector not supported");
+
+ chld_lock = 1;
+ pid = fork();
+ ASSERT_LE(0, pid)
+ TH_LOG("fork: %m");
+
+ if (pid == 0) {
+ while (chld_lock == 1)
+ asm volatile("" : : "g"(chld_lock) : "memory");
+
+ asm volatile("csrr %[vstart], vstart" : [vstart] "=r"(vstart));
+ asm volatile("csrr %[vl], vl" : [vl] "=r"(vl));
+ asm volatile("csrr %[vtype], vtype" : [vtype] "=r"(vtype));
+ asm volatile("csrr %[vcsr], vcsr" : [vcsr] "=r"(vcsr));
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+
+ asm volatile ("ebreak" : : : );
+ } else {
+ struct __riscv_v_regset_state *regset_data;
+ unsigned long vstart_csr;
+ unsigned long vlenb_csr;
+ unsigned long vtype_csr;
+ unsigned long vcsr_csr;
+ unsigned long vl_csr;
+ size_t regset_size;
+ struct iovec iov;
+ int status;
+
+ /* attach */
+
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* unlock */
+
+ ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
+
+ /* resume and wait for ebreak */
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace PEEKDATA */
+
+ errno = 0;
+ vstart_csr = ptrace(PTRACE_PEEKDATA, pid, &vstart, NULL);
+ ASSERT_FALSE((errno != 0) && (vstart_csr == -1));
+
+ errno = 0;
+ vl_csr = ptrace(PTRACE_PEEKDATA, pid, &vl, NULL);
+ ASSERT_FALSE((errno != 0) && (vl_csr == -1));
+
+ errno = 0;
+ vtype_csr = ptrace(PTRACE_PEEKDATA, pid, &vtype, NULL);
+ ASSERT_FALSE((errno != 0) && (vtype_csr == -1));
+
+ errno = 0;
+ vcsr_csr = ptrace(PTRACE_PEEKDATA, pid, &vcsr, NULL);
+ ASSERT_FALSE((errno != 0) && (vcsr_csr == -1));
+
+ errno = 0;
+ vlenb_csr = ptrace(PTRACE_PEEKDATA, pid, &vlenb, NULL);
+ ASSERT_FALSE((errno != 0) && (vlenb_csr == -1));
+
+ /* read tracee csr regs using ptrace GETREGSET */
+
+ regset_size = sizeof(*regset_data) + vlenb_csr * 32;
+ regset_data = calloc(1, regset_size);
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* compare */
+
+ EXPECT_EQ(vstart_csr, regset_data->vstart);
+ EXPECT_EQ(vtype_csr, regset_data->vtype);
+ EXPECT_EQ(vlenb_csr, regset_data->vlenb);
+ EXPECT_EQ(vcsr_csr, regset_data->vcsr);
+ EXPECT_EQ(vl_csr, regset_data->vl);
+
+ /* cleanup */
+
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+ }
+}
+
TEST_HARNESS_MAIN
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 4/9] riscv: vector: init vector context with proper vlenb
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (2 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 3/9] selftests: riscv: verify initial vector state with ptrace Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 5/9] riscv: csr: define vector registers elements Sergey Matyukevich
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
The vstate in thread_struct is zeroed when the vector context is
initialized. That includes read-only register vlenb, which holds
the vector register length in bytes. This zeroed state persists
until mstatus.VS becomes 'dirty' and a context switch saves the
actual hardware values.
This can expose the zero vlenb value to the user-space in early
debug scenarios, e.g. when ptrace attaches to a traced process
early, before any vector instruction except the first one was
executed.
Fix this by specifying proper vlenb on vector context init.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
arch/riscv/kernel/vector.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
index 901e67adf576..34048c4c26dc 100644
--- a/arch/riscv/kernel/vector.c
+++ b/arch/riscv/kernel/vector.c
@@ -109,8 +109,8 @@ bool insn_is_vector(u32 insn_buf)
return false;
}
-static int riscv_v_thread_zalloc(struct kmem_cache *cache,
- struct __riscv_v_ext_state *ctx)
+static int riscv_v_thread_ctx_alloc(struct kmem_cache *cache,
+ struct __riscv_v_ext_state *ctx)
{
void *datap;
@@ -120,13 +120,15 @@ static int riscv_v_thread_zalloc(struct kmem_cache *cache,
ctx->datap = datap;
memset(ctx, 0, offsetof(struct __riscv_v_ext_state, datap));
+ ctx->vlenb = riscv_v_vsize / 32;
+
return 0;
}
void riscv_v_thread_alloc(struct task_struct *tsk)
{
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
- riscv_v_thread_zalloc(riscv_v_kernel_cachep, &tsk->thread.kernel_vstate);
+ riscv_v_thread_ctx_alloc(riscv_v_kernel_cachep, &tsk->thread.kernel_vstate);
#endif
}
@@ -212,12 +214,14 @@ bool riscv_v_first_use_handler(struct pt_regs *regs)
* context where VS has been off. So, try to allocate the user's V
* context and resume execution.
*/
- if (riscv_v_thread_zalloc(riscv_v_user_cachep, ¤t->thread.vstate)) {
+ if (riscv_v_thread_ctx_alloc(riscv_v_user_cachep, ¤t->thread.vstate)) {
force_sig(SIGBUS);
return true;
}
+
riscv_v_vstate_on(regs);
riscv_v_vstate_set_restore(current, regs);
+
return true;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 5/9] riscv: csr: define vector registers elements
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (3 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 4/9] riscv: vector: init vector context with proper vlenb Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 6/9] riscv: ptrace: validate input vector csr registers Sergey Matyukevich
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Define masks and shifts for vector csr registers according
to the RVV spec 1.0.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
arch/riscv/include/asm/csr.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 4a37a98398ad..4f55dcf86627 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -444,6 +444,17 @@
#define CSR_VTYPE 0xc21
#define CSR_VLENB 0xc22
+#define VTYPE_VLMUL _AC(7, UL)
+#define VTYPE_VLMUL_FRAC _AC(4, UL)
+#define VTYPE_VSEW_SHIFT 3
+#define VTYPE_VSEW (_AC(7, UL) << VTYPE_VSEW_SHIFT)
+#define VTYPE_VTA_SHIFT 6
+#define VTYPE_VTA (_AC(1, UL) << VTYPE_VTA_SHIFT)
+#define VTYPE_VMA_SHIFT 7
+#define VTYPE_VMA (_AC(1, UL) << VTYPE_VMA_SHIFT)
+#define VTYPE_VILL_SHIFT (__riscv_xlen - 1)
+#define VTYPE_VILL (_AC(1, UL) << VTYPE_VILL_SHIFT)
+
/* Scalar Crypto Extension - Entropy */
#define CSR_SEED 0x015
#define SEED_OPST_MASK _AC(0xC0000000, UL)
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 6/9] riscv: ptrace: validate input vector csr registers
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (4 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 5/9] riscv: csr: define vector registers elements Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 7/9] selftests: riscv: verify ptrace rejects invalid vector csr inputs Sergey Matyukevich
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add strict validation for vector csr registers when setting them via
ptrace:
- reject attempts to set reserved bits or invalid field combinations
- enforce strict VL checks against calculated VLMAX values
Vector spec 1.0 allows normal applications to set candidate VL values
and read back the hardware-adjusted results, see section 6 for details.
Disallow such flexibility in vector ptrace operations and strictly
enforce valid VL input.
The traced process may not update its saved vector context if no vector
instructions execute between breakpoints. So the purpose of the strict
ptrace approach is to make sure that debuggers maintain an accurate view
of the tracee's vector context across multiple halt/resume debug cycles.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
arch/riscv/kernel/ptrace.c | 62 +++++++++++++++++++++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index 906cf1197edc..a567e558e746 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -124,6 +124,66 @@ static int riscv_vr_get(struct task_struct *target,
return membuf_write(&to, vstate->datap, riscv_v_vsize);
}
+static int invalid_ptrace_v_csr(struct __riscv_v_ext_state *vstate,
+ struct __riscv_v_regset_state *ptrace)
+{
+ unsigned long vsew, vlmul, vfrac, vl;
+ unsigned long elen, vlen;
+ unsigned long sew, lmul;
+ unsigned long reserved;
+
+ if (!has_vector())
+ return 1;
+
+ vlen = vstate->vlenb * 8;
+ if (vstate->vlenb != ptrace->vlenb)
+ return 1;
+
+ reserved = ~(CSR_VXSAT_MASK | (CSR_VXRM_MASK << CSR_VXRM_SHIFT));
+ if (ptrace->vcsr & reserved)
+ return 1;
+
+ /* do not allow to set vill */
+ reserved = ~(VTYPE_VSEW | VTYPE_VLMUL | VTYPE_VMA | VTYPE_VTA);
+ if (ptrace->vtype & reserved)
+ return 1;
+
+ elen = riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE64X) ? 64 : 32;
+ vsew = (ptrace->vtype & VTYPE_VSEW) >> VTYPE_VSEW_SHIFT;
+ sew = 8 << vsew;
+
+ if (sew > elen)
+ return 1;
+
+ vfrac = (ptrace->vtype & VTYPE_VLMUL_FRAC);
+ vlmul = (ptrace->vtype & VTYPE_VLMUL);
+
+ /* RVV 1.0 spec 3.4.2: VLMUL(0x4) reserved */
+ if (vlmul == 4)
+ return 1;
+
+ /* RVV 1.0 spec 3.4.2: (LMUL < SEW_min / ELEN) reserved */
+ if (vlmul == 5 && elen == 32)
+ return 1;
+
+ /* for zero vl verify that at least one element is possible */
+ vl = ptrace->vl ? ptrace->vl : 1;
+
+ if (vfrac) {
+ /* integer 1/LMUL: VL =< VLMAX = VLEN / SEW / LMUL */
+ lmul = 2 << (3 - (vlmul - vfrac));
+ if (vlen < vl * sew * lmul)
+ return 1;
+ } else {
+ /* integer LMUL: VL =< VLMAX = LMUL * VLEN / SEW */
+ lmul = 1 << vlmul;
+ if (vl * sew > lmul * vlen)
+ return 1;
+ }
+
+ return 0;
+}
+
static int riscv_vr_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
@@ -145,7 +205,7 @@ static int riscv_vr_set(struct task_struct *target,
if (unlikely(ret))
return ret;
- if (vstate->vlenb != ptrace_vstate.vlenb)
+ if (invalid_ptrace_v_csr(vstate, &ptrace_vstate))
return -EINVAL;
vstate->vstart = ptrace_vstate.vstart;
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 7/9] selftests: riscv: verify ptrace rejects invalid vector csr inputs
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (5 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 6/9] riscv: ptrace: validate input vector csr registers Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 8/9] selftests: riscv: verify ptrace accepts valid vector csr values Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 9/9] selftests: riscv: verify syscalls discard vector context Sergey Matyukevich
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add a test to v_ptrace test suite to verify that ptrace rejects the
invalid input combinations of vector csr registers. Use kselftest
fixture variants to create multiple invalid inputs for the test.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
.../testing/selftests/riscv/vector/v_ptrace.c | 232 ++++++++++++++++++
1 file changed, 232 insertions(+)
diff --git a/tools/testing/selftests/riscv/vector/v_ptrace.c b/tools/testing/selftests/riscv/vector/v_ptrace.c
index 9fea29f7b686..6f3f228c0954 100644
--- a/tools/testing/selftests/riscv/vector/v_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/v_ptrace.c
@@ -183,4 +183,236 @@ TEST(ptrace_v_early_debug)
}
}
+FIXTURE(v_csr_invalid)
+{
+};
+
+FIXTURE_SETUP(v_csr_invalid)
+{
+}
+
+FIXTURE_TEARDOWN(v_csr_invalid)
+{
+}
+
+/* modifications of the initial 'vsetvli x0, x0, e8, m8, tu, mu' settings */
+FIXTURE_VARIANT(v_csr_invalid)
+{
+ unsigned long vstart;
+ unsigned long vl;
+ unsigned long vtype;
+ unsigned long vcsr;
+ unsigned long vlenb_mul;
+ unsigned long vlenb_min;
+ unsigned long vlenb_max;
+};
+
+/* unexpected vlenb value */
+FIXTURE_VARIANT_ADD(v_csr_invalid, new_vlenb)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x3,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x2,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* invalid reserved bits in vcsr */
+FIXTURE_VARIANT_ADD(v_csr_invalid, vcsr_invalid_reserved_bits)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x3,
+ .vcsr = 0x1UL << 8,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* invalid reserved bits in vtype */
+FIXTURE_VARIANT_ADD(v_csr_invalid, vtype_invalid_reserved_bits)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = (0x1UL << 8) | 0x3,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* set vill bit */
+FIXTURE_VARIANT_ADD(v_csr_invalid, invalid_vill_bit)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = (0x1UL << (__riscv_xlen - 1)) | 0x3,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* reserved vsew value: vsew > 3 */
+FIXTURE_VARIANT_ADD(v_csr_invalid, reserved_vsew)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x4UL << 3,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* reserved vlmul value: vlmul == 4 */
+FIXTURE_VARIANT_ADD(v_csr_invalid, reserved_vlmul)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x4,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x0,
+};
+
+/* invalid fractional LMUL for VLEN <= 256: LMUL= 1/8, SEW = 64 */
+FIXTURE_VARIANT_ADD(v_csr_invalid, frac_lmul1)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x1d,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x20,
+};
+
+/* invalid integral LMUL for VLEN <= 16: LMUL= 2, SEW = 64 */
+FIXTURE_VARIANT_ADD(v_csr_invalid, int_lmul1)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x19,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x2,
+};
+
+/* invalid VL for VLEN <= 128: LMUL= 2, SEW = 64, VL = 8 */
+FIXTURE_VARIANT_ADD(v_csr_invalid, vl1)
+{
+ .vstart = 0x0,
+ .vl = 0x8,
+ .vtype = 0x19,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x0,
+ .vlenb_max = 0x10,
+};
+
+TEST_F(v_csr_invalid, ptrace_v_invalid_values)
+{
+ unsigned long vlenb;
+ pid_t pid;
+
+ if (!is_vector_supported())
+ SKIP(return, "Vector not supported");
+
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+ if (variant->vlenb_min) {
+ if (vlenb < variant->vlenb_min)
+ SKIP(return, "This test does not support VLEN < %lu\n",
+ variant->vlenb_min * 8);
+ }
+ if (variant->vlenb_max) {
+ if (vlenb > variant->vlenb_max)
+ SKIP(return, "This test does not support VLEN > %lu\n",
+ variant->vlenb_max * 8);
+ }
+
+ chld_lock = 1;
+ pid = fork();
+ ASSERT_LE(0, pid)
+ TH_LOG("fork: %m");
+
+ if (pid == 0) {
+ while (chld_lock == 1)
+ asm volatile("" : : "g"(chld_lock) : "memory");
+
+ asm(".option arch, +zve32x\n");
+ asm(".option arch, +c\n");
+ asm volatile("vsetvli x0, x0, e8, m8, tu, mu\n");
+
+ while (1) {
+ asm volatile("c.ebreak");
+ asm volatile("c.nop");
+ }
+ } else {
+ struct __riscv_v_regset_state *regset_data;
+ size_t regset_size;
+ struct iovec iov;
+ int status;
+ int ret;
+
+ /* attach */
+
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* unlock */
+
+ ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
+
+ /* resume and wait for the 1st c.ebreak */
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace GETREGSET */
+
+ regset_size = sizeof(*regset_data) + vlenb * 32;
+ regset_data = calloc(1, regset_size);
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify initial vsetvli x0, x0, e8, m8, tu, mu settings */
+
+ EXPECT_EQ(vlenb, regset_data->vlenb);
+ EXPECT_EQ(0UL, regset_data->vstart);
+ EXPECT_EQ(3UL, regset_data->vtype);
+ EXPECT_EQ(0UL, regset_data->vcsr);
+ EXPECT_EQ(0UL, regset_data->vl);
+
+ /* apply invalid settings from fixture variants */
+
+ regset_data->vlenb *= variant->vlenb_mul;
+ regset_data->vstart = variant->vstart;
+ regset_data->vtype = variant->vtype;
+ regset_data->vcsr = variant->vcsr;
+ regset_data->vl = variant->vl;
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ errno = 0;
+ ret = ptrace(PTRACE_SETREGSET, pid, NT_RISCV_VECTOR, &iov);
+ ASSERT_EQ(errno, EINVAL);
+ ASSERT_EQ(ret, -1);
+
+ /* cleanup */
+
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+ }
+}
+
TEST_HARNESS_MAIN
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 8/9] selftests: riscv: verify ptrace accepts valid vector csr values
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (6 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 7/9] selftests: riscv: verify ptrace rejects invalid vector csr inputs Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
2025-10-25 21:06 ` [PATCH v3 9/9] selftests: riscv: verify syscalls discard vector context Sergey Matyukevich
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add a test to v_ptrace test suite to verify that ptrace accepts the
valid input combinations of vector csr registers. Use kselftest
fixture variants to create multiple inputs for the test.
The test simulates a debug scenario with three breakpoints:
1. init: let the tracee set up its initial vector configuration
2. 1st bp: modify the tracee's vector csr registers from the debugger
- resume the tracee to execute a block without vector instructions
3. 2nd bp: read back the tracees's vector csr registers from the debugger
- compare with values set by the debugger
- resume the tracee to execute a block with vector instructions
4. 3rd bp: read back the tracess's vector csr registers again
- compare with values set by the debugger
The last check helps to confirm that ptrace validation check for vector
csr registers input values works properly and maintains an accurate view
of the tracee's vector context in debugger.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
.../testing/selftests/riscv/vector/v_ptrace.c | 217 ++++++++++++++++++
1 file changed, 217 insertions(+)
diff --git a/tools/testing/selftests/riscv/vector/v_ptrace.c b/tools/testing/selftests/riscv/vector/v_ptrace.c
index 6f3f228c0954..7e8fdebded07 100644
--- a/tools/testing/selftests/riscv/vector/v_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/v_ptrace.c
@@ -415,4 +415,221 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values)
}
}
+FIXTURE(v_csr_valid)
+{
+};
+
+FIXTURE_SETUP(v_csr_valid)
+{
+}
+
+FIXTURE_TEARDOWN(v_csr_valid)
+{
+}
+
+/* modifications of the initial 'vsetvli x0, x0, e8, m8, tu, mu' settings */
+FIXTURE_VARIANT(v_csr_valid)
+{
+ unsigned long vstart;
+ unsigned long vl;
+ unsigned long vtype;
+ unsigned long vcsr;
+ unsigned long vlenb_mul;
+ unsigned long vlenb_min;
+ unsigned long vlenb_max;
+};
+
+/* valid for VLEN >= 128: LMUL= 1/4, SEW = 32 */
+FIXTURE_VARIANT_ADD(v_csr_valid, frac_lmul1)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x16,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x10,
+ .vlenb_max = 0x0,
+};
+
+/* valid for VLEN >= 16: LMUL= 2, SEW = 32 */
+FIXTURE_VARIANT_ADD(v_csr_valid, int_lmul1)
+{
+ .vstart = 0x0,
+ .vl = 0x0,
+ .vtype = 0x11,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x2,
+ .vlenb_max = 0x0,
+};
+
+/* valid for VLEN >= 32: LMUL= 2, SEW = 32, VL = 2 */
+FIXTURE_VARIANT_ADD(v_csr_valid, int_lmul2)
+{
+ .vstart = 0x0,
+ .vl = 0x2,
+ .vtype = 0x11,
+ .vcsr = 0x0,
+ .vlenb_mul = 0x1,
+ .vlenb_min = 0x4,
+ .vlenb_max = 0x0,
+};
+
+TEST_F(v_csr_valid, ptrace_v_valid_values)
+{
+ unsigned long vlenb;
+ pid_t pid;
+
+ if (!is_vector_supported())
+ SKIP(return, "Vector not supported");
+
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+ if (variant->vlenb_min) {
+ if (vlenb < variant->vlenb_min)
+ SKIP(return, "This test does not support VLEN < %lu\n",
+ variant->vlenb_min * 8);
+ }
+ if (variant->vlenb_max) {
+ if (vlenb > variant->vlenb_max)
+ SKIP(return, "This test does not support VLEN > %lu\n",
+ variant->vlenb_max * 8);
+ }
+
+ chld_lock = 1;
+ pid = fork();
+ ASSERT_LE(0, pid)
+ TH_LOG("fork: %m");
+
+ if (pid == 0) {
+ while (chld_lock == 1)
+ asm volatile("" : : "g"(chld_lock) : "memory");
+
+ asm(".option arch, +zve32x\n");
+ asm(".option arch, +c\n");
+ asm volatile("vsetvli x0, x0, e8, m8, tu, mu\n");
+
+ while (1) {
+ asm volatile ("c.ebreak");
+ asm volatile ("c.nop");
+ /* V state clean: context will not be saved */
+ asm volatile ("c.ebreak");
+ asm volatile("vmv.v.i v0, -1");
+ /* V state dirty: context will be saved */
+ }
+ } else {
+ struct __riscv_v_regset_state *regset_data;
+ struct user_regs_struct regs;
+ size_t regset_size;
+ struct iovec iov;
+ int status;
+
+ /* attach */
+
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* unlock */
+
+ ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
+
+ /* resume and wait for the 1st c.ebreak */
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace GETREGSET */
+
+ regset_size = sizeof(*regset_data) + vlenb * 32;
+ regset_data = calloc(1, regset_size);
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify initial vsetvli x0, x0, e8, m8, tu, mu settings */
+
+ EXPECT_EQ(vlenb, regset_data->vlenb);
+ EXPECT_EQ(3UL, regset_data->vtype);
+ EXPECT_EQ(0UL, regset_data->vstart);
+ EXPECT_EQ(0UL, regset_data->vcsr);
+ EXPECT_EQ(0UL, regset_data->vl);
+
+ /* apply valid settings from fixture variants */
+
+ regset_data->vlenb *= variant->vlenb_mul;
+ regset_data->vstart = variant->vstart;
+ regset_data->vtype = variant->vtype;
+ regset_data->vcsr = variant->vcsr;
+ regset_data->vl = variant->vl;
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* skip 1st c.ebreak, then resume and wait for the 2nd c.ebreak */
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
+ regs.pc += 2;
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace GETREGSET */
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify vector csr regs from tracee context */
+
+ EXPECT_EQ(regset_data->vstart, variant->vstart);
+ EXPECT_EQ(regset_data->vtype, variant->vtype);
+ EXPECT_EQ(regset_data->vcsr, variant->vcsr);
+ EXPECT_EQ(regset_data->vl, variant->vl);
+ EXPECT_EQ(regset_data->vlenb, vlenb);
+
+ /* skip 2nd c.ebreak, then resume and wait for the 3rd c.ebreak */
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
+ regs.pc += 2;
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace GETREGSET */
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify vector csr regs from tracee context */
+
+ EXPECT_EQ(regset_data->vstart, variant->vstart);
+ EXPECT_EQ(regset_data->vtype, variant->vtype);
+ EXPECT_EQ(regset_data->vcsr, variant->vcsr);
+ EXPECT_EQ(regset_data->vl, variant->vl);
+ EXPECT_EQ(regset_data->vlenb, vlenb);
+
+ /* cleanup */
+
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+ }
+}
+
TEST_HARNESS_MAIN
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 9/9] selftests: riscv: verify syscalls discard vector context
2025-10-25 21:06 [PATCH v3 0/9] riscv: vector: misc ptrace fixes for debug use-cases Sergey Matyukevich
` (7 preceding siblings ...)
2025-10-25 21:06 ` [PATCH v3 8/9] selftests: riscv: verify ptrace accepts valid vector csr values Sergey Matyukevich
@ 2025-10-25 21:06 ` Sergey Matyukevich
8 siblings, 0 replies; 10+ messages in thread
From: Sergey Matyukevich @ 2025-10-25 21:06 UTC (permalink / raw)
To: linux-riscv, linux-kernel, linux-kselftest
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Oleg Nesterov, Shuah Khan, Thomas Huth, Charlie Jenkins,
Andy Chiu, Samuel Holland, Joel Granados, Conor Dooley,
Yong-Xuan Wang, Heiko Stuebner, Sergey Matyukevich
Add a test to v_ptrace test suite to verify that vector csr registers
are clobbered on syscalls.
Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com>
---
.../testing/selftests/riscv/vector/v_ptrace.c | 102 ++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/tools/testing/selftests/riscv/vector/v_ptrace.c b/tools/testing/selftests/riscv/vector/v_ptrace.c
index 7e8fdebded07..51a7cc71b2be 100644
--- a/tools/testing/selftests/riscv/vector/v_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/v_ptrace.c
@@ -183,6 +183,108 @@ TEST(ptrace_v_early_debug)
}
}
+TEST(ptrace_v_syscall_clobbering)
+{
+ unsigned long vlenb;
+ pid_t pid;
+
+ if (!is_vector_supported())
+ SKIP(return, "Vector not supported");
+
+ asm volatile("csrr %[vlenb], vlenb" : [vlenb] "=r"(vlenb));
+
+ chld_lock = 1;
+ pid = fork();
+ ASSERT_LE(0, pid)
+ TH_LOG("fork: %m");
+
+ if (pid == 0) {
+ while (chld_lock == 1)
+ asm volatile("" : : "g"(chld_lock) : "memory");
+
+ asm(".option arch, +zve32x\n");
+ asm(".option arch, +c\n");
+ asm volatile("vsetvli x0, x0, e8, m8, tu, mu\n");
+
+ while (1) {
+ asm volatile ("c.ebreak");
+ sleep(0);
+ }
+ } else {
+ struct __riscv_v_regset_state *regset_data;
+ struct user_regs_struct regs;
+ size_t regset_size;
+ struct iovec iov;
+ int status;
+
+ /* attach */
+
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* unlock */
+
+ ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, pid, &chld_lock, 0));
+
+ /* resume and wait for the 1st c.ebreak */
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vector csr regs using ptrace GETREGSET */
+
+ regset_size = sizeof(*regset_data) + vlenb * 32;
+ regset_data = calloc(1, regset_size);
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify initial vsetvli x0, x0, e8, m8, tu, mu settings */
+
+ EXPECT_EQ(3UL, regset_data->vtype);
+ EXPECT_EQ(0UL, regset_data->vstart);
+ EXPECT_EQ(16UL, regset_data->vlenb);
+ EXPECT_EQ(0UL, regset_data->vcsr);
+ EXPECT_EQ(0UL, regset_data->vl);
+
+ /* skip 1st c.ebreak, then resume and wait for the 2nd c.ebreak */
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
+ regs.pc += 2;
+ ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
+
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFSTOPPED(status));
+
+ /* read tracee vtype using ptrace GETREGSET */
+
+ iov.iov_base = regset_data;
+ iov.iov_len = regset_size;
+
+ ASSERT_EQ(0, ptrace(PTRACE_GETREGSET, pid, NT_RISCV_VECTOR, &iov));
+
+ /* verify that V state is illegal after syscall */
+
+ EXPECT_EQ((1UL << (__riscv_xlen - 1)), regset_data->vtype);
+ EXPECT_EQ(vlenb, regset_data->vlenb);
+ EXPECT_EQ(0UL, regset_data->vstart);
+ EXPECT_EQ(0UL, regset_data->vcsr);
+ EXPECT_EQ(0UL, regset_data->vl);
+
+ /* cleanup */
+
+ ASSERT_EQ(0, kill(pid, SIGKILL));
+ }
+}
+
FIXTURE(v_csr_invalid)
{
};
--
2.51.0
^ permalink raw reply related [flat|nested] 10+ messages in thread