From: Rafael J. Wysocki Date: Tue, 14 Jan 2014 19:04:51 +0000 (-0700) Subject: PCI: Check parent kobject in pci_destroy_dev() X-Git-Tag: v3.14-rc1~132^2~2^2 X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=8a4c5c329de716996eea03d93753ccbb5406072b;p=linux.git PCI: Check parent kobject in pci_destroy_dev() If pci_stop_and_remove_bus_device() is run concurrently for a device and its parent bridge via remove_callback(), both code paths attempt to acquire pci_rescan_remove_lock. If the child device removal acquires it first, there will be no problems. However, if the parent bridge removal acquires it first, it will eventually execute pci_destroy_dev() for the child device, but that device object will not be freed yet due to the reference held by the concurrent child removal. Consequently, both pci_stop_bus_device() and pci_remove_bus_device() will be executed for that device unnecessarily and pci_destroy_dev() will see a corrupted list head in that object. Moreover, an excess put_device() will be executed for that device in that case which may lead to a use-after-free in the final kobject_put() done by sysfs_schedule_callback_work(). To avoid that problem, make pci_destroy_dev() check if the device's parent kobject is NULL, which only happens after device_del() has already run for it. Make pci_destroy_dev() return immediately whithout doing anything in that case. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 10fa13f9e309..4ff36bfa785e 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -20,6 +20,9 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { + if (!dev->dev.kobj.parent) + return; + device_del(&dev->dev); put_device(&dev->dev);