Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "slack up" <yxcnew@outlook.com>
Cc: kvm@vger.kernel.org
Subject: Re: [RFC PATCH 3/3] KVM: riscv: selftests: Add Vector owner regression tests
Date: Tue, 27 Oct 2026 14:03:11 +0000	[thread overview]
Message-ID: <sashiko-outbox-186684@kernel.org> (raw)
In-Reply-To: <OSQPR01MB6326D30F15C187093E41762AC2602@OSQPR01MB6326.apcprd01.prod.exchangelabs.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] KVM: riscv: selftests: prevent C calls from clobbering v0 in guest code
- [Medium] KVM: riscv: selftests: split Vector owner tests reimplement kvm_util helpers
- [Medium] KVM: riscv: selftests: vector_owner_lifetime_test clears owner before new_vm
--

commit cd1b458fe31c51e7e8df73a0ffb2811bb58b58bb
Author: Xiaochun Yu <yxcnew@outlook.com>

KVM: riscv: selftests: Add Vector owner regression tests

This commit adds three RISC-V KVM selftests covering guest Vector owner
lifetime across vCPU destruction, KVM_GET_ONE_REG and KVM_SET_ONE_REG state
synchronization, and partial user copy faults during KVM_SET_ONE_REG.

> diff --git a/tools/testing/selftests/kvm/riscv/vector_owner_fault_test.c b/tools/testing/selftests/kvm/riscv/vector_owner_fault_test.c
> new file mode 100644
> index 0000000000000..a25dd21db77f9
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/riscv/vector_owner_fault_test.c
> @@ -0,0 +1,147 @@

[ ... ]

> +static void guest_code(void)
> +{
> +	unsigned long i, vlenb;
> +
> +	csr_set(CSR_SSTATUS, SR_VS_INITIAL);
> +	vlenb = csr_read(CSR_VLENB);
> +	GUEST_ASSERT(vlenb && vlenb <= RISCV_MAX_VLENB);
> +	for (i = 0; i < vlenb; i++)
> +		source[i] = OLD_PATTERN;
> +
> +	asm volatile(".option push\n"
> +		".option arch, +v\n"
> +		"vsetvli zero, %1, e8, m1, ta, ma\n"
> +		"vle8.v v0, (%0)\n"
> +		".option pop\n"
> +		:
> +		: "r"(source), "r"(vlenb)
> +		: "memory");
> +	GUEST_SYNC(1);
> +
> +	asm volatile(".option push\n"
> +		".option arch, +v\n"
> +		"vsetvli zero, %1, e8, m1, ta, ma\n"
> +		"vse8.v v0, (%0)\n"
> +		".option pop\n"
> +		:
> +		: "r"(observed), "r"(vlenb)
> +		: "memory");
> +	GUEST_DONE();
> +}

[Severity: Medium]
Can C function calls and compiler auto-vectorization clobber v0 while
sstatus.VS is enabled in guest_code()?

When building the RISC-V KVM selftests with a toolchain targeting RVV (such
as a native GCC or Clang configured for rv64gcv/rva23, or with
EXTRA_CFLAGS=-march=rv64gcv), Makefile.kvm compiles guest code and
lib/string_override.c with -O2 -fno-builtin-memset and without
-fno-tree-vectorize or -fno-vectorize.

In guest_code() here, as well as guest_code() in
tools/testing/selftests/kvm/riscv/vector_owner_onereg_test.c and old_guest()
in tools/testing/selftests/kvm/riscv/vector_owner_lifetime_test.c, the guest
sets SR_VS_INITIAL, loads a pattern into the caller-saved register v0 (in an
inline asm block without vector register clobbers), and then calls
GUEST_SYNC() or GUEST_DONE() while sstatus.VS is still active:

  GUEST_SYNC() / GUEST_DONE()
    -> ucall()
      -> ucall_alloc()
        -> memset(uc->args, 0, sizeof(uc->args))

If the compiler vectorizes memset() or the surrounding C loops/helpers, it is
free to use caller-saved v0 before the sbi_ecall traps to the host. That
overwrites v0 with zeros before KVM saves or inspects the register, causing
false test failures in vector_owner_fault_test and vector_owner_onereg_test,
and zeroing old_vcpu's v0 before exit in vector_owner_lifetime_test so a
leaked resident v0 would match the zero pattern expected by new_guest().

Should the guest helpers keep sstatus.VS disabled across C calls (or perform
the vector load/store and ECALL inside a single assembly block) so the
compiler cannot touch v0 while guest Vector state is live?

> +static void pin_to_one_cpu(void)
> +{
> +	cpu_set_t allowed, pinned;
> +	int cpu;
> +
> +	kvm_sched_getaffinity(0, sizeof(allowed), &allowed);
> +	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
> +		if (CPU_ISSET(cpu, &allowed))
> +			break;
> +	}
> +	TEST_ASSERT(cpu < CPU_SETSIZE, "no CPU available");
> +	CPU_ZERO(&pinned);
> +	CPU_SET(cpu, &pinned);
> +	kvm_sched_setaffinity(0, sizeof(pinned), &pinned);
> +}

[ ... ]

> +int main(void)
> +{

[ ... ]

> +	pin_to_one_cpu();
> +	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
> +	TEST_REQUIRE(__vcpu_has_isa_ext(vcpu, KVM_RISCV_ISA_EXT_V));
> +	vm_init_vector_tables(vm);
> +	vcpu_init_vector_tables(vcpu);
> +	vlenb_id = KVM_REG_RISCV | KVM_REG_RISCV_VECTOR |
> +		KVM_REG_SIZE_ULONG | KVM_REG_RISCV_VECTOR_CSR_REG(vlenb);
> +	ret = __vcpu_get_reg(vcpu, vlenb_id, &vlenb);
> +	TEST_ASSERT(!ret, "GET vlenb failed: %d", ret);
> +	TEST_ASSERT(vlenb && vlenb <= RISCV_MAX_VLENB &&
> +		    !(vlenb & (vlenb - 1)), "bad vlenb: %lu", vlenb);
> +	size = (u64)__builtin_ctzl(vlenb) << KVM_REG_SIZE_SHIFT;
> +	v0_id = KVM_REG_RISCV | KVM_REG_RISCV_VECTOR | size |
> +		KVM_REG_RISCV_VECTOR_REG(0);
> +
> +	page_size = sysconf(_SC_PAGESIZE);
> +	TEST_ASSERT(page_size > 0 && vlenb <= 2 * page_size, "bad page size");
> +	mapping = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
> +		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> +	TEST_ASSERT(mapping != MAP_FAILED, "mmap failed: %d", errno);

[ ... ]

> +	kvm_vm_free(vm);
> +	ret = munmap(mapping, 2 * page_size);
> +	TEST_ASSERT(!ret, "munmap failed: %d", errno);
> +	return 0;
> +}

[Severity: Medium]
Could these three tests use existing kvm_util/kvm_syscalls helpers and be
consolidated into a single Vector owner test binary?

Specifically:
1. pin_to_one_cpu() in vector_owner_fault_test.c,
   vector_owner_lifetime_test.c, and vector_owner_onereg_test.c reimplements
   pin_self_to_any_cpu() (or pin_self_to_cpu()) from
   tools/testing/selftests/kvm/include/kvm_util.h.
2. main() in vector_owner_fault_test.c hand-rolls mmap() and munmap() with
   TEST_ASSERT() instead of using kvm_mmap() and kvm_munmap() from
   tools/testing/selftests/kvm/include/kvm_syscalls.h, and both
   vector_owner_fault_test.c and vector_owner_onereg_test.c call
   __vcpu_get_reg() with a manual TEST_ASSERT() for the ULONG-sized vlenb
   register instead of vcpu_get_reg().
3. Splitting the lifetime, ONE_REG, and fault sub-cases across three separate
   binaries in tools/testing/selftests/kvm/Makefile.kvm duplicates the Vector
   VM/vCPU initialization, vlenb and v0 register ID calculation, guest
   vle8.v/vse8.v wrappers, and ucall run helpers across all three files.

