Re: [PATCH v4 30/48] KVM: arm64: gic-v5: Mask per-vcpu PPI state in vgic_v5_finalize_ppi_state()

Sascha Bischoff <[email protected]> Fri, 31 Jul 2026 14:21:08 +0000
Newsgroups dev.linux.lists.sashiko-reviews,dev.linux.lists.kvmarm,org.kernel.vger.kvm
Message-ID <[email protected]>
On Fri, 2026-07-24 at 11:52 +0000, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider:
> 
> New issues:
> - [High] Restored active software-managed PPIs are not queued to the
> ap_list, leading to lost interrupts on VM restore.
> 
> Pre-existing issues:
> - [High] Data race on the global `vgic_ppi_mask` when `SW_PPI` is not
> implemented in hardware, leading to guest state corruption.
> --
> 
> --- Patch [30]: [PATCH v4 30/48] KVM: arm64: gic-v5: Mask per-vcpu
> PPI state in vgic_v5_finalize_ppi_state() ---
> commit 04a9407ed3605cc393d7dce45456f17c9e1fc2c3
> Author: Sascha Bischoff <[email protected]>
> 
> KVM: arm64: gic-v5: Mask per-vcpu PPI state in
> vgic_v5_finalize_ppi_state()
> 
> This commit reworks vgic_v5_finalize_ppi_state() to discover exposed
> PPIs and
> mask non-exposed PPI state when VCPUs are first run. It clears state
> for
> non-exposed PPIs to ensure only the intended state is exposed to the
> guest
> after userspace restores the state.
> 
> > diff --git a/arch/arm64/kvm/vgic/vgic-v5.c
> > b/arch/arm64/kvm/vgic/vgic-v5.c
> > index 04392b686c2e2..6af1264ed2371 100644
> > --- a/arch/arm64/kvm/vgic/vgic-v5.c
> > +++ b/arch/arm64/kvm/vgic/vgic-v5.c
> [ ... ]
> > @@ -795,35 +796,65 @@ int vgic_v5_finalize_ppi_state(struct kvm
> > *kvm)
> >  	guard(mutex)(&kvm->arch.config_lock);
> >  
> >  	/*
> > -	 * If SW_PPI has been advertised, then we know we already
> > -	 * initialised the whole thing, and we can return early.
> > Yes,
> > -	 * this is pretty hackish as far as state tracking goes...
> > +	 * Discover the set of PPIs that are exposed to the guest
> > once per VM.
> > +	 * Once known, apply that mask to each VCPU's restored PPI
> > state as the
> > +	 * VCPUs are first run.
> >  	 */
> > -	if (test_bit(GICV5_ARCH_PPI_SW_PPI, kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask))
> > -		return 0;
> > -
> > -	/* The PPI state for all VCPUs should be the same. Pick
> > the first. */
> > -	vcpu0 = kvm_get_vcpu(kvm, 0);
> > +	if (!test_bit(GICV5_ARCH_PPI_SW_PPI, kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask)) {
> > +		bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_mask,
> > +			    VGIC_V5_NR_PRIVATE_IRQS);
> > +		bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr,
> > +			    VGIC_V5_NR_PRIVATE_IRQS);
> 
> [Severity: High]
> This is a pre-existing issue, but does this conditional in
> vgic_v5_finalize_ppi_state() create a data race on the global
> vgic_ppi_mask
> if the SW_PPI feature is not implemented in hardware?

From the A.a GICv5 spec:

R_MSWFB
When FEAT_GCIE is implemented, PPI 3 (SW_PPI) is implemented.

So, if we have GICv5, we have the SW_PPI. No question.

> 
> In a multi-VCPU VM booting on GICv5 hardware without the optional
> SW_PPI
> feature, when a subsequent VCPU runs for the first time, this test
> will
> evaluate to false because the bit was never set.
> 
> This seems to cause the global mask to be cleared via bitmap_zero()
> while
> earlier VCPUs might already be running in the guest. Could this lead
> to a
> transiently zeroed vgic_ppi_mask during sysreg accesses and silently
> dropped
> interrupts for emulated devices like timers?
> 
> > +
> > +		for_each_set_bit(i, ppi_caps.impl_ppi_mask,
> > VGIC_V5_NR_PRIVATE_IRQS) {
> [ ... ]
> > +	/* Also update the vgic_irqs */
> > +	for (i = 0; i < VGIC_V5_NR_PRIVATE_IRQS; i++) {
> > +		bool visible = test_bit(i, kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask);
> >  		const u32 intid = vgic_v5_make_ppi(i);
> >  		struct vgic_irq *irq;
> >  
> > -		irq = vgic_get_vcpu_irq(vcpu0, intid);
> > +		irq = vgic_get_vcpu_irq(vcpu, intid);
> >  
> > -		/* Expose PPIs with an owner or the SW_PPI, only
> > */
> >  		scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
> > {
> > -			if (irq->owner || i ==
> > GICV5_ARCH_PPI_SW_PPI) {
> > -				__set_bit(i, kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_mask);
> > -				__assign_bit(i, kvm-
> > >arch.vgic.gicv5_vm.vgic_ppi_hmr,
> > -					     irq->config ==
> > VGIC_CONFIG_LEVEL);
> > +			if (!visible) {
> > +				irq->enabled = false;
> > +				irq->active = false;
> > +				irq->pending_latch = false;
> > +				irq->line_level = false;
> > +			} else {
> > +				irq->enabled = test_bit(i, cpu_if-
> > >vgic_ppi_enabler);
> > +				irq->active = test_bit(i, cpu_if-
> > >vgic_ppi_activer);
> >  			}
> >  		}
> 
> [Severity: High]
> Is it safe to directly set irq->active here without queueing the
> interrupt?
> 
> When restoring a VM with active software-managed PPIs (like the PMU
> or timers),
> we mutate irq->active inside the lock in
> vgic_v5_finalize_ppi_state(), but we
> don't seem to invoke the corresponding queueing machinery.
> 
> Without enqueuing the IRQ onto the VCPU's ap_list, won't this cause
> the
> interrupt to be invisible to the injection path and get lost on VM
> restore?

No, because we always consider all PPIs (that we intend to expose to
the guest). We don't have an AP list at all for PPIs on GICv5.

> 
> >  
> > -		vgic_put_irq(vcpu0->kvm, irq);
> > +		vgic_put_irq(kvm, irq);
> >  	}
> 

Thanks,
Sascha