[PATCH] PCI: Fix pci_alloc_child_bus() error path

From: Hans Zhang

Date: Mon Nov 02 2026 - 01:41:35 EST


If device_register() fails in pci_alloc_child_bus(), put_device()
frees the child bus, but bridge->subordinate and the bridge resource
names still point into the freed child. Subsequent rescans can then
dereference dangling pointers.

Also, WARN_ON(ret < 0) reports device_register() failures as a kernel
bug even when they are caused by normal runtime resource exhaustion,
such as fault-injected -ENOMEM.

Defer assigning bridge->subordinate and the bridge resource names until
after device_register() succeeds, and handle the error without WARN_ON().

Reported-by: syzbot+ea7278bdcb20a491495b@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=ea7278bdcb20a491495b
Fixes: 4f535093cf8f6 ("PCI: Put pci_dev in device tree as early as possible")
Assisted-by: Codex:deepseek-v4-flash
Signed-off-by: Hans Zhang <18255117159@xxxxxxx>
---
drivers/pci/probe.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9a178e0e5f7f..0f317d25a32b 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1259,21 +1259,29 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
pci_info(child, "extended config space not accessible\n");
}

- /* Set up default resource pointers and names */
- for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
+ /*
+ * Set up default resource pointers now, but defer pointing the bridge
+ * resources at child->name until device_register() succeeds. On
+ * failure put_device() frees child, so any references to child would
+ * otherwise dangle.
+ */
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i];
- child->resource[i]->name = child->name;
- }
- bridge->subordinate = child;

add_dev:
pci_set_bus_msi_domain(child);
ret = device_register(&child->dev);
- if (WARN_ON(ret < 0)) {
+ if (ret < 0) {
put_device(&child->dev);
return NULL;
}

+ if (bridge) {
+ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+ child->resource[i]->name = child->name;
+ bridge->subordinate = child;
+ }
+
pcibios_add_bus(child);

if (child->ops->add_bus) {

base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.43.0