From 5a23e8b0fd6010e25ae58362292235cc9213ca57 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 May 2021 17:15:52 +0300 Subject: [PATCH] mfd: Remove software node conditionally and locate at right place Currently the software node is removed in error case and at ->remove() stage unconditionally, that ruins the symmetry. Besides, in some cases, when mfd_add_device() fails, the device_remove_software_node() call may lead to NULL pointer dereference: BUG: kernel NULL pointer dereference, address: 00000000 ... EIP: strlen+0x12/0x20 ... kernfs_name_hash+0x13/0x70 kernfs_find_ns+0x32/0xc0 kernfs_remove_by_name_ns+0x2a/0x90 sysfs_remove_link+0x16/0x30 software_node_notify.cold+0x34/0x6b device_remove_software_node+0x5a/0x90 mfd_add_device.cold+0x30a/0x427 Fix all these by guarding device_remove_software_node() with a conditional and locating it at the right place. Fixes: 42e59982917a ("mfd: core: Add support for software nodes") Signed-off-by: Andy Shevchenko Signed-off-by: Lee Jones --- drivers/mfd/mfd-core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 6f02b8022c6d..79f5c6a18815 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -266,18 +266,18 @@ static int mfd_add_device(struct device *parent, int id, if (has_acpi_companion(&pdev->dev)) { ret = acpi_check_resource_conflict(&res[r]); if (ret) - goto fail_of_entry; + goto fail_res_conflict; } } } ret = platform_device_add_resources(pdev, res, cell->num_resources); if (ret) - goto fail_of_entry; + goto fail_res_conflict; ret = platform_device_add(pdev); if (ret) - goto fail_of_entry; + goto fail_res_conflict; if (cell->pm_runtime_no_callbacks) pm_runtime_no_callbacks(&pdev->dev); @@ -286,13 +286,15 @@ static int mfd_add_device(struct device *parent, int id, return 0; +fail_res_conflict: + if (cell->swnode) + device_remove_software_node(&pdev->dev); fail_of_entry: list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list) if (of_entry->dev == &pdev->dev) { list_del(&of_entry->list); kfree(of_entry); } - device_remove_software_node(&pdev->dev); fail_alias: regulator_bulk_unregister_supply_alias(&pdev->dev, cell->parent_supplies, @@ -358,11 +360,12 @@ static int mfd_remove_devices_fn(struct device *dev, void *data) if (level && cell->level > *level) return 0; + if (cell->swnode) + device_remove_software_node(&pdev->dev); + regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies, cell->num_parent_supplies); - device_remove_software_node(&pdev->dev); - platform_device_unregister(pdev); return 0; }