Commit f73e5ed9bc for qemu.org

commit f73e5ed9bc4cfacf041323a6b40a85e6b6459b75
Author: Kevin Wolf <kwolf@redhat.com>
Date:   Fri Dec 5 15:57:18 2025 +0100

    pcie_sriov: Fix PCI_SRIOV_* accesses in pcie_sriov_pf_exit()

    PCI_SRIOV_* are offsets into the SR-IOV capability, not into the PCI
    config space. pcie_sriov_pf_exit() erroneously takes them as the latter,
    which makes it read PCI_HEADER_TYPE and PCI_BIST when it tries to read
    PCI_SRIOV_TOTAL_VF.

    In many cases we're lucky enough that the PCI config space will be 0
    there, so we just skip the whole for loop, but this isn't guaranteed.
    For example, setting the multifunction bit on the PF and then doing a
    'device_del' on it will get a larger number and cause a segfault.

    Fix this and access the real PCI_SRIOV_* fields in the capability.

    Cc: qemu-stable@nongnu.org
    Fixes: 19e55471d4e8 ('pcie_sriov: Allow user to create SR-IOV device')
    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
    Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
    Message-Id: <20251205145718.55136-1-kwolf@redhat.com>

diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
index 34e0875d21..c41ac95bee 100644
--- a/hw/pci/pcie_sriov.c
+++ b/hw/pci/pcie_sriov.c
@@ -195,14 +195,17 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,

 void pcie_sriov_pf_exit(PCIDevice *dev)
 {
+    uint8_t *cfg;
+
     if (dev->exp.sriov_cap == 0) {
         return;
     }
+    cfg = dev->config + dev->exp.sriov_cap;

     if (dev->exp.sriov_pf.vf_user_created) {
         uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID);
-        uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF);
-        uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID);
+        uint16_t total_vfs = pci_get_word(cfg + PCI_SRIOV_TOTAL_VF);
+        uint16_t vf_dev_id = pci_get_word(cfg + PCI_SRIOV_VF_DID);

         unregister_vfs(dev);

@@ -213,8 +216,6 @@ void pcie_sriov_pf_exit(PCIDevice *dev)
             pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id);
         }
     } else {
-        uint8_t *cfg = dev->config + dev->exp.sriov_cap;
-
         unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
     }
 }