* [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®