* [PATCH v2 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector
[not found] <20260715051629.1169645-1-tchiu@tenstorrent.com>
@ 2026-07-15 5:16 ` Andy Chiu
2026-07-15 5:34 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Andy Chiu @ 2026-07-15 5:16 UTC (permalink / raw)
To: anup, Atish Patra, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Greentime Hu, Andy Chiu, Eric Biggers,
Vincent Chen, kvm, kvm-riscv, linux-riscv
Cc: Andy Chiu, dfustini, Deepak Gupta, Charlie Jenkins, Sean Chang,
Thomas Huth, Yong-Xuan Wang
Running vector workloads like perf + mcf on KVM can result in an
unexpected termination due to a vtype corruption. This happens because
the kernel-mode vector (KMV) misattributes the guest's vcpu context as
the user's context and source from a wrong status.VS.
The simplified call chain that results in this problem is shown as
follow:
__riscv_sys_ioctl()
kvm_arch_vcpu_ioctl_run()
kvm_riscv_vcpu_exit()
kvm_riscv_vcpu_sbi_ecall()
kvm_riscv_vcpu_pmu_ctr_stop()
kvm_vcpu_write_guest()
__copy_to_user()
enter_vector_usercopy()
kernel_vector_begin()
kernel_vector_begin() should use the sstatus.VS from guest's vcpu
context instead of task_pt_reg(current). Also, it should not save
guest's v-reg into the user's context memory.
To resolve this, the vcpu context must be correctly saved when KMV is
serving a guest. However, invoking KVM functions directly from generic
RISC-V architecture code introduces a reverse dependency, breaking
builds when KVM is configured as N or M.
Address this by registering an RCU-protected callback for context
flushing. KVM registers this callback at module initialization and
unregisters it on exit. When KMV starts a kernel context, it can now
safely flush the vector context via the callback.
Fixes: ecd2ada8a5e0 ("riscv: Add support for kernel mode vector")
Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v2: address concerns pointed out by sashiko
- encloses vcpu_flush_v_callback() with rcu_read_{lock,unlock}
- put riscv_v_start before put_cpu_vector_context() to prevent
redundant context save
- protect v context operations against softirqs
- apply bitmask when reading SR_VS out of guest's sstatus
---
arch/riscv/include/asm/kvm_vcpu_vector.h | 24 +++++++++++
arch/riscv/include/asm/processor.h | 2 +
arch/riscv/include/asm/vector.h | 5 +++
arch/riscv/kernel/kernel_mode_vector.c | 51 ++++++++++++++++++------
arch/riscv/kvm/main.c | 4 ++
arch/riscv/kvm/vcpu.c | 10 +++++
arch/riscv/kvm/vcpu_vector.c | 20 ++++++++++
7 files changed, 104 insertions(+), 12 deletions(-)
diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h
index 57a798a4cb0d..6371d5ea5392 100644
--- a/arch/riscv/include/asm/kvm_vcpu_vector.h
+++ b/arch/riscv/include/asm/kvm_vcpu_vector.h
@@ -35,6 +35,22 @@ void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx);
void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx);
int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu);
void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu);
+void kvm_riscv_register_vctx_callback(void (*func)(void));
+void kvm_riscv_unregister_vctx_callback(void);
+void kvm_riscv_vcpu_flush_vector(void);
+
+static inline void kvm_riscv_v_init(void)
+{
+ if (has_vector())
+ kvm_riscv_register_vctx_callback(&kvm_riscv_vcpu_flush_vector);
+}
+
+static inline void kvm_riscv_v_exit(void)
+{
+ if (has_vector())
+ kvm_riscv_unregister_vctx_callback();
+}
+
#else
struct kvm_cpu_context;
@@ -69,6 +85,14 @@ static inline int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
{
}
+
+static inline void kvm_riscv_v_init(void)
+{
+}
+
+static inline void kvm_riscv_v_exit(void)
+{
+}
#endif
int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
index 812517b2cec1..6e9db2b209e6 100644
--- a/arch/riscv/include/asm/processor.h
+++ b/arch/riscv/include/asm/processor.h
@@ -97,6 +97,8 @@ struct pt_regs;
#define RISCV_V_CTX_UNIT_DEPTH 0x00010000
#define RISCV_KERNEL_MODE_V 0x00000001
+#define RISCV_V_VCPU_NEED_RESTORE 0x00000002
+#define RISCV_V_VCPU_CTX 0x00000004
#define RISCV_PREEMPT_V 0x00000100
#define RISCV_PREEMPT_V_DIRTY 0x80000000
#define RISCV_PREEMPT_V_NEED_RESTORE 0x40000000
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 00cb9c0982b1..fffe72a77208 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -58,6 +58,11 @@ static inline u32 riscv_v_flags(void)
return READ_ONCE(current->thread.riscv_v_flags);
}
+static inline void riscv_v_flags_set(u32 flags)
+{
+ WRITE_ONCE(current->thread.riscv_v_flags, flags);
+}
+
static __always_inline bool has_vector(void)
{
return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c
index 6627e1a82e19..1c2463aee247 100644
--- a/arch/riscv/kernel/kernel_mode_vector.c
+++ b/arch/riscv/kernel/kernel_mode_vector.c
@@ -12,16 +12,31 @@
#include <linux/types.h>
#include <asm/vector.h>
+#include <asm/kvm_vcpu_vector.h>
#include <asm/switch_to.h>
#include <asm/simd.h>
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
#include <asm/asm-prototypes.h>
#endif
-static inline void riscv_v_flags_set(u32 flags)
+static void (* __rcu kvm_flush_vector_ctx_callback)(void);
+
+void kvm_riscv_register_vctx_callback(void (*func)(void))
{
- WRITE_ONCE(current->thread.riscv_v_flags, flags);
+ if (WARN_ON_ONCE(rcu_access_pointer(kvm_flush_vector_ctx_callback)))
+ return;
+
+ rcu_assign_pointer(kvm_flush_vector_ctx_callback, func);
}
+EXPORT_SYMBOL_GPL(kvm_riscv_register_vctx_callback);
+
+void kvm_riscv_unregister_vctx_callback(void)
+{
+ rcu_assign_pointer(kvm_flush_vector_ctx_callback, NULL);
+ synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(kvm_riscv_unregister_vctx_callback);
+
static inline void riscv_v_start(u32 flags)
{
@@ -86,6 +101,22 @@ void put_cpu_vector_context(void)
}
EXPORT_SYMBOL_GPL(put_cpu_vector_context);
+static void __riscv_flush_vector_context(void)
+{
+ void (*vcpu_flush_v_callback)(void);
+
+ if (riscv_v_flags() & RISCV_V_VCPU_CTX) {
+ rcu_read_lock();
+ vcpu_flush_v_callback = rcu_dereference(kvm_flush_vector_ctx_callback);
+ vcpu_flush_v_callback();
+ rcu_read_unlock();
+ return;
+ }
+
+ riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current));
+ riscv_v_vstate_set_restore(current, task_pt_regs(current));
+}
+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
static __always_inline u32 *riscv_v_flags_ptr(void)
{
@@ -129,7 +160,7 @@ static int riscv_v_stop_kernel_context(void)
static int riscv_v_start_kernel_context(void)
{
- struct __riscv_v_ext_state *kvstate, *uvstate;
+ struct __riscv_v_ext_state *kvstate;
kvstate = ¤t->thread.kernel_vstate;
if (!kvstate->datap)
@@ -147,13 +178,10 @@ static int riscv_v_start_kernel_context(void)
}
/* Transfer the ownership of V from user to kernel, then save */
- riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);
- if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) {
- uvstate = ¤t->thread.vstate;
- __riscv_v_vstate_save(uvstate, uvstate->datap);
- }
- riscv_preempt_v_clear_dirty(current);
- riscv_v_vstate_set_restore(current, task_pt_regs(current));
+ get_cpu_vector_context();
+ __riscv_flush_vector_context();
+ riscv_v_start(RISCV_PREEMPT_V);
+ put_cpu_vector_context();
return 0;
}
@@ -219,8 +247,7 @@ void kernel_vector_begin(void)
if (riscv_v_start_kernel_context()) {
get_cpu_vector_context();
- riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current));
- riscv_v_vstate_set_restore(current, task_pt_regs(current));
+ __riscv_flush_vector_context();
}
riscv_v_enable();
diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c
index 0924c75100a2..da8a5f6d4b4d 100644
--- a/arch/riscv/kvm/main.c
+++ b/arch/riscv/kvm/main.c
@@ -14,6 +14,7 @@
#include <asm/kvm_mmu.h>
#include <asm/kvm_nacl.h>
#include <asm/sbi.h>
+#include <asm/kvm_vcpu_vector.h>
DEFINE_STATIC_KEY_FALSE(kvm_riscv_vsstage_tlb_no_gpa);
@@ -76,6 +77,7 @@ static void kvm_riscv_teardown(void)
{
kvm_riscv_aia_exit();
kvm_riscv_nacl_exit();
+ kvm_riscv_v_exit();
kvm_unregister_perf_callbacks();
}
@@ -170,6 +172,8 @@ static int __init riscv_kvm_init(void)
kvm_riscv_setup_vendor_features();
+ kvm_riscv_v_init();
+
kvm_register_perf_callbacks();
rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index cf6e231e76e2..b7b68809a581 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -603,9 +603,11 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
kvm_riscv_vcpu_host_fp_save(&vcpu->arch.host_context);
kvm_riscv_vcpu_guest_fp_restore(&vcpu->arch.guest_context,
vcpu->arch.isa);
+ get_cpu_vector_context();
kvm_riscv_vcpu_host_vector_save(&vcpu->arch.host_context);
kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context,
vcpu->arch.isa);
+ put_cpu_vector_context();
kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
@@ -626,9 +628,11 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
kvm_riscv_vcpu_host_fp_restore(&vcpu->arch.host_context);
kvm_riscv_vcpu_timer_save(vcpu);
+ get_cpu_vector_context();
kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context,
vcpu->arch.isa);
kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context);
+ put_cpu_vector_context();
if (kvm_riscv_nacl_available()) {
nsh = nacl_shmem();
@@ -765,6 +769,12 @@ static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
kvm_riscv_vcpu_swap_in_guest_state(vcpu);
guest_state_enter_irqoff();
+ if (current->thread.riscv_v_flags & RISCV_V_VCPU_NEED_RESTORE) {
+ current->thread.riscv_v_flags &= ~RISCV_V_VCPU_NEED_RESTORE;
+ __kvm_riscv_vector_restore(gcntx);
+ gcntx->sstatus = (gcntx->sstatus & ~SR_VS) | SR_VS_CLEAN;
+ }
+
if (kvm_riscv_nacl_sync_sret_available()) {
nsh = nacl_shmem();
diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
index 62d2fb77bb9b..d25c10bd99a8 100644
--- a/arch/riscv/kvm/vcpu_vector.c
+++ b/arch/riscv/kvm/vcpu_vector.c
@@ -58,6 +58,7 @@ void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
if (riscv_isa_extension_available(isa, v))
__kvm_riscv_vector_restore(cntx);
kvm_riscv_vcpu_vector_clean(cntx);
+ riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_CTX);
}
}
@@ -72,6 +73,7 @@ void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
{
if (!kvm_riscv_isa_check_host(V))
__kvm_riscv_vector_restore(cntx);
+ riscv_v_flags_set(riscv_v_flags() & ~RISCV_V_VCPU_CTX);
}
int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu)
@@ -95,6 +97,24 @@ void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
kfree(vcpu->arch.guest_context.vector.datap);
kfree(vcpu->arch.host_context.vector.datap);
}
+
+void kvm_riscv_vcpu_flush_vector(void)
+{
+ struct kvm_vcpu *vcpu = *this_cpu_ptr(kvm_get_running_vcpus());
+
+ /*
+ * Only reached from __riscv_flush_vector_context() when RISCV_V_VCPU_CTX is set, which
+ * always have kvm_get_running_vcpus non-NULL.
+ */
+ if (WARN_ON_ONCE(!vcpu))
+ return;
+
+ kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context, vcpu->arch.isa);
+
+ if ((vcpu->arch.guest_context.sstatus & SR_VS) != SR_VS_OFF)
+ riscv_v_flags_set(riscv_v_flags() | RISCV_V_VCPU_NEED_RESTORE);
+}
+
#endif
static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector
2026-07-15 5:16 ` [PATCH v2 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector Andy Chiu
@ 2026-07-15 5:34 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-15 5:34 UTC (permalink / raw)
To: Andy Chiu; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The `RISCV_V_VCPU_NEED_RESTORE` thread flag is leaked across KVM_RUN boundaries, leading to uninitialized kernel memory disclosure.
--
--- Patch [3]: [PATCH v2 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector ---
commit 48826c7676c07c389b4f37039e65e239445a85c8
Author: Andy Chiu <tchiu@tenstorrent.com>
RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector
This patch registers an RCU-protected callback to flush the vector context
when the kernel-mode vector is serving a guest. This addresses vtype
corruption during vector workloads by correctly saving the guest's context
and sourcing the right status.VS.
> diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
> index cf6e231e76e25..b7b68809a5817 100644
> --- a/arch/riscv/kvm/vcpu.c
> +++ b/arch/riscv/kvm/vcpu.c
[ ... ]
> @@ -765,6 +769,12 @@ static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
> kvm_riscv_vcpu_swap_in_guest_state(vcpu);
> guest_state_enter_irqoff();
>
> + if (current->thread.riscv_v_flags & RISCV_V_VCPU_NEED_RESTORE) {
> + current->thread.riscv_v_flags &= ~RISCV_V_VCPU_NEED_RESTORE;
> + __kvm_riscv_vector_restore(gcntx);
> + gcntx->sstatus = (gcntx->sstatus & ~SR_VS) | SR_VS_CLEAN;
> + }
> +
[Severity: Critical]
Could this leak uninitialized kernel memory to the guest if the thread flag
carries over from a previous vCPU run?
If a softirq uses Kernel Mode Vector while a vCPU is loaded, it sets
RISCV_V_VCPU_NEED_RESTORE via kvm_riscv_vcpu_flush_vector(). If the thread
subsequently exits KVM to userspace, and then runs a different vCPU (for
example, one without the vector extension), this code will consume the
leaked flag.
Will this blindly restore the new vCPU's uninitialized gcntx->vector.datap
(which was allocated via kmalloc) into the physical vector registers, and
illegitimately force SR_VS_CLEAN in the guest's sstatus?
[ ... ]
> diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
> index 62d2fb77bb9b9..d25c10bd99a89 100644
> --- a/arch/riscv/kvm/vcpu_vector.c
> +++ b/arch/riscv/kvm/vcpu_vector.c
[ ... ]
> @@ -72,6 +73,7 @@ void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
> {
> if (!kvm_riscv_isa_check_host(V))
> __kvm_riscv_vector_restore(cntx);
> + riscv_v_flags_set(riscv_v_flags() & ~RISCV_V_VCPU_CTX);
> }
[Severity: Critical]
Should RISCV_V_VCPU_NEED_RESTORE also be cleared here to prevent it from
leaking to the thread's future execution?
Since kvm_arch_vcpu_put() calls kvm_riscv_vcpu_host_vector_restore() during
KVM exit, clearing it here might prevent the cross-vCPU leakage described
above.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715051629.1169645-1-tchiu@tenstorrent.com?part=3
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-15 5:34 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260715051629.1169645-1-tchiu@tenstorrent.com>
2026-07-15 5:16 ` [PATCH v2 3/3] RISC-V: KVM: fix vcpu vector context handling for kernel-mode vector Andy Chiu
2026-07-15 5:34 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox