After commit 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible"), pcibios_add_device() and device_add() are moved to pci_device_add(), and it never return error code in error cases. If pcibios_device_add() or device_add() returns error in pci_device_add(), the 'dev->p' is not set which will lead null-ptr-deref in device_attach() called by pci_bus_add_device(). Change return type of pci_device_add() to int, and handle error in it if pcibios_device_add() or device_add() fails. So callers can check the return value and handle it. Fixes: 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible") Signed-off-by: Yang Yingliang <yangyingliang@xxxxxxxxxx> --- drivers/pci/probe.c | 21 +++++++++++++++++---- include/linux/pci.h | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 03b926d7c7ec..792dfee9cfd7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2517,7 +2517,7 @@ static void pci_set_msi_domain(struct pci_dev *dev) dev_set_msi_domain(&dev->dev, d); } -void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) +int pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { int ret; @@ -2552,7 +2552,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) up_write(&pci_bus_sem); ret = pcibios_device_add(dev); - WARN_ON(ret < 0); + if (WARN_ON(ret < 0)) + goto err; /* Set up MSI IRQ domain */ pci_set_msi_domain(dev); @@ -2560,7 +2561,16 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Notifier could use PCI capabilities */ dev->match_driver = false; ret = device_add(&dev->dev); - WARN_ON(ret < 0); + if (WARN_ON(ret < 0)) + goto err; + + return 0; + +err: + down_write(&pci_bus_sem); + list_del(&dev->bus_list); + up_write(&pci_bus_sem); + return ret; } struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) @@ -2577,7 +2587,10 @@ struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) if (!dev) return NULL; - pci_device_add(dev, bus); + if (pci_device_add(dev, bus)) { + pci_dev_put(dev); + return NULL; + } return dev; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 2bda4a4e47e8..3292261ea9c8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1124,7 +1124,7 @@ static inline void pci_dev_assign_slot(struct pci_dev *dev) { } #endif int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); -void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); +int pci_device_add(struct pci_dev *dev, struct pci_bus *bus); unsigned int pci_scan_child_bus(struct pci_bus *bus); void pci_bus_add_device(struct pci_dev *dev); void pci_read_bridge_bases(struct pci_bus *child); -- 2.25.1