Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Anup Patel" <anup.patel@oss.qualcomm.com>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH] RISC-V: KVM: Convert IMSIC vsfile_lock to a raw spinlock
Date: Sat, 14 Nov 2026 14:08:52 +0000	[thread overview]
Message-ID: <sashiko-outbox-214611@kernel.org> (raw)
In-Reply-To: <20261114135056.202556-1-anup.patel@oss.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] KVM: riscv: unordered MMIO write in
  kvm_riscv_vcpu_aia_imsic_inject()
- [High] KVM: riscv: race on IRQ_VS_EXT in
  kvm_riscv_vcpu_aia_imsic_inject()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [Critical] RISC-V: KVM: double-free in
  kvm_riscv_vcpu_aia_imsic_update() [1]
--

commit 7416cdfbe4d0521c182f4faca63b3ac6e4ae60b3
Author: Anup Patel <anup.patel@oss.qualcomm.com>

RISC-V: KVM: Convert IMSIC vsfile_lock to a raw spinlock

This commit converts imsic->vsfile_lock from rwlock_t to raw_spinlock_t,
removes vsfile_hgei_lock, moves imsic_swfile_extirq_update() outside
vsfile_lock, and switches kvm_riscv_vcpu_aia_imsic_inject() to
writel_relaxed().

> diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c
> index 302938972aa0e..0b9d15721d52f 100644
> --- a/arch/riscv/kvm/aia_imsic.c
> +++ b/arch/riscv/kvm/aia_imsic.c

[ ... ]

> @@ -726,10 +715,10 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
>  		return 1;
>  
>  	/* Read old IMSIC VS-file details */
> -	read_lock_irqsave(&imsic->vsfile_lock, flags);
> +	raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>  	old_vsfile_hgei = imsic->vsfile_hgei;
>  	old_vsfile_cpu = imsic->vsfile_cpu;
> -	read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +	raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);

[Severity: Critical]
This problem wasn't introduced by this patch, but can [1] cause a
double free of old_vsfile_hgei when kvm_riscv_vcpu_aia_imsic_update()
races with kvm_riscv_vcpu_aia_imsic_release()?

kvm_riscv_vcpu_aia_imsic_update() caches old_vsfile_hgei and
old_vsfile_cpu under imsic->vsfile_lock and drops the lock before later
freeing the old HGEI slot:

