From 5c8418cf4025388bedd4d65ada993f7d3786cc3a Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 26 Nov 2024 13:04:34 -0800 Subject: [PATCH] PCI/pwrctrl: Unregister platform device only if one actually exists If a PCI device has an associated device_node with power supplies, pci_bus_add_device() creates platform devices for use by pwrctrl. When the PCI device is removed, pci_stop_dev() uses of_find_device_by_node() to locate the related platform device, then unregisters it. But when we remove a PCI device with no associated device node, dev_of_node(dev) is NULL, and of_find_device_by_node(NULL) returns the first device with "dev->of_node == NULL". The result is that we (a) mistakenly unregister a completely unrelated platform device, leading to issues like the first trace below, and (b) dereference the NULL pointer from dev_of_node() when clearing OF_POPULATED, as in the second trace. Unregister a platform device only if there is one associated with this PCI device. This resolves issues seen when doing: # echo 1 > /sys/bus/pci/devices/.../remove Sample issue from unregistering the wrong platform device: WARNING: CPU: 0 PID: 5095 at drivers/regulator/core.c:5885 regulator_unregister+0x140/0x160 Call trace: regulator_unregister+0x140/0x160 devm_rdev_release+0x1c/0x30 release_nodes+0x68/0x100 devres_release_all+0x98/0xf8 device_unbind_cleanup+0x20/0x70 device_release_driver_internal+0x1f4/0x240 device_release_driver+0x20/0x40 bus_remove_device+0xd8/0x170 device_del+0x154/0x380 device_unregister+0x28/0x88 of_device_unregister+0x1c/0x30 pci_stop_bus_device+0x154/0x1b0 pci_stop_and_remove_bus_device_locked+0x28/0x48 remove_store+0xa0/0xb8 dev_attr_store+0x20/0x40 sysfs_kf_write+0x4c/0x68 Later NULL pointer dereference for of_node_clear_flag(NULL, OF_POPULATED): Unable to handle kernel NULL pointer dereference at virtual address 00000000000000c0 Call trace: pci_stop_bus_device+0x190/0x1b0 pci_stop_and_remove_bus_device_locked+0x28/0x48 remove_store+0xa0/0xb8 dev_attr_store+0x20/0x40 sysfs_kf_write+0x4c/0x68 Link: https://lore.kernel.org/r/20241126210443.4052876-1-briannorris@chromium.org Fixes: 681725afb6b9 ("PCI/pwrctl: Remove pwrctl device without iterating over all children of pwrctl parent") Reported-by: Saurabh Sengar Closes: https://lore.kernel.org/r/1732890621-19656-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Brian Norris [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas --- drivers/pci/remove.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 963b8d2855c1..efc37fcb73e2 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -19,14 +19,19 @@ static void pci_free_resources(struct pci_dev *dev) static void pci_pwrctrl_unregister(struct device *dev) { + struct device_node *np; struct platform_device *pdev; - pdev = of_find_device_by_node(dev_of_node(dev)); + np = dev_of_node(dev); + if (!np) + return; + + pdev = of_find_device_by_node(np); if (!pdev) return; of_device_unregister(pdev); - of_node_clear_flag(dev_of_node(dev), OF_POPULATED); + of_node_clear_flag(np, OF_POPULATED); } static void pci_stop_dev(struct pci_dev *dev)