[PATCH v2] PCI: fix handle error case in pci_alloc_child_bus()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If device_register() returns error in pci_alloc_child_bus(), but it's not
handled, the 'bridge->subordinate' points a bus that is not registered,
it will lead kernel crash because of trying to delete unregistered device
in pci_remove_bus_device().

Unable to handle kernel NULL pointer dereference at virtual address 0000000000000108
CPU: 48 PID: 38377 Comm: bash Kdump: loaded Tainted: G        W          6.1.0-rc1+ #172
Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.58 10/24/2018
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : device_del+0x54/0x3d0
lr : device_del+0x37c/0x3d0
Call trace:
 device_del+0x54/0x3d0
 device_unregister+0x24/0x78
 pci_remove_bus+0x90/0xa0
 pci_remove_bus_device+0x128/0x138
 pci_stop_and_remove_bus_device_locked+0x2c/0x40
 remove_store+0xa4/0xb

Beside, the 'child' allocated by pci_alloc_bus() and the name allocated by
dev_set_name() need be freed, and also the refcount of bridge and of_node
which is get in pci_alloc_child_bus() need be put.

Fix these by setting 'bridge->subordinate' to NULL and calling put_device(),
if device_register() returns error, and return NULL in pci_alloc_child_bus().

Fixes: 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible")
Reported-by: kernel test robot <lkp@xxxxxxxxx>
Reported-by: Dan Carpenter <error27@xxxxxxxxx>
Signed-off-by: Yang Yingliang <yangyingliang@xxxxxxxxxx>
---
v1 -> v2:
   Check bridge if it's NULL, before set subordinate, which is reported by
   kernel test robot and Dan Carpenter.
---
 drivers/pci/probe.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 792dfee9cfd7..7109f9b215a6 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1139,7 +1139,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
 add_dev:
 	pci_set_bus_msi_domain(child);
 	ret = device_register(&child->dev);
-	WARN_ON(ret < 0);
+	if (WARN_ON(ret < 0)) {
+		if (bridge)
+			bridge->subordinate = NULL;
+		put_device(&child->dev);
+		return NULL;
+	}
 
 	pcibios_add_bus(child);
 
-- 
2.25.1




[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux