Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sascha Bischoff" <Sascha.Bischoff@arm.com>
Cc: kvmarm@lists.linux.dev, "Marc Zyngier" <maz@kernel.org>,
	"Oliver Upton" <oupton@kernel.org>,
	kvm@vger.kernel.org
Subject: Re: [PATCH v5 10/49] KVM: arm64: gic-v5: Introduce guest IST alloc and management
Date: Fri, 07 Aug 2026 13:07:52 +0000	[thread overview]
Message-ID: <20260807130753.3E3A41F00A3D@smtp.kernel.org> (raw)
In-Reply-To: <20260807111159.429128-11-sascha.bischoff@arm.com>

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:

New issues:
- [Critical] Hardware Use-After-Free (UAF) due to missing IRS invalidation command during LPI IST teardown.
- [Critical] Integer truncation in `vgic_v5_alloc_linear_ist` causes ZERO_SIZE_PTR allocation and silent memory corruption.
- [High] Unconditional NULL pointer dereference of `vcpu0` during IST assignment.
- [High] Silent physical address truncation in L2 IST allocation due to unvalidated masking.
- [Medium] Permanent memory leak of VM resources due to early aborts in `vgic_v5_vmte_release`.

Pre-existing issues:
- [High] Missing DSB barrier after cache maintenance allows the hardware IRS to read stale VMTE data.
--

--- Patch [10]: [PATCH v5 10/49] KVM: arm64: gic-v5: Introduce guest IST alloc and management ---
commit 4b81b458fb30bd2b57a30edcbe23a8f8de22edbb
Author: Sascha Bischoff <Sascha.Bischoff@arm.com>
Date:   Fri Aug 7 11:17:41 2026 +0000

    KVM: arm64: gic-v5: Introduce guest IST alloc and management

GICv5 guests use Interrupt State Tables (ISTs) to track and manage the
interrupt state for SPIs and LPIs. This commit introduces the machinery
needed to allocate a linear SPI IST and a shadow LPI IST, and pass them
to the host's IRS via the VMTE.

> diff --git a/arch/arm64/kvm/vgic/vgic-v5-tables.c b/arch/arm64/kvm/vgic/vgic-v5-tables.c
> index 7252d48431a5a..f088006f39674 100644
> --- a/arch/arm64/kvm/vgic/vgic-v5-tables.c
> +++ b/arch/arm64/kvm/vgic/vgic-v5-tables.c
[ ... ]
> @@ -581,6 +608,22 @@ int vgic_v5_vmte_release(struct kvm *kvm)
>  	if (!vmi)
>  		goto no_vmi;
>  
> +	/* If we have an LPI IST, free it */
> +	if (vmi->h_lpi_ist) {
> +		ret = vgic_v5_lpi_ist_free(kvm);
> +		if (ret)
> +			return ret;

[Severity: Medium]
What happens to the remaining allocated resources if this returns early?

If vgic_v5_lpi_ist_free() returns an error, the function aborts immediately.
This bypasses the release of vped_base, vpet_base, and vmd_base, and skips
removing the entry from vm_info, permanently leaking these VM resources.

> +	}
> +	vmi->h_lpi_ist = NULL;
> +
> +	/* If we have an SPI IST, free it */
> +	if (vmi->h_spi_ist) {
> +		ret = vgic_v5_spi_ist_free(kvm);
> +		if (ret)
> +			return ret;

[Severity: Medium]
Similarly, could an error here leak the same resources?

> +	}
> +	vmi->h_spi_ist = NULL;
[ ... ]
> @@ -679,3 +722,511 @@ int vgic_v5_vmte_free_vpe(struct kvm_vcpu *vcpu)
>  
>  	return 0;
>  }
> +
> +/*
> + * Assign an already allocated IST to the VM by populating the fields in the
> + * corresponding VMTE. We re-use this code for both an SPI IST and LPI IST, even
> + * if the paths to reach it might be vastly different.
> + */
> +static int vgic_v5_vmte_assign_ist(struct kvm *kvm, phys_addr_t ist_base,
> +				   bool two_level, unsigned int id_bits,
> +				   unsigned int l2sz, unsigned int istsz,
> +				   bool spi_ist)
> +{
> +	struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
[ ... ]
> +	scoped_guard(raw_spinlock_irqsave, &vgic_v5_irs_lock) {
> +		/* Bail if already allocated */
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte));
> +		if (le64_to_cpu(READ_ONCE(vmte->val[section])) &
> +		    GICV5_VMTEL2E_IST_VALID)
> +			return -EINVAL;
> +
> +		WRITE_ONCE(vmte->val[section], cpu_to_le64(tmp));
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte));
> +	}
> +
> +	/* Finally, mark the entry as valid */
> +	cmd = spi_ist ? SPI_VIST_MAKE_VALID : LPI_VIST_MAKE_VALID;
> +	ret = irq_set_vcpu_affinity(vgic_v5_vpe_db(vcpu0), &cmd);

[Severity: High]
What happens if vcpu0 is NULL? 

Userspace can invoke KVM VGIC initialization ioctls before any VCPUs are
created (where kvm->online_vcpus is 0). In this state, kvm_get_vcpu() returns
NULL. When vgic_v5_vpe_db() attempts to dereference vcpu0 to get the doorbell
address, it would cause a host kernel panic.

[Severity: High]
This is a pre-existing issue, but does vgic_v5_clean_inval() require a barrier
to guarantee completion?

The vgic_v5_clean_inval() function calls dcache_clean_inval_poc() to flush
caches for a non-coherent IRS. However, it lacks a trailing dsb(sy) or dsb(ish)
barrier. Because of this, the memory-mapped write to the doorbell in
irq_set_vcpu_affinity() might execute before the cache flush completes,
allowing the IRS hardware to read stale VMTE data.

> +
> +	return ret;
> +}
[ ... ]
> +/*
> + * Allocate a Linear IST - always used for SPIs and potentially LPIs.
> + *
> + * The calculation for n has been taken from section 11.2.2 of the GICv5 EAC0
> + * spec.
> + *
> + * NOTE: istsz is the FIELD used by GICv5, not the actual size (or log2() of the
> + * size).
> + */
> +static int vgic_v5_alloc_linear_ist(struct kvm *kvm, bool spi_ist,
> +				    unsigned int id_bits, unsigned int istsz)
> +{
> +	const size_t n = max(5, id_bits + 1 + istsz);
> +	u32 vm_id = vgic_v5_vm_id(kvm);
> +	struct vgic_v5_vm_info *vmi;
> +	__le64 *ist;
> +	u32 l1sz;
> +
> +	vmi = xa_load(&vm_info, vm_id);
> +	if (!vmi)
> +		return -EINVAL;
> +
> +	/*
> +	 * Allocate the IST. We only have one level, so we just use the L2 ISTE.
> +	 */
> +	l1sz = BIT(n + 1);

[Severity: Critical]
Can l1sz overflow and truncate to zero?

If a guest requests a large LPI ID space (e.g., id_bits = 30), n can reach 32.
BIT(33) evaluates to 8,589,934,592, which truncates to exactly 0 when assigned
to the 32-bit u32 l1sz variable.

> +	ist = kzalloc(l1sz, GFP_KERNEL_ACCOUNT);

When kzalloc() is called with a size of 0, it returns ZERO_SIZE_PTR (0x10).
KVM will then translate this via virt_to_phys() and program the bogus physical
address into the hardware's VMTE. The IRS will perform DMA to this address,
leading to host memory corruption.

> +	if (!ist)
> +		return -ENOMEM;
[ ... ]
> +/*
> + * Allocate ALL of the second level ISTs for a two-level IST - LPI, only.
> + *
> + * The calculation for n has been taken from section 11.2.2 of the GICv5 EAC0
> + * spec. The l2_size calculation is from section 11.2.3 of the same document.
> + *
> + * NOTE: istsz and l2sz are the FIELDS used by GICv5, not the actual sizes (or
> + * log2() of the sizes).
> + */
> +static int vgic_v5_alloc_l2_ists(struct kvm *kvm, unsigned int id_bits,
> +				 unsigned int istsz, unsigned int l2sz)
> +{
[ ... ]
> +		/*
> +		 * We are not doing on-demand allocation of the L2 ISTs, and are
> +		 * instead provisioning the whole IST up front. This means that
> +		 * we are able to mark the L2 ISTs as valid in the L1 ISTEs as
> +		 * the overall IST is not yet valid.
> +		 */
> +		val = (virt_to_phys(l2ist) & GICV5_ISTL1E_L2_ADDR_MASK) |
> +		      GICV5_ISTL1E_VALID;

[Severity: High]
Is it possible for virt_to_phys(l2ist) to have bits above the
GICV5_ISTL1E_L2_ADDR_MASK?

Unlike vgic_v5_vmte_assign_ist() which checks if the address fits the hardware
mask, this function unconditionally applies a bitwise AND. On systems with
LVA/LPA (up to 52-bit PAs), if the allocated page resides above the bits
covered by the mask, the upper bits are silently truncated. The IRS would then
write incoming LPI states to an aliased, incorrect physical memory address.

> +		l1ist[index] = cpu_to_le64(val);
> +
> +		vmi->h_lpi_l2_ists[index] = l2ist;
> +
> +		vgic_v5_clean_inval(l2ist, l2_size);
> +	}
[ ... ]
> +/*
> + * Free a Linear IST. Can only happen once the VM is dead.
> + */
> +static int vgic_v5_linear_ist_free(struct kvm *kvm, bool spi)
> +{
> +	u32 vm_id = vgic_v5_vm_id(kvm);
> +	struct vmtl2_entry *vmte;
> +	struct vgic_v5_vm_info *vmi;
> +	int section;
[ ... ]
> +	if (spi) {
> +		section = GICV5_VMTEL2_SPI_SECTION;
> +		vgic_v5_free_allocated_spi_ist(kvm);
> +	} else {
> +		section = GICV5_VMTEL2_LPI_SECTION;
> +		vgic_v5_free_allocated_lpi_ist(vmi, 0, 0, 0);
> +	}
> +
> +	/* The VM should be dead here, so we can just zero the VMT section */
> +	scoped_guard(raw_spinlock_irqsave, &vgic_v5_irs_lock) {
> +		WRITE_ONCE(vmte->val[section], cpu_to_le64(0));
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte));
> +	}

[Severity: Critical]
Does the hardware IRS need to be notified that the LPI IST is now invalid?

The memory is explicitly freed and the software VMTE is zeroed, but no
LPI_VIST_MAKE_INVALID command is sent to the IRS hardware. Because the hardware
IRS is never notified, it will retain the physical address in its cache and
will continue to DMA incoming LPI state updates to the newly freed host memory
pages, leading to a Use-After-Free and host memory corruption.

> +
> +	return 0;
> +}
> +
> +/*
> + * Free a Two-Level IST. Can only happen once the VM is dead.
> + */
> +static int vgic_v5_two_level_ist_free(struct kvm *kvm, bool spi)
> +{
[ ... ]
> +	vgic_v5_free_allocated_lpi_ist(vmi, id_bits, istsz, l2sz);
> +
> +	/* The VM must be dead, so we can just zero the VMT section */
> +	scoped_guard(raw_spinlock_irqsave, &vgic_v5_irs_lock) {
> +		WRITE_ONCE(vmte->val[section], cpu_to_le64(0));
> +		vgic_v5_clean_inval(vmte, sizeof(*vmte));
> +	}

[Severity: Critical]
Similarly, does the two-level IST teardown also require an
LPI_VIST_MAKE_INVALID hardware command?

The LPI IST memory is freed and the VMTE is zeroed without sending the
invalidation command to the IRS, opening the same race window for hardware
Use-After-Free.

> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260807111159.429128-1-sascha.bischoff@arm.com?part=10

  reply	other threads:[~2026-08-07 13:07 UTC|newest]

Thread overview: 84+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-07 11:12 [PATCH v5 00/49] KVM: arm64: Add GICv5 IRS support Sascha Bischoff
2026-08-07 11:13 ` [PATCH v5 01/49] irqchip/gic-v5: Allow KVM setup without a maintenance IRQ Sascha Bischoff
2026-08-07 11:13 ` [PATCH v5 02/49] irqchip/gic-v5: Provide OF IRS config frame attrs to KVM Sascha Bischoff
2026-08-07 11:53   ` sashiko-bot
2026-08-07 11:14 ` [PATCH v5 03/49] irqchip/gic-v5: Set up gic_kvm_info on ACPI hosts Sascha Bischoff
2026-08-07 12:01   ` sashiko-bot
2026-08-07 13:44   ` Lorenzo Pieralisi
2026-08-07 11:14 ` [PATCH v5 04/49] KVM: arm64: gic-v5: Define remaining IRS MMIO registers Sascha Bischoff
2026-08-07 12:05   ` sashiko-bot
2026-08-07 11:15 ` [PATCH v5 05/49] arm64/sysreg: Add GICv5 GIC VDPEND encoding Sascha Bischoff
2026-08-07 11:15 ` [PATCH v5 06/49] arm64/sysreg: Update ICC_CR0_EL1 with LINK and LINK_IDLE fields Sascha Bischoff
2026-08-07 12:17   ` sashiko-bot
2026-08-07 11:16 ` [PATCH v5 07/49] KVM: arm64: gic-v5: Cache host IRS ID registers Sascha Bischoff
2026-08-07 12:27   ` sashiko-bot
2026-08-07 11:16 ` [PATCH v5 08/49] KVM: arm64: gic-v5: Add VPE doorbell domain Sascha Bischoff
2026-08-07 12:45   ` sashiko-bot
2026-08-07 11:17 ` [PATCH v5 09/49] KVM: arm64: gic-v5: Create and manage VM and VPE tables Sascha Bischoff
2026-08-07 12:50   ` sashiko-bot
2026-08-07 11:17 ` [PATCH v5 10/49] KVM: arm64: gic-v5: Introduce guest IST alloc and management Sascha Bischoff
2026-08-07 13:07   ` sashiko-bot [this message]
2026-08-07 11:18 ` [PATCH v5 11/49] KVM: arm64: gic-v5: Implement VMT/vIST IRS MMIO Ops Sascha Bischoff
2026-08-07 13:13   ` sashiko-bot
2026-08-07 11:18 ` [PATCH v5 12/49] KVM: arm64: gic-v5: Keep GICv5 vCPU limit model-specific Sascha Bischoff
2026-08-07 13:30   ` sashiko-bot
2026-08-07 11:19 ` [PATCH v5 13/49] KVM: arm64: gic-v5: Implement VPE IRS MMIO Ops Sascha Bischoff
2026-08-07 11:19 ` [PATCH v5 14/49] KVM: arm64: gic-v5: Set up VMTEs and VPE doorbells Sascha Bischoff
2026-08-07 13:42   ` sashiko-bot
2026-08-07 11:20 ` [PATCH v5 15/49] KVM: arm64: gic-v5: Add resident/non-resident hyp calls Sascha Bischoff
2026-08-07 11:20 ` [PATCH v5 16/49] KVM: arm64: gic-v5: Request doorbells when VPEs enter WFI Sascha Bischoff
2026-08-07 14:17   ` sashiko-bot
2026-08-07 11:21 ` [PATCH v5 17/49] KVM: arm64: gic-v5: Introduce struct vgic_v5_irs and IRS base address Sascha Bischoff
2026-08-07 11:21 ` [PATCH v5 18/49] KVM: arm64: gic-v5: Add IRS IODEV support to MMIO handlers Sascha Bischoff
2026-08-07 11:22 ` [PATCH v5 19/49] KVM: arm64: gic-v5: Add KVM_VGIC_V5_ADDR_TYPE_IRS to UAPI Sascha Bischoff
2026-08-07 14:27   ` sashiko-bot
2026-08-07 11:22 ` [PATCH v5 20/49] KVM: arm64: gic-v5: Add GICv5 IRS IODEV and MMIO emulation Sascha Bischoff
2026-08-07 14:34   ` sashiko-bot
2026-08-07 11:23 ` [PATCH v5 21/49] KVM: arm64: gic-v5: Initialise per-VM IRS state Sascha Bischoff
2026-08-07 14:49   ` sashiko-bot
2026-08-07 11:23 ` [PATCH v5 22/49] KVM: arm64: gic-v5: Register the IRS IODEV Sascha Bischoff
2026-08-07 14:52   ` sashiko-bot
2026-08-07 11:24 ` [PATCH v5 23/49] KVM: arm64: gic-v5: Set IRICHPPIDIS based on IRS enable state Sascha Bischoff
2026-08-07 11:24 ` [PATCH v5 24/49] KVM: arm64: selftests: Update vGICv5 selftest to set IRS address Sascha Bischoff
2026-08-07 15:04   ` sashiko-bot
2026-08-07 11:25 ` [PATCH v5 25/49] KVM: arm64: gic-v5: Add GIC VDPEND hyp call Sascha Bischoff
2026-08-07 11:25 ` [PATCH v5 26/49] KVM: arm64: gic: Introduce set_pending_state() to irq_ops Sascha Bischoff
2026-08-07 15:14   ` sashiko-bot
2026-08-07 11:26 ` [PATCH v5 27/49] KVM: arm64: gic-v5: Support SPI injection Sascha Bischoff
2026-08-07 15:23   ` sashiko-bot
2026-08-07 11:26 ` [PATCH v5 28/49] Documentation: KVM: Extend VGICv5 device attribute docs Sascha Bischoff
2026-08-07 15:29   ` sashiko-bot
2026-08-07 11:27 ` [PATCH v5 29/49] KVM: arm64: gic-v5: Add GICv5 SPI injection to irqfd Sascha Bischoff
2026-08-07 15:40   ` sashiko-bot
2026-08-07 11:27 ` [PATCH v5 30/49] KVM: arm64: gic-v5: Mask per-vCPU PPI state in vgic_v5_finalize_ppi_state() Sascha Bischoff
2026-08-07 11:28 ` [PATCH v5 31/49] KVM: arm64: gic-v5: Add GICv5 EL1 sysreg userspace accessors Sascha Bischoff
2026-08-07 16:27   ` sashiko-bot
2026-08-07 11:28 ` [PATCH v5 32/49] KVM: arm64: gic-v5: Handle userspace accesses to IRS MMIO region Sascha Bischoff
2026-08-07 16:20   ` sashiko-bot
2026-08-07 11:29 ` [PATCH v5 33/49] KVM: arm64: gic-v5: Add CoreSight MMIO regs to IRS Sascha Bischoff
2026-08-07 11:29 ` [PATCH v5 34/49] KVM: arm64: gic-v5: Add VGICv5 IST save/restore UAPI Sascha Bischoff
2026-08-07 16:30   ` sashiko-bot
2026-08-07 11:30 ` [PATCH v5 35/49] KVM: arm64: gic-v5: Implement save/restore mechanisms for ISTs Sascha Bischoff
2026-08-07 16:48   ` sashiko-bot
2026-08-07 11:30 ` [PATCH v5 36/49] Documentation: KVM: Document KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS for VGICv5 Sascha Bischoff
2026-08-07 16:55   ` sashiko-bot
2026-08-07 11:31 ` [PATCH v5 37/49] Documentation: KVM: Add KVM_DEV_ARM_VGIC_GRP_IRS_REGS to VGICv5 docs Sascha Bischoff
2026-08-07 16:52   ` sashiko-bot
2026-08-07 11:31 ` [PATCH v5 38/49] Documentation: KVM: Add docs for KVM_DEV_ARM_VGIC_GRP_IST Sascha Bischoff
2026-08-07 11:32 ` [PATCH v5 39/49] Documentation: KVM: Add the VGICv5 IRS save/restore sequences Sascha Bischoff
2026-08-07 11:32 ` [PATCH v5 40/49] KVM: selftests: Add VGICv5 IRS address attribute tests Sascha Bischoff
2026-08-07 11:33 ` [PATCH v5 41/49] KVM: selftests: Add VGICv5 NR_IRQS " Sascha Bischoff
2026-08-07 17:12   ` sashiko-bot
2026-08-07 11:33 ` [PATCH v5 42/49] KVM: selftests: Add VGICv5 IRS_REGS " Sascha Bischoff
2026-08-07 17:17   ` sashiko-bot
2026-08-07 11:34 ` [PATCH v5 43/49] KVM: selftests: Add VGICv5 IST " Sascha Bischoff
2026-08-07 17:21   ` sashiko-bot
2026-08-07 11:35 ` [PATCH v5 44/49] KVM: selftests: Add VGICv5 USERSPACE_PPIS tests Sascha Bischoff
2026-08-07 11:35 ` [PATCH v5 45/49] KVM: selftests: Add VGICv5 CPU sysreg attribute tests Sascha Bischoff
2026-08-07 11:36 ` [PATCH v5 46/49] KVM: selftests: Add VGICv5 SPI injection tests Sascha Bischoff
2026-08-07 11:36 ` [PATCH v5 47/49] KVM: selftests: Add VGICv5 LPI delivery tests Sascha Bischoff
2026-08-07 17:39   ` sashiko-bot
2026-08-07 11:37 ` [PATCH v5 48/49] KVM: selftests: Add VGICv5 IST save/restore coverage Sascha Bischoff
2026-08-07 17:50   ` sashiko-bot
2026-08-07 11:37 ` [PATCH v5 49/49] KVM: selftests: Add VGICv5 sparse vCPU IDs test Sascha Bischoff
2026-08-07 17:56   ` sashiko-bot

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=20260807130753.3E3A41F00A3D@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Sascha.Bischoff@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=maz@kernel.org \
    --cc=oupton@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