From: sashiko-bot@kernel.org
To: "Samiullah Khawaja" <skhawaja@google.com>
Cc: kvm@vger.kernel.org, linux-pci@vger.kernel.org
Subject: Re: [PATCH] vfio/pci: Only lock the upstream bridge if the reset may need it
Date: Fri, 13 Nov 2026 00:26:21 +0000 [thread overview]
Message-ID: <sashiko-outbox-211997@kernel.org> (raw)
In-Reply-To: <20261112235700.2029562-1-skhawaja@google.com>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] PCI: pci_reset_needs_bridge_lock() is true for VFs behind CXL
ports
- [Medium] PCI: pci_reset_needs_bridge_lock() is true for VFs on hotplug
slots
It also noticed 2 related pre-existing issues this patch does not
introduce:
- [Medium] PCI: incomplete sibling check in cxl_reset_bus_function() [1]
- [Critical] vfio/pci: double free in vfio_pci_set_power_state() [2]
--
commit 5344c77aa429b69701e7ba84d963f4e63e1dda8b
Author: Samiullah Khawaja <skhawaja@google.com>
vfio/pci: Only lock the upstream bridge if the reset may need it
This commit adds pci_reset_needs_bridge_lock() to check whether a device has
a bus reset method enabled, and updates vfio_pci_core_disable() to only lock
the upstream bridge when a bus reset may be used.
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 7c6f2aba81443..c44e4d4d87743 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -5256,6 +5256,34 @@ int __pci_reset_function_locked(struct pci_dev *dev)
> }
> EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
>
> +/**
> + * pci_reset_needs_bridge_lock - check whether a reset of @dev may need the
> + * upstream bridge to be locked
> + * @dev: PCI device to check
> + *
> + * A bus reset of @dev is done through its upstream bridge, so the caller must
> + * hold the bridge lock. The other reset methods only access @dev itself.
> + *
> + * Return: true if any of the reset methods enabled for @dev is a bus reset.
> + */
> +bool pci_reset_needs_bridge_lock(struct pci_dev *dev)
> +{
> + int i, m;
> +
> + for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
> + m = dev->reset_methods[i];
> + if (!m)
> + break;
> +
> + if (pci_reset_fn_methods[m].reset_fn == pci_reset_bus_function ||
[Severity: Medium]
Does pci_reset_needs_bridge_lock() still return true for VFs on hotplug
slots, leaving concurrent VF resets broken on hotplug-capable ports?
When pci_iov_add_virtfn() creates a VF on a bus with a hotplug slot (such as
pciehp with PCI_SLOT_ALL_DEVICES), pci_setup_device() assigns virtfn->slot
and virtfn->multifunction is cleared to 0 before pci_init_reset_methods()
runs. During probing, pci_reset_bus_function() calls
pci_dev_reset_slot_function() before pci_parent_bus_reset():
drivers/pci/pci.c:pci_dev_reset_slot_function() {
if (dev->subordinate || !dev->slot ||
dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET ||
(dev->multifunction && !dev->slot->per_func_slot))
return -ENOTTY;
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
}
Because pci_dev_reset_slot_function() returns 0, pci_reset_bus_function is
recorded in virtfn->reset_methods[] behind pcie_reset_flr (and similarly for
a single-function PF probed before VFs are enabled). Checking all entries
in dev->reset_methods[] causes pci_reset_needs_bridge_lock() to return true
even though __pci_reset_function_locked() will use pcie_reset_flr first, so
concurrent vfio_pci_core_disable() calls still contend on the bridge trylock
and skip the FLR.
> + pci_reset_fn_methods[m].reset_fn == cxl_reset_bus_function)
[Severity: Medium]
This problem wasn't introduced by this patch, but cxl_reset_bus_function()
[1] returns 0 on probe without checking whether sibling devices on the
bus prevent a bus reset:
drivers/pci/pci.c:cxl_reset_bus_function() {
...
dvsec = cxl_port_dvsec(bridge);
if (!dvsec)
return -ENOTTY;
if (probe)
return 0;
...
rc = pci_reset_bus_function(dev, probe);
}
During probing, returning 0 before calling pci_reset_bus_function() bypasses
the sibling checks in pci_dev_reset_slot_function() and
pci_parent_bus_reset(). For a CXL VF or a device sharing a bus, the PCI
core records cxl_reset_bus_function as supported, but when the reset is
later executed with probe == false, pci_reset_bus_function() fails with
-ENOTTY.
> + return true;
> + }
[Severity: Medium]
Will pci_reset_needs_bridge_lock() also always return true for VFs behind a
CXL-capable upstream port?
In cxl_reset_bus_function(), pci_upstream_bridge() resolves a VF to its PF's
upstream bridge via pci_physfn(), and if that bridge has a CXL Port DVSEC,
the probe path immediately returns 0. Every VF behind such a port therefore
has cxl_reset_bus_function in dev->reset_methods[] (behind pcie_reset_flr),
so pci_reset_needs_bridge_lock() returns true and concurrent
vfio_pci_core_disable() calls on those VFs still fail pci_dev_trylock() on
the shared bridge and skip the reset.
[ ... ]
> diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
> index fcbb2da860e2a..ba3dfdadae399 100644
> --- a/drivers/vfio/pci/vfio_pci_core.c
> +++ b/drivers/vfio/pci/vfio_pci_core.c
> @@ -777,7 +777,8 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
> * overwrite the previously restored configuration information.
> */
> if (vdev->reset_works) {
> - bridge = pci_upstream_bridge(pdev);
> + bridge = pci_reset_needs_bridge_lock(pdev) ?
> + pci_upstream_bridge(pdev) : NULL;
> if (bridge && !pci_dev_trylock(bridge))
> goto out_restore_state;
> if (pci_dev_trylock(pdev)) {
[Severity: Critical]
This problem wasn't introduced by this patch, but vfio_pci_core_disable()
[2] calls vfio_pci_set_power_state(vdev, PCI_D0) without holding
vdev->memory_lock, which can race with another path transitioning the device
to D0 and cause a use-after-free and double free of vdev->pm_save:
CPU0 CPU1
vfio_pci_core_iotcl() / config write vfio_pci_core_disable()
down_write(&vdev->memory_lock)
vfio_pci_set_power_state(..., D0)
vfio_pci_set_power_state(..., D0)
needs_restore = true
needs_restore = true
pci_set_power_state()
pci_set_power_state()
pci_load_and_free_saved_state(
&vdev->pm_save)
pci_load_and_free_saved_state(
&vdev->pm_save)
<-- use-after-free / double free
When the device is in D3hot, both threads see pdev->current_state >=
PCI_D3hot before the state is updated, so both set needs_restore = true and
call pci_load_and_free_saved_state(&vdev->pm_save) concurrently.
[1] https://sashiko.dev/#/bug/linux-f16fb8bb-75c3-4e2c-af09-9d033af32bc8
[2] https://sashiko.dev/#/bug/linux-d67caf72-8141-4778-be9b-75a34618f8f7
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261112235700.2029562-1-skhawaja@google.com?part=1
prev parent reply other threads:[~2026-11-13 0:26 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 23:57 [PATCH] vfio/pci: Only lock the upstream bridge if the reset may need it Samiullah Khawaja
2026-11-13 0:26 ` sashiko-bot [this message]
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-211997@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=skhawaja@google.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.