> diff --git a/tools/testing/selftests/kvm/riscv/vector_owner_lifetime_test.c b/tools/testing/selftests/kvm/riscv/vector_owner_lifetime_test.c
> new file mode 100644
> index 0000000000000..c7b057b27cd0e
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/riscv/vector_owner_lifetime_test.c
> @@ -0,0 +1,127 @@

[ ... ]

> +int main(void)
> +{
> +	struct kvm_vm *old_vm, *new_vm;
> +	struct kvm_vcpu *old_vcpu, *new_vcpu;
> +
> +	pin_to_one_cpu();
> +	old_vm = create_vector_vm(&old_vcpu, old_guest);
> +	run_to_done(old_vcpu);
> +	kvm_vm_free(old_vm);
> +
> +	new_vm = create_vector_vm(&new_vcpu, new_guest);
> +	run_to_done(new_vcpu);
> +	kvm_vm_free(new_vm);

[Severity: Medium]
Does freeing old_vm while no other VM is open clear the per-CPU Vector owner
before new_vm is ever created?

In main() in tools/testing/selftests/kvm/riscv/vector_owner_lifetime_test.c,
kvm_vm_free(old_vm) closes the only open VM fd, which drops kvm_usage_count
to zero:

  kvm_destroy_vm()
    -> kvm_disable_virtualization()
      -> cpuhp_remove_state(CPUHP_AP_KVM_ONLINE)
        -> kvm_offline_cpu()
          -> kvm_arch_disable_virtualization_cpu()
            -> kvm_riscv_csr_cleanup()
              -> kvm_riscv_clear_former_vcpu()
                -> kvm_riscv_vcpu_vector_owner_invalidate()

Because kvm_riscv_vcpu_vector_owner_invalidate() zeroes the per-CPU
kvm_riscv_vector_owner structure when the last VM is destroyed, the resident
owner from old_vcpu is already cleared before new_vm is created. When
new_vcpu runs, kvm_riscv_vcpu_vector_owner_load() in
arch/riscv/kvm/vcpu_vector.c always hits the !owner->identity branch:

	} else if (!owner->identity) {
		reason = vcpu->arch.last_exit_cpu == cpu ?
			 KVM_RISCV_VECTOR_OWNER_EMPTY :
			 KVM_RISCV_VECTOR_OWNER_MIGRATION;
	} else if (owner->identity != (unsigned long)vcpu) {
		reason = KVM_RISCV_VECTOR_OWNER_OTHER_VCPU;
	} else if (owner->cookie != vcpu->arch.vector_owner_cookie) {
		reason = KVM_RISCV_VECTOR_OWNER_COOKIE;
	} else if (owner->generation != vcpu->arch.vector_state_generation) {
		reason = KVM_RISCV_VECTOR_OWNER_GENERATION;

So the test never exercises the stale-vCPU identity or cookie checks (and
even if a second VM is kept alive so kvm_usage_count does not drop to zero
and new_vcpu reuses old_vcpu's struct kvm_vcpu address, old_vcpu leaves
owner->generation == 2 while new_vcpu starts its first KVM_RUN with
vector_state_generation == 1).

Should the test keep a VM alive across old_vcpu destruction (or recreate the
vCPU in a way that preserves the per-CPU owner state and matches generation)
so that the stale-owner checks in kvm_riscv_vcpu_vector_owner_load() are
actually reached?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/OSQPR01MB63265385597FE472C4209353C2602@OSQPR01MB6326.apcprd01.prod.exchangelabs.com?part=3

      reply	other threads:[~2026-10-27 14:03 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 13:13 [RFC PATCH 0/3] RISC-V: KVM: Preserve resident guest Vector state slack up
2026-10-27 13:13 ` [RFC PATCH 1/3] riscv: vector: Add a guest owner invalidation hook slack up
2026-10-27 14:03   ` sashiko-bot
2026-10-27 13:13 ` [RFC PATCH 2/3] RISC-V: KVM: Preserve guest Vector state across vCPU put/load slack up
2026-10-27 14:11   ` sashiko-bot
2026-10-27 13:13 ` [RFC PATCH 3/3] KVM: riscv: selftests: Add Vector owner regression tests slack up
2026-10-27 14:03   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-186684@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=yxcnew@outlook.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox