* [PATCH 0/2] PCI: Guard against NULL bus->self on SR-IOV virtual buses
@ 2026-06-10 13:57 Yuguo Li
2026-06-10 13:57 ` [PATCH 1/2] PCI: Bail out of pci_read_bridge_bases() for " Yuguo Li
2026-06-10 13:57 ` [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() Yuguo Li
0 siblings, 2 replies; 5+ messages in thread
From: Yuguo Li @ 2026-06-10 13:57 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: linux-pci, linux-kernel, Jesse Barnes, Kenji Kaneshige,
Ivan Kokshaysky, Yinghai Lu, Yuguo Li
Hi Bjorn, all,
This series fixes two NULL pointer dereferences in the PCI core that are
hit on x86_64 with SR-IOV PFs whose VFs span more than one bus number,
once anything pokes those VFs through paths that walk up through their
parent bus.
Background
==========
When a VF allocated by pci_iov_add_virtfn() lands on a bus number
different from its PF, virtfn_add_bus() calls
pci_add_new_bus(parent, NULL, busnr)
producing a struct pci_bus with a valid ->parent but no bridge device
(->self == NULL). include/linux/pci.h already documents this:
/*
* Note: we'd love to have a "device" pointer here, but
* we can't have it. ... See virtfn_add_bus() for an
* example of a bus without a device.
*/
The MSI IRQ domain path was taught about this case years ago by
38ea72bdb65d ("PCI/MSI: Fix MSI IRQ domains for VFs on virtual buses")
but other paths in drivers/pci/ that follow bus->self were not updated
in step. Two of them have been observable for a long time but only
trip on configurations where a VF actually crosses a bus boundary AND
something (sysfs rescan, resource reallocation, etc.) reaches them on
that virtual bus -- which is not exercised by routine boot.
What trips
==========
Patch 1/2 - drivers/pci/probe.c::pci_read_bridge_bases()
Only guards pci_is_root_bus(child). Reached on x86 via
pcibios_fixup_bus() during pci_rescan_bus() on a virtual bus
(bus->is_added stays 0 because the SR-IOV add path never invokes
pci_scan_child_bus_*()). Dereferences child->self at the
pci_info()/dev->transparent site.
Fixes: f92d4e29d785 ("PCI: fix wrong assumption in
pci_read_bridge_bases")
Patch 2/2 - drivers/pci/setup-res.c::_pci_assign_resource()
Walks up parent buses retrying allocation; loop termination is
"if (!bus->parent || !bus->self->transparent) break;". For a
virtual bus the !bus->parent test fails (parent is real), then
bus->self->transparent NULL-derefs.
Fixes: d09ee9687e02 ("PCI: improve resource allocation under
transparent bridges")
Reproducer
==========
- x86_64, mainline v7.1-rc7+
- any SR-IOV-capable PF whose sriov_numvfs is large enough that at
least one VF crosses to a new bus number (mlx5, i40e, ixgbe, igb,
bnxt, ...)
- LTP testcases/bin/tpci is one off-the-shelf trigger: its
test_scan_bus subtest reaches patch 1 via pci_rescan_bus(), and
the resource-assignment subtest reaches patch 2 via
pci_assign_resource().
Without this series, tpci panics within seconds. With both patches
applied to the same tree, tpci completes its full PCI subtest matrix
on the same hardware without splatting.
Patches
=======
Yuguo Li (2):
PCI: Bail out of pci_read_bridge_bases() for SR-IOV virtual buses
PCI: setup-res: Guard against bus->self == NULL in
_pci_assign_resource()
drivers/pci/probe.c | 10 ++++++++++
drivers/pci/setup-res.c | 2 +-
2 files changed, 11 insertions(+), 1 deletion(-)
--
2.43.7
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 1/2] PCI: Bail out of pci_read_bridge_bases() for SR-IOV virtual buses 2026-06-10 13:57 [PATCH 0/2] PCI: Guard against NULL bus->self on SR-IOV virtual buses Yuguo Li @ 2026-06-10 13:57 ` Yuguo Li 2026-06-10 13:57 ` [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() Yuguo Li 1 sibling, 0 replies; 5+ messages in thread From: Yuguo Li @ 2026-06-10 13:57 UTC (permalink / raw) To: Bjorn Helgaas Cc: linux-pci, linux-kernel, Jesse Barnes, Kenji Kaneshige, Ivan Kokshaysky, Yinghai Lu, Yuguo Li pci_iov_add_virtfn() routes through virtfn_add_bus(), which calls pci_add_new_bus(parent, NULL, busnr) whenever a VF lands on a bus number different from its PF. This produces a pci_bus with a valid parent but no bridge device (bus->self == NULL). There is no bridge to read, and the SR-IOV add path never invokes pci_scan_child_bus_*(), so bus->is_added stays 0 on these buses. That stays harmless until something invokes pci_rescan_bus() on a virtual bus, e.g. via the per-device sysfs entry: echo 1 > /sys/bus/pci/devices/<VF>/rescan On x86, pci_scan_child_bus_extend() then sees !bus->is_added and calls pcibios_fixup_bus(), which unconditionally calls pci_read_bridge_bases(). The function only guards against root buses, so it dereferences child->self and oopses. The fault address is the offset of pci_dev->transparent, reached via the dev->transparent test in the pci_info() call. Reproduced on mainline 7.1.0-rc7+ on x86_64 with an SR-IOV-capable PF whose VFs span multiple bus numbers, by writing "1" to a VF's sysfs rescan attribute (LTP's tpci test does this implicitly via pci_rescan_bus()): BUG: kernel NULL pointer dereference, address: 0000000000000860 #PF: supervisor read access in kernel mode Oops: Oops: 0000 [#1] SMP NOPTI CPU: 12 ... 7.1.0-rc7+ ... PREEMPTLAZY RIP: 0010:pci_read_bridge_bases+0x39/0x120 RBX: 0000000000000000 (= bus->self) Call Trace: <TASK> pcibios_fixup_bus+0xe/0xd0 pci_scan_child_bus_extend+0x6b/0x2e0 pci_rescan_bus+0x11/0x30 ... (sysfs write to .../rescan) do_syscall_64+0xab/0x500 With this patch applied to the same tree, the trigger sequence completes without crashing. Triggering this only requires: - x86_64 (or any arch whose pcibios_fixup_bus() calls pci_read_bridge_bases()), - any SR-IOV-capable PF (e.g. mlx5, i40e, ixgbe, igb) where sriov_numvfs is large enough that at least one VF crosses to a new bus number, and - a write to that VF's sysfs rescan attribute. The same NULL self pattern on SR-IOV virtual buses was already addressed for the MSI IRQ domain path in commit 38ea72bdb65d ("PCI/MSI: Fix MSI IRQ domains for VFs on virtual buses"), but pci_read_bridge_bases() was not updated in step. The hidden assumption that non-root buses always have a bridge dates back to commit f92d4e29d785 ("PCI: fix wrong assumption in pci_read_bridge_bases"), which tightened the entry guard from "if (!dev)" to "if (!child->parent)" to handle root buses that do have a self, but inadvertently exposed the "non-root + self == NULL" SR-IOV virtual bus case. Add an explicit early return for bus->self == NULL. There are no bridge windows or transparent decode flags to propagate when no bridge device exists, so returning early is semantically correct and matches the existing pci_is_root_bus() bail-out. Fixes: f92d4e29d785 ("PCI: fix wrong assumption in pci_read_bridge_bases") Cc: stable@vger.kernel.org Signed-off-by: Yuguo Li <hugoolli@tencent.com> --- drivers/pci/probe.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc..6bcc3b58031b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -577,6 +577,16 @@ void pci_read_bridge_bases(struct pci_bus *child) if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ return; + /* + * SR-IOV virtual buses are created by virtfn_add_bus() via + * pci_add_new_bus(parent, NULL, busnr) when a VF lands on a bus + * number different from its PF. Such buses have a valid parent + * but no bridge device (->self == NULL), so there are no bridge + * windows to read. Bail out before dereferencing @dev. + */ + if (!dev) + return; + pci_info(dev, "PCI bridge to %pR%s\n", &child->busn_res, dev->transparent ? " (subtractive decode)" : ""); -- 2.43.7 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() 2026-06-10 13:57 [PATCH 0/2] PCI: Guard against NULL bus->self on SR-IOV virtual buses Yuguo Li 2026-06-10 13:57 ` [PATCH 1/2] PCI: Bail out of pci_read_bridge_bases() for " Yuguo Li @ 2026-06-10 13:57 ` Yuguo Li 2026-06-11 9:57 ` Ilpo Järvinen 1 sibling, 1 reply; 5+ messages in thread From: Yuguo Li @ 2026-06-10 13:57 UTC (permalink / raw) To: Bjorn Helgaas Cc: linux-pci, linux-kernel, Jesse Barnes, Kenji Kaneshige, Ivan Kokshaysky, Yinghai Lu, Yuguo Li _pci_assign_resource() walks up the parent buses looking for a transparent bridge to retry resource allocation against. The termination check dereferences bus->self->transparent without first testing bus->self. For SR-IOV virtual buses created by virtfn_add_bus() via pci_add_new_bus(parent, NULL, busnr) -- which happens when a VF lands on a bus number different from its PF -- bus->self is NULL. When __pci_assign_resource() is invoked on such a VF (e.g. via pci_assign_resource() from userspace-triggered LTP coverage) and the allocation fails on the first iteration, the !bus->parent test passes because the virtual bus does have a parent, and the next term then NULL-derefs bus->self. Add an explicit !bus->self check, mirroring the established pattern elsewhere in drivers/pci/ (e.g. pci.c, probe.c, pciehp_hpc.c). Reproduced on mainline 7.1.0-rc7+ on x86_64 with an SR-IOV PF whose VFs span multiple bus numbers, by triggering pci_assign_resource() on a VF that lives on a virtual bus: BUG: kernel NULL pointer dereference, address: 0000000000000860 RIP: 0010:_pci_assign_resource+0x63/0x130 Call Trace: pci_assign_resource+0xe9/0x370 ... (LTP tpci test-case 12 driving pci_assign_resource via sysfs) do_syscall_64+0xab/0x500 This is the same SR-IOV-virtual-bus / self == NULL pattern fixed for pci_read_bridge_bases() in commit ("PCI: Bail out of pci_read_bridge_bases() for SR-IOV virtual buses"). Fixes: d09ee9687e02 ("PCI: improve resource allocation under transparent bridges") Cc: stable@vger.kernel.org Signed-off-by: Yuguo Li <hugoolli@tencent.com> --- drivers/pci/setup-res.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 991d3ed543f5..e8bd3d4ff923 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -353,7 +353,7 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, bus = dev->bus; while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { - if (!bus->parent || !bus->self->transparent) + if (!bus->parent || !bus->self || !bus->self->transparent) break; bus = bus->parent; } -- 2.43.7 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() 2026-06-10 13:57 ` [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() Yuguo Li @ 2026-06-11 9:57 ` Ilpo Järvinen 2026-06-12 8:40 ` hugo lee 0 siblings, 1 reply; 5+ messages in thread From: Ilpo Järvinen @ 2026-06-11 9:57 UTC (permalink / raw) To: Yuguo Li Cc: Bjorn Helgaas, linux-pci, LKML, Jesse Barnes, Kenji Kaneshige, Ivan Kokshaysky, Yinghai Lu, Yuguo Li On Wed, 10 Jun 2026, Yuguo Li wrote: > _pci_assign_resource() walks up the parent buses looking for a > transparent bridge to retry resource allocation against. The > termination check dereferences bus->self->transparent without first > testing bus->self. > > For SR-IOV virtual buses created by virtfn_add_bus() via > pci_add_new_bus(parent, NULL, busnr) -- which happens when a VF lands > on a bus number different from its PF -- bus->self is NULL. When > __pci_assign_resource() is invoked on such a VF (e.g. via > pci_assign_resource() from userspace-triggered LTP coverage) and the > allocation fails on the first iteration, the !bus->parent test passes > because the virtual bus does have a parent, and the next term then > NULL-derefs bus->self. > > Add an explicit !bus->self check, mirroring the established pattern > elsewhere in drivers/pci/ (e.g. pci.c, probe.c, pciehp_hpc.c). > > Reproduced on mainline 7.1.0-rc7+ on x86_64 with an SR-IOV PF whose > VFs span multiple bus numbers, by triggering pci_assign_resource() on > a VF that lives on a virtual bus: > > BUG: kernel NULL pointer dereference, address: 0000000000000860 > RIP: 0010:_pci_assign_resource+0x63/0x130 > Call Trace: > pci_assign_resource+0xe9/0x370 > ... (LTP tpci test-case 12 driving pci_assign_resource via sysfs) Hi, And who called _*pci_assign_resource(), etc.? I actually wanted to check the code... :-( Please don't strip PCI core related parts of the callchain. > do_syscall_64+0xab/0x500 > > This is the same SR-IOV-virtual-bus / self == NULL pattern fixed for > pci_read_bridge_bases() in commit ("PCI: Bail out of > pci_read_bridge_bases() for SR-IOV virtual buses"). > > Fixes: d09ee9687e02 ("PCI: improve resource allocation under transparent bridges") > Cc: stable@vger.kernel.org > Signed-off-by: Yuguo Li <hugoolli@tencent.com> > --- > drivers/pci/setup-res.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c > index 991d3ed543f5..e8bd3d4ff923 100644 > --- a/drivers/pci/setup-res.c > +++ b/drivers/pci/setup-res.c > @@ -353,7 +353,7 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, > > bus = dev->bus; > while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { > - if (!bus->parent || !bus->self->transparent) > + if (!bus->parent || !bus->self || !bus->self->transparent) I'd like to know if this should not break in case of !bus->self because I couldn't immediately find the code which would add window resources to the VF virtual bus so I'm not convinced this check is right. __pci_assign_resource() obviously must have failed to assign the resource if you can trigger NULL deref here so that hints VF bus does not have the resource that could parent dev's resource present (another explanation could be that it just doesn't fit there but I cannot say it based on the limited information). Did the resource fail to assign? What is the resulting resource tree with this change? The more approriate check might be this one: if (!bus->parent || (bus->self && !bus->self->transparent)) -- i. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() 2026-06-11 9:57 ` Ilpo Järvinen @ 2026-06-12 8:40 ` hugo lee 0 siblings, 0 replies; 5+ messages in thread From: hugo lee @ 2026-06-12 8:40 UTC (permalink / raw) To: Ilpo Järvinen Cc: Bjorn Helgaas, linux-pci, LKML, Jesse Barnes, Kenji Kaneshige, Ivan Kokshaysky, Yinghai Lu, Yuguo Li On Thu, Jun 11, 2026 at 5:57 PM Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> wrote: > > On Wed, 10 Jun 2026, Yuguo Li wrote: > > > _pci_assign_resource() walks up the parent buses looking for a > > transparent bridge to retry resource allocation against. The > > termination check dereferences bus->self->transparent without first > > testing bus->self. > > > > For SR-IOV virtual buses created by virtfn_add_bus() via > > pci_add_new_bus(parent, NULL, busnr) -- which happens when a VF lands > > on a bus number different from its PF -- bus->self is NULL. When > > __pci_assign_resource() is invoked on such a VF (e.g. via > > pci_assign_resource() from userspace-triggered LTP coverage) and the > > allocation fails on the first iteration, the !bus->parent test passes > > because the virtual bus does have a parent, and the next term then > > NULL-derefs bus->self. > > > > Add an explicit !bus->self check, mirroring the established pattern > > elsewhere in drivers/pci/ (e.g. pci.c, probe.c, pciehp_hpc.c). > > > > Reproduced on mainline 7.1.0-rc7+ on x86_64 with an SR-IOV PF whose > > VFs span multiple bus numbers, by triggering pci_assign_resource() on > > a VF that lives on a virtual bus: > > > > BUG: kernel NULL pointer dereference, address: 0000000000000860 > > RIP: 0010:_pci_assign_resource+0x63/0x130 > > Call Trace: > > pci_assign_resource+0xe9/0x370 > > ... (LTP tpci test-case 12 driving pci_assign_resource via sysfs) > > Hi, > > And who called _*pci_assign_resource(), etc.? I actually wanted to check > the code... :-( Please don't strip PCI core related parts of the > callchain. > Hi Ilpo, Thanks for the review. A clarification on the call chain, results of running your suggested form, and a question on the right shape. 1. Call chain The "..." in last mail was LTP module frames and the syscall layer, not PCI core frames -- the chain hits _pci_assign_resource() in one core hop : pci_assign_resource+0xe9/0x370 test_case+0x202/0x6e0 [ltp_tpci] sys_tcase+0x51/0x80 [ltp_tpci] kernfs_fop_write_iter+0x117/0x1f0 vfs_write+0x28d/0x450 ksys_write+0x69/0xe0 do_syscall_64+0xab/0x500 The trigger is upstream LTP test_pci. Patch 1 is reached via sub-case BUS_SCAN (case 6, pci_rescan_bus()). Patch 2 is reached via sub-case PCI_RESOURCES(case 12), which on every prefetchable MMIO BAR runs: pci_release_resource(dev, i); ret = pci_assign_resource(dev, i); https://github.com/linux-test-project/ltp/blob/d631e9c/testcases/kernel/device-drivers/pci/tpci_kernel/ltp_tpci.c#L428 > > do_syscall_64+0xab/0x500 > > > > This is the same SR-IOV-virtual-bus / self == NULL pattern fixed for > > pci_read_bridge_bases() in commit ("PCI: Bail out of > > pci_read_bridge_bases() for SR-IOV virtual buses"). > > > > Fixes: d09ee9687e02 ("PCI: improve resource allocation under transparent bridges") > > Cc: stable@vger.kernel.org > > Signed-off-by: Yuguo Li <hugoolli@tencent.com> > > --- > > drivers/pci/setup-res.c | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c > > index 991d3ed543f5..e8bd3d4ff923 100644 > > --- a/drivers/pci/setup-res.c > > +++ b/drivers/pci/setup-res.c > > @@ -353,7 +353,7 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, > > > > bus = dev->bus; > > while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { > > - if (!bus->parent || !bus->self->transparent) > > + if (!bus->parent || !bus->self || !bus->self->transparent) > > I'd like to know if this should not break in case of !bus->self because > I couldn't immediately find the code which would add window resources to > the VF virtual bus so I'm not convinced this check is right. > > __pci_assign_resource() obviously must have failed to assign the resource > if you can trigger NULL deref here so that hints VF bus does not have the > resource that could parent dev's resource present (another explanation > could be that it just doesn't fit there but I cannot say it based on the > limited information). > > Did the resource fail to assign? What is the resulting resource tree with > this change? > > The more approriate check might be this one: > > if (!bus->parent || (bus->self && !bus->self->transparent)) > > -- > i. > 2.SRIOV VF and pci resource management Yes, resource failed to assign. Confirmed on a system carrying these two patches: cross-bus VFs end up with resource[0] either 0/0/0 or "start=0, end=size-1, flags preserved" (the residue pci_release_resource() leaves), while same-bus VFs on the same PF remain assigned, but the result comes from the generic allocator on the PF bus, not from the SR-IOV slice that virtfn_add_bus() set up. Either way, dev->resource[] no longer reflects what the hardware decodes. VF resources don't normally come from _pci_assign_resource(). At probe time, pci_iov_add_virtfn() programs them as a deterministic slice of the PF VF BAR window. So it is inappropriate to call pci_release_resource()/pci_assign_resource() for a VF. Maybe we should add an early bail-out on dev->is_virtfn at the top of both -- with -EBUSY from _pci_assign_resource(), which ltp_tpci already treats as TPASS, and keep dev->resource[] consistent with the probe-time slice. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-12 8:41 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-06-10 13:57 [PATCH 0/2] PCI: Guard against NULL bus->self on SR-IOV virtual buses Yuguo Li 2026-06-10 13:57 ` [PATCH 1/2] PCI: Bail out of pci_read_bridge_bases() for " Yuguo Li 2026-06-10 13:57 ` [PATCH 2/2] PCI: setup-res: Guard against bus->self == NULL in _pci_assign_resource() Yuguo Li 2026-06-11 9:57 ` Ilpo Järvinen 2026-06-12 8:40 ` hugo lee
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®