Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jose Ignacio Tornos Martinez" <jtornosm@redhat.com>
Cc: linux-pci@vger.kernel.org, kvm@vger.kernel.org
Subject: Re: [PATCH v3 2/8] KVM: add PCI MSI hypercall for VFIO passthrough
Date: Thu, 29 Oct 2026 16:59:02 +0000	[thread overview]
Message-ID: <sashiko-outbox-189700@kernel.org> (raw)
In-Reply-To: <20261029163713.1366077-3-jtornosm@redhat.com>

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [Critical] kvm: vfio: use-after-free on struct kvm_vfio and race on pci_msi_alloc
- [Critical] kvm: vfio: use-after-free on vfio_device, msi_desc, and irq_data
- [High] kvm: deadlock acquiring kvm->lock in kvm_vfio_pci_msi_alloc()
- [High] kvm: vfio: guest-triggerable WARN_ON_ONCE via unvalidated MSI vector
- [High] kvm: vfio: broken Multi-MSI message lookup in kvm_vfio_pci_msi_alloc()
- [High] kvm: vfio: ambiguous and unisolated device lookup in kvm_vfio_pci_msi_alloc()
- [Medium] kvm: vfio: link failure calling vfio_file_find_pci_device() directly
- [Medium] kvm: x86: KVM_HC_PCI_MSI_ALLOC lacks feature enumeration and opt-in
- [Low] kvm: vfio: build failure accessing irq_data.parent_data directly
--

commit dc68213ab54a0729231a44c10317169580ab3c78
Author: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>

KVM: add PCI MSI hypercall for VFIO passthrough

This commit introduces a new x86 hypercall, KVM_HC_PCI_MSI_ALLOC, to allow a
guest to query the host physical MSI address and data for a VFIO passthrough
PCI device. It connects arch/x86/kvm/x86.c to a new kvm_vfio_pci_msi_alloc()
helper in virt/kvm/vfio.c via a pci_msi_alloc callback on struct kvm.

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 32563a91fba99..2f0cae07f9786 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
[ ... ]
> @@ -7490,6 +7491,44 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl,
>  		vcpu->arch.complete_userspace_io = complete_hypercall;
>  		return 0;
>  	}
> +	case KVM_HC_PCI_MSI_ALLOC: {
> +		struct msi_msg msg = {};
> +		struct {
> +			u32 address_lo;
> +			u32 address_hi;
> +			u32 data;
> +			u32 reserved;
> +		} resp;
> +
> +		if (vcpu->kvm->pci_msi_alloc == NULL) {
> +			ret = -KVM_ENOSYS;
> +			break;
> +		}

[Severity: Medium]
Should KVM_HC_PCI_MSI_ALLOC be gated behind a paravirtual CPUID feature bit
checked via guest_pv_has() or an explicit userspace capability opt-in?

In ____kvm_emulate_hypercall() in arch/x86/kvm/x86.c, other KVM hypercalls
such as KVM_HC_KICK_CPU, KVM_HC_SEND_IPI, and KVM_HC_SCHED_YIELD check
guest_pv_has(vcpu, KVM_FEATURE_*), and KVM_HC_MAP_GPA_RANGE checks
user_exit_on_hypercall(). Here, kvm_vfio_create() unconditionally sets
dev->kvm->pci_msi_alloc = kvm_vfio_pci_msi_alloc whenever a kvm-vfio device
is created, making this hypercall and the host physical MSI routing
information available to any guest by default without enumeration or
userspace opt-in.

Also, since the guest memory layout of resp is part of the guest-host ABI,
should that structure be defined in include/uapi/linux/kvm_para.h rather than
as an anonymous local struct in ____kvm_emulate_hypercall(), and should
64-bit hypercall arguments a0 and a1 be validated for upper-bit truncation
before being passed as u32 and unsigned int?

> +
> +		/*
> +		 * a0 = PCI vendor:device (vendor << 16 | device)
> +		 * a1 = MSI vector index
> +		 * a2 = GPA of response buffer
> +		 */
> +		ret = vcpu->kvm->pci_msi_alloc(vcpu->kvm, a0, a1, &msg);
[ ... ]
> diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
> index 19548a4309426..60356173e69f3 100644
> --- a/virt/kvm/vfio.c
> +++ b/virt/kvm/vfio.c
[ ... ]
> @@ -315,6 +318,8 @@ static void kvm_vfio_release(struct kvm_device *dev)
>  
>  	kvm_vfio_update_coherency(dev);
>  
> +	dev->kvm->pci_msi_alloc = NULL;
> +
>  	kfree(kv);
>  	kfree(dev); /* alloc by kvm_ioctl_create_device, free by .release */
>  }
> @@ -349,6 +354,98 @@ static int kvm_vfio_create(struct kvm_device *dev, u32 type)
>  	mutex_init(&kv->lock);
>  
>  	dev->private = kv;
> +	dev->kvm->pci_msi_alloc = kvm_vfio_pci_msi_alloc;
> +
> +	return 0;
> +}
> +
> +/**
> + * kvm_vfio_pci_msi_alloc - Get physical MSI address/data for a VFIO device
> + * @kvm: the VM instance
> + * @devid: PCI vendor:device packed as (vendor << 16 | device)
> + * @vector: MSI vector index
> + * @msg: output MSI message (address + data)
> + *
> + * Walks VFIO files attached to this VM, uses vfio_file_find_pci_device()
> + * to find the PCI device matching the given vendor:device ID, and returns
> + * the host's physical MSI address/data for the requested vector.
> + *
> + * Returns 0 on success, negative errno on failure.
> + */
> +int kvm_vfio_pci_msi_alloc(struct kvm *kvm, u32 devid,
> +			     unsigned int vector, struct msi_msg *msg)
> +{
> +	struct kvm_vfio_file *kvf;
> +	struct kvm_vfio *kv = NULL;
> +	struct vfio_device *vdev;
> +	struct kvm_device *dev;
> +	struct msi_desc *desc;
> +	struct irq_data *irqd;
> +	struct pci_dev *pdev;
> +	u16 vendor = devid >> 16;
> +	u16 device = devid & 0xffff;
> +	int irq;
> +
> +	scoped_guard(mutex, &kvm->lock) {
> +		list_for_each_entry(dev, &kvm->devices, vm_node) {
> +			if (dev->ops == &kvm_vfio_ops) {
> +				kv = dev->private;
> +				break;
> +			}
> +		}
> +	}

[Severity: High]
Can acquiring kvm->lock here in kvm_vfio_pci_msi_alloc() cause a deadlock?

When a guest invokes KVM_HC_PCI_MSI_ALLOC, the call chain is:

  kvm_vcpu_ioctl() [holds vcpu->mutex]
    -> kvm_arch_vcpu_ioctl_run()
      -> vcpu_enter_guest() [holds kvm->srcu via kvm_vcpu_srcu_read_lock()]
        -> handle_exit()
          -> kvm_emulate_hypercall()
            -> ____kvm_emulate_hypercall()
              -> kvm_vfio_pci_msi_alloc()
                -> mutex_lock(&kvm->lock)

According to Documentation/virt/kvm/locking.rst, kvm->lock must be acquired
outside vcpu->mutex (for example, kvm_lock_all_vcpus() and
kvm_vm_ioctl_create_vcpu() in virt/kvm/kvm_main.c take kvm->lock and then
vcpu->mutex). In addition, synchronize_srcu(&kvm->srcu) is called while
holding kvm->lock or vcpu->mutex.

Doesn't taking kvm->lock while holding both vcpu->mutex and the kvm->srcu
read lock introduce an ABBA lock inversion and an SRCU deadlock?

> +
> +	if (!kv)
> +		return -ENODEV;
> +
> +	guard(mutex)(&kv->lock);

[Severity: Critical]
What prevents struct kvm_vfio (kv) and its kv->file_list entries from being
freed before or during kvm_vfio_pci_msi_alloc()?

In kvm_vfio_pci_msi_alloc(), kvm->lock is released at the end of the
scoped_guard block without pinning dev or kv. If userspace closes the
kvm-vfio device fd concurrently:

  kvm_device_release()
    -> mutex_lock(&kvm->lock)
    -> list_del(&dev->vm_node)
    -> mutex_unlock(&kvm->lock)
    -> synchronize_rcu()
    -> kvm_vfio_release(dev)
      -> list_for_each_entry_safe(kvf, tmp, &kv->file_list, node)
           kvm_vfio_file_free(dev, kvf);
      -> dev->kvm->pci_msi_alloc = NULL;
      -> kfree(kv);

Because synchronize_rcu() does not wait for preemptible vCPU threads in
____kvm_emulate_hypercall(), and kvm_vfio_release() frees kv->file_list
without taking kv->lock and then frees kv, can kvm_vfio_pci_msi_alloc()
access freed memory when locking kv->lock or iterating kv->file_list?

Similarly, in ____kvm_emulate_hypercall():

	if (vcpu->kvm->pci_msi_alloc == NULL) {
		ret = -KVM_ENOSYS;
		break;
	}
	ret = vcpu->kvm->pci_msi_alloc(vcpu->kvm, a0, a1, &msg);

could kvm_vfio_release() set dev->kvm->pci_msi_alloc = NULL between the NULL
check and the indirect call, resulting in a NULL function pointer call?

> +
> +	vdev = NULL;
> +	list_for_each_entry(kvf, &kv->file_list, node) {
> +		vdev = vfio_file_find_pci_device(kvf->file, vendor, device);
> +		if (vdev)
> +			break;
> +	}

[Severity: Medium]
Will calling vfio_file_find_pci_device() directly break the build when
CONFIG_KVM=y and CONFIG_VFIO=m (with CONFIG_VFIO_GROUP=y)?

In virt/kvm/vfio.c, all other VFIO helpers (such as vfio_file_set_kvm and
vfio_file_enforced_coherent) are looked up dynamically via symbol_get() and
symbol_put() so that built-in KVM does not have a link-time dependency on
vfio.ko. Calling vfio_file_find_pci_device() directly from virt/kvm/vfio.c
causes an undefined symbol reference at link time when KVM is built-in and
VFIO is a module.

Also, in include/linux/vfio.h, vfio_file_find_pci_device() is guarded by
#if IS_ENABLED(CONFIG_VFIO_GROUP). When CONFIG_VFIO_DEVICE_CDEV=y and
CONFIG_VFIO_GROUP=n, vfio_file_find_pci_device() compiles to the inline stub
returning NULL, causing kvm_vfio_pci_msi_alloc() to always return -ENODEV on
cdev-only builds.

[Severity: High]
Is matching solely by 16-bit vendor and device ID sufficient when looking up
the VFIO device in kvm_vfio_pci_msi_alloc()?

If a VM has two passthrough PCI devices with the same vendor:device ID, this
loop always stops at the first device in kv->file_list and returns its MSI
message for both devices, which will trigger IOMMU source-ID validation
faults on the second device.

In addition, when kvf->file is a VFIO group fd, vfio_file_find_pci_device()
walks group->device_list and matches any PCI device in the host IOMMU group
without checking whether that device is opened or assigned to this VM. Can a
guest use this hypercall to read the host physical MSI address and data of
unassigned sibling PCI devices in the same IOMMU group?

> +
> +	if (!vdev)
> +		return -ENODEV;
> +
> +	pdev = to_pci_dev(vdev->dev);
> +
> +	if (!pdev->msi_enabled && !pdev->msix_enabled) {
> +		memset(msg, 0, sizeof(*msg));
> +		return 0;
> +	}
> +
> +	irq = pci_irq_vector(pdev, vector);

[Severity: High]
Can a guest trigger a host WARN_ON_ONCE in msi_domain_get_virq() by passing
a vector index greater than MSI_MAX_INDEX (0xffff)?

In ____kvm_emulate_hypercall(), the guest-controlled register a1 is passed
directly as vector to kvm_vfio_pci_msi_alloc(), which passes it unvalidated
to pci_irq_vector(pdev, vector) -> msi_get_virq() -> msi_domain_get_virq():

kernel/irq/msi.c:msi_domain_get_virq() {
	if (WARN_ON_ONCE(index > MSI_MAX_INDEX || domid >= MSI_MAX_DEVICE_IRQDOMAINS))
		return 0;
}

Should vector be bounds-checked before calling pci_irq_vector()?

> +	if (irq <= 0) {
> +		memset(msg, 0, sizeof(*msg));
> +		return 0;
> +	}
> +
> +	irqd = irq_get_irq_data(irq);
> +	if (!irqd) {
> +		memset(msg, 0, sizeof(*msg));
> +		return 0;
> +	}
> +
> +	desc = irq_get_msi_desc(irq);
> +	if (desc) {
> +		*msg = desc->msg;

[Severity: Critical]
What protects vdev, pdev, irqd, and desc from concurrent teardown while
kvm_vfio_pci_msi_alloc() accesses them?

First, for a VFIO group file, vfio_file_find_pci_device() drops
group->device_lock before returning vdev without acquiring a reference on
vdev. Holding the group file in kv->file_list does not prevent an individual
vfio_device in group->device_list from being unregistered via
vfio_unregister_group_dev(), so vdev and pdev can be freed while
kvm_vfio_pci_msi_alloc() dereferences them.

Second, pci_irq_vector() only holds msi_descs_lock(&pdev->dev) internally
while looking up the Linux IRQ number. Once it returns irq,
kvm_vfio_pci_msi_alloc() calls irq_get_irq_data(irq), irq_get_msi_desc(irq),
and reads desc->msg (or walks irqd->parent_data) without holding
msi_descs_lock(&pdev->dev), vdev->igate, or the IRQ descriptor lock. If host
userspace concurrently reconfigures or disables MSI/MSI-X via
VFIO_DEVICE_SET_IRQS -> vfio_msi_disable() -> pci_free_irq_vectors(), can
both the irq_desc and struct msi_desc be freed while kvm_vfio_pci_msi_alloc()
is dereferencing desc or irqd?

[Severity: High]
Does reading desc->msg return the wrong MSI message for Multi-MSI vectors
where vector > 0?

For PCI Multi-MSI, __msi_domain_alloc_irqs() in kernel/irq/msi.c attaches the
same shared struct msi_desc to all allocated Linux IRQs in the Multi-MSI
group:

	for (i = 0; i < desc->nvec_used; i++) {
		irq_set_msi_desc_off(virq, i, desc);
		...
	}

And pci_msi_domain_write_msg() in drivers/pci/msi/irqdomain.c only caches
desc->msg for the first vector:

	if (desc->irq == irq_data->irq)
		__pci_write_msi_msg(desc, msg);

Because irq_get_msi_desc(irq) is non-NULL for every MSI/MSI-X vector, the
if (desc) branch is always taken (making the else branch unreachable dead
code), and any Multi-MSI request with vector > 0 receives vector 0's cached
desc->msg instead of the composed message for that vector. Should this use
irq_chip_compose_msi_msg() or adjust the message for Multi-MSI vectors?

> +	} else {
> +		struct irq_data *pos;
> +
> +		for (pos = irqd; pos; pos = pos->parent_data) {
> +			if (pos->chip && pos->chip->irq_compose_msi_msg) {
> +				pos->chip->irq_compose_msi_msg(pos, msg);
> +				break;
> +			}
> +		}

[Severity: Low]
Can accessing pos->parent_data directly fail to compile when
CONFIG_IRQ_DOMAIN_HIERARCHY is disabled?

In include/linux/irq.h, struct irq_data only defines parent_data under
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY:

struct irq_data {
	...
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
	struct irq_data		*parent_data;
#endif
	void			*chip_data;
};

On architectures that select CONFIG_KVM_VFIO without unconditionally
selecting CONFIG_IRQ_DOMAIN_HIERARCHY (such as s390 or powerpc configs
without CONFIG_PCI_MSI), referencing pos->parent_data directly in
virt/kvm/vfio.c will break the build unless irq_chip_compose_msi_msg() or a
config guard is used.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029163713.1366077-1-jtornosm@redhat.com?part=2

  parent reply	other threads:[~2026-10-29 16:59 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 16:37 [PATCH v3 0/8] PCI/KVM/VFIO: MSI address passthrough for Qualcomm WiFi devices Jose Ignacio Tornos Martinez
2026-10-29 16:37 ` [PATCH v3 1/8] vfio: add helper to find PCI device by vendor/device ID Jose Ignacio Tornos Martinez
2026-10-29 16:53   ` sashiko-bot
2026-10-29 16:37 ` [PATCH v3 2/8] KVM: add PCI MSI hypercall for VFIO passthrough Jose Ignacio Tornos Martinez
2026-10-29 16:58   ` Jason Gunthorpe
2026-10-29 16:59   ` sashiko-bot [this message]
2026-10-29 16:37 ` [PATCH v3 3/8] PCI: add KVM MSI domain guest module Jose Ignacio Tornos Martinez
2026-10-29 17:06   ` sashiko-bot
2026-10-29 16:37 ` [PATCH v3 4/8] ath11k: add PCIe link recovery retry Jose Ignacio Tornos Martinez
2026-10-29 16:45   ` sashiko-bot
2026-10-29 16:37 ` [PATCH v3 5/8] ath11k: use KVM MSI domain for VM passthrough Jose Ignacio Tornos Martinez
2026-10-29 16:54   ` sashiko-bot
2026-10-29 17:00   ` Jason Gunthorpe
2026-10-29 16:37 ` [PATCH v3 6/8] ath12k: add PCIe link recovery retry Jose Ignacio Tornos Martinez
2026-10-29 17:00   ` sashiko-bot
2026-10-29 16:37 ` [PATCH v3 7/8] ath12k: use KVM MSI domain for VM passthrough Jose Ignacio Tornos Martinez
2026-10-29 17:04   ` sashiko-bot
2026-10-29 16:37 ` [PATCH v3 8/8] PCI: add QCN9274 to reset quirks and KVM MSI domain Jose Ignacio Tornos Martinez
2026-10-29 17:02   ` 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=sashiko-outbox-189700@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=jtornosm@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-pci@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