Memory safety / Use-after-free (stale PCI device reference in VF handling)

HIGH
torvalds/linux
Commit: 5c0e3ba4f500
Affected: <= v7.0-rc6 (CN23XX LiquidIO SR-IOV VF handling prior to this patch)
2026-07-17 16:44 UTC

Description

The commit fixes a memory-safety vulnerability in the LiquidIO CN23XX driver where a cached pointer to a VF PCI device (dpiring_to_vfpcidev_lut) could be dereferenced after the VF device was removed or its reference dropped. The previous code cached VF PCI device pointers without proper reference management and later dereferenced them during OCC/FLR handling (via OCTEON_VF_ACTIVE path). The patch removes the cache and replaces it with a runtime lookup that derives the VF from the DPI ring, validates it against the PF, and performs proper reference handling (pcie_flr then pci_dev_put). This reduces the risk of use-after-free or invalid dereferences when handling VF FLR requests.

Proof of Concept

Note: This PoC outlines a prior-to-fix exploit path. It is intended for assessment purposes and not for production use. It requires a system with a Cavium CN23XX LiquidIO device, SR-IOV enabled, and at least one VF. The vulnerability would be exploitable when the PF caches VF PCI device pointers and later dereferences them during a mailbox-driven FLR request (OCTEON_VF_ACTIVE). The patch removes the cache and introduces a runtime lookup with reference management, mitigating the issue. Attack scenario before the fix (illustrative, not to be executed on live systems): 1) Setup a CN23XX LiquidIO device with SR-IOV and create multiple VFs. 2) Ensure the PF enables SR-IOV and that the driver builds the dpiring_to_vfpcidev_lut cache by iterating pci_get_device() without taking a persistent reference to each VF PCI device (as done pre-fix). 3) Unbind/delete a VF device or cause the VF PCI device to be hot-unplugged while the VF mapping cache is still in use by the PF. 4) Trigger an OCTEON_VF_ACTIVE/FLR flow via the mailbox path (for example, by sending a VF FLR request on the DPI ring using the VF’s q_no). 5) The code path would dereference the cached VF PCI device pointer (without an owning reference), potentially use-after-free or use-after-mutex scenarios, leading to a crash or memory corruption. Representative, high-level steps (conceptual): - Identify q_no corresponding to a VF’s DPI ring (q_no = VF index * rings_per_vf + ring_no). - Have the PF enqueue an OCTEON_VF_ACTIVE mailbox command for that q_no. - The handler would previously obtain vfdev = lut[q_no] and call pcie_flr(vfdev) and later pci_dev_put(vfdev) on that cached pointer. - If the VF PCI device had been removed/freed, or if pci_dev_put() hadn’t been balanced, this could result in dereferencing a freed object. PoC outcome after the fix (expected): The runtime VF lookup derives the VF PCIDevice from the PF, validates against PF and VF ID, and uses pcie_flr on a live, authenticated VF device, immediately dropping the reference with pci_dev_put(). This eliminates the stale-pointer dereference path.

Commit Details

Author: Yuho Choi

Date: 2026-07-01 04:08 UTC

Message:

net/liquidio: drop cached VF pci_dev LUT The PF SR-IOV enable path caches VF pci_dev pointers in dpiring_to_vfpcidev_lut[] by iterating with pci_get_device(). Those entries do not own a reference, because the iterator drops the previous device reference on each step. The cached pointer is then dereferenced later when handling OCTEON_VF_FLR_REQUEST. Replace the cached VF mapping with runtime lookup on the mailbox DPI ring: derive the VF index from q_no, resolve the VF via exported PCI IOV helpers, validate it with the PF pointer and VF ID, then issue pcie_flr() and drop the reference with pci_dev_put(). Remove the unused VF lookup table initialization and cleanup. Fixes: ca6139ffc67ee ("liquidio CN23XX: sysfs VF config support") Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support") Signed-off-by: Yuho Choi <dbgh9129@gmail.com> Link: https://patch.msgid.link/20260701040847.1897845-1-dbgh9129@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>

Triage Assessment

Vulnerability Type: Memory safety / Use-after-free

Confidence: HIGH

Reasoning:

The patch removes a caching mechanism that could hold stale or invalid pci_dev references for VFs and replaces it with a runtime lookup that validates VF mappings via PF pointer and VF ID, followed by proper reference management (pcie_flr, pci_dev_put). This mitigates potential use-after-free/dereference of invalid PCI device pointers during VF FLR handling, reducing memory/safety risks and potential security impact.

Verification Assessment

Vulnerability Type: Memory safety / Use-after-free (stale PCI device reference in VF handling)

Confidence: HIGH

Affected Versions: <= v7.0-rc6 (CN23XX LiquidIO SR-IOV VF handling prior to this patch)

Code Diff

diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0db08ac3d098fb..e303956b4bf12a 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3779,9 +3779,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) static int octeon_enable_sriov(struct octeon_device *oct) { unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced; - struct pci_dev *vfdev; int err; - u32 u; if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) { err = pci_enable_sriov(oct->pci_dev, @@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) return err; } oct->sriov_info.sriov_enabled = 1; - - /* init lookup table that maps DPI ring number to VF pci_dev - * struct pointer - */ - u = 0; - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, NULL); - while (vfdev) { - if (vfdev->is_virtfn && - (vfdev->physfn == oct->pci_dev)) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = - vfdev; - u += oct->sriov_info.rings_per_vf; - } - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, vfdev); - } } return num_vfs_alloced; @@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) static int lio_pci_sriov_disable(struct octeon_device *oct) { - int u; - if (pci_vfs_assigned(oct->pci_dev)) { dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n"); return -EPERM; @@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct) pci_disable_sriov(oct->pci_dev); - u = 0; - while (u < MAX_POSSIBLE_VFS) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL; - u += oct->sriov_info.rings_per_vf; - } - oct->sriov_info.num_vfs_alloced = 0; dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n", oct->pf_num); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 19344b21f8fb94..858a0fff2cc0b6 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -390,9 +390,6 @@ struct octeon_sriov_info { struct lio_trusted_vf trusted_vf; - /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ - struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; - u64 vf_macaddr[MAX_POSSIBLE_VFS]; u16 vf_vlantci[MAX_POSSIBLE_VFS]; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index ad685f5d0a1363..697fcdc41e3cd7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -26,6 +26,31 @@ #include "octeon_mailbox.h" #include "cn23xx_pf_device.h" +static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no) +{ + struct pci_dev *vfdev = NULL; + int vfidx; + + if (!oct->sriov_info.rings_per_vf) + return NULL; + + if (q_no % oct->sriov_info.rings_per_vf) + return NULL; + + vfidx = q_no / oct->sriov_info.rings_per_vf; + if (vfidx >= oct->sriov_info.num_vfs_alloced) + return NULL; + + while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, vfdev))) { + if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev && + pci_iov_vf_id(vfdev) == vfidx) + return vfdev; + } + + return NULL; +} + /** * octeon_mbox_read: * @mbox: Pointer mailbox @@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, struct octeon_mbox_cmd *mbox_cmd) { struct octeon_device *oct = mbox->oct_dev; + struct pci_dev *vfdev; switch (mbox_cmd->msg.s.cmd) { case OCTEON_VF_ACTIVE: @@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, dev_info(&oct->pci_dev->dev, "got a request for FLR from VF that owns DPI ring %u\n", mbox->q_no); - pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]); + vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no); + if (!vfdev) + break; + + pcie_flr(vfdev); + pci_dev_put(vfdev); break; case OCTEON_PF_CHANGED_VF_MACADDR:
← Back to Alerts View on GitHub →