CPU0                                  CPU1
kvm_riscv_vcpu_aia_imsic_update()     kvm_riscv_vcpu_aia_imsic_release()
  raw_spin_lock_irqsave(...)
  old_vsfile_hgei = imsic->vsfile_hgei;
  old_vsfile_cpu = imsic->vsfile_cpu;
  raw_spin_unlock_irqrestore(...)
                                        raw_spin_lock_irqsave(...)
                                        old_vsfile_hgei = imsic->vsfile_hgei;
                                        old_vsfile_cpu = imsic->vsfile_cpu;
                                        imsic->vsfile_cpu = -1;
                                        imsic->vsfile_hgei = -1;
                                        raw_spin_unlock_irqrestore(...)
                                        ...
                                        kvm_riscv_aia_free_hgei(...)
  ...
  if (old_vsfile_cpu >= 0) {
    ...
    kvm_riscv_aia_free_hgei(...)  <-- double free

If a concurrent CPU hotplug or vCPU reset runs
kvm_riscv_vcpu_aia_imsic_release() in that window, both paths free the
same HGEI slot.  If that slot was reallocated to another VM in between,
the second free corrupts cross-VM interrupt state.

[ ... ]

> @@ -970,17 +965,26 @@ int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
>  	if (imsic->nr_msis <= iid)
>  		return -EINVAL;
>  
> -	read_lock_irqsave(&imsic->vsfile_lock, flags);
> +	raw_spin_lock_irqsave(&imsic->vsfile_lock, flags);
>  
>  	if (imsic->vsfile_cpu >= 0) {
> -		writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
> +		/*
> +		 * We can use writel_relaxed() over here because the
> +		 * raw_spin_unlock() below will do mmiowb_spin_unlock()
> +		 * which in-turn drains the write-buffer ensuring the
> +		 * write is visible to the CPU owning the IMSIC VS-file.
> +		 */
> +		writel_relaxed(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);

[Severity: High]
Does replacing writel() with writel_relaxed() leave the MMIO write to
imsic->vsfile_va unordered with raw_spin_unlock_irqrestore()?

mmiowb_spin_unlock() only emits mmiowb() when ms->mmiowb_pending is set:

include/asm-generic/mmiowb.h:mmiowb_spin_unlock() {
	struct mmiowb_state *ms = __mmiowb_state();

	if (unlikely(ms->mmiowb_pending)) {
		ms->mmiowb_pending = 0;
		mmiowb();
	}
}

On RISC-V, writel() calls __io_bw() and __io_aw() (which calls
mmiowb_set_pending()), whereas writel_relaxed() uses __io_rbw() and
__io_raw(), which are both no-ops.  Because writel_relaxed() never sets
ms->mmiowb_pending, raw_spin_unlock_irqrestore() does not emit mmiowb()
(RISCV_FENCE(o, w)).  If the vCPU concurrently migrates or releases its
VS-file in kvm_riscv_vcpu_aia_imsic_update() or
kvm_riscv_vcpu_aia_imsic_release(), the store to IMSIC_MMIO_SETIPNUM_LE
can arrive after imsic_vsfile_read() has cleared the old VS-file and
kvm_riscv_aia_free_hgei() has freed the slot, losing the guest interrupt
or delivering a spurious MSI to another vCPU.

>  	} else {
>  		eix = &imsic->swfile->eix[iid / BITS_PER_TYPE(u64)];
>  		set_bit(iid & (BITS_PER_TYPE(u64) - 1), eix->eip);
> -		imsic_swfile_extirq_update(vcpu);
> +		swfile_update = true;
>  	}
>  
> -	read_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +	raw_spin_unlock_irqrestore(&imsic->vsfile_lock, flags);
> +
> +	if (swfile_update)
> +		imsic_swfile_extirq_update(vcpu);

[Severity: High]
Can moving imsic_swfile_extirq_update() outside imsic->vsfile_lock race
with kvm_riscv_vcpu_aia_imsic_update() and leave IRQ_VS_EXT stuck pending?

imsic_swfile_read() clears imsic->swfile and unsets IRQ_VS_EXT without
taking swfile_extirq_lock, relying on vsfile_lock to serialize against
SW-file producers:

CPU0                                  CPU1
kvm_riscv_vcpu_aia_imsic_inject()     kvm_riscv_vcpu_aia_imsic_update()
  raw_spin_lock_irqsave(&vsfile_lock)
  /* vsfile_cpu < 0 */
  set_bit(..., eix->eip)
  swfile_update = true
  raw_spin_unlock_irqrestore(...)
  imsic_swfile_extirq_update()
    raw_spin_lock_irqsave(&extirq_lock)
    /* reads eidelivery && topei != 0 */
                                        raw_spin_lock_irqsave(&vsfile_lock)
                                        imsic->vsfile_cpu = vcpu->cpu
                                        raw_spin_unlock_irqrestore(...)
                                        imsic_swfile_read(..., true, ...)
                                          memset(imsic->swfile, 0, ...)
                                          kvm_riscv_vcpu_unset_interrupt(
                                              vcpu, IRQ_VS_EXT)
    kvm_riscv_vcpu_set_interrupt(
        vcpu, IRQ_VS_EXT)  <-- re-asserts IRQ_VS_EXT

Once the vCPU is using a hardware VS-file (HSTATUS.VGEIN > 0), guest
accesses to CSR_VSTOPEI are handled by hardware without trapping, so
nothing clears IRQ_VS_EXT in CSR_HVIP and the guest vCPU is wedged in a
spurious external interrupt storm.

[1] https://sashiko.dev/#/bug/linux-0a58883c-0da3-49e7-847e-a71b508f0638

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114135056.202556-1-anup.patel@oss.qualcomm.com?part=1

      reply	other threads:[~2026-11-14 14:08 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 13:50 [PATCH] RISC-V: KVM: Convert IMSIC vsfile_lock to a raw spinlock Anup Patel
2026-11-14 14:08 ` 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-214611@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=anup.patel@oss.qualcomm.com \
    --cc=kvm@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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