]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mfd: core: Add locking around 'mfd_of_node_list'
authorDouglas Anderson <dianders@chromium.org>
Wed, 10 Dec 2025 19:30:03 +0000 (11:30 -0800)
committerLee Jones <lee@kernel.org>
Thu, 22 Jan 2026 14:22:22 +0000 (14:22 +0000)
Manipulating a list in the kernel isn't safe without some sort of
mutual exclusion. Add a mutex any time we access / modify
'mfd_of_node_list' to prevent possible crashes.

Cc: stable@vger.kernel.org
Fixes: 466a62d7642f ("mfd: core: Make a best effort attempt to match devices with the correct of_nodes")
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Link: https://patch.msgid.link/20251210113002.1.I6ceaca2cfb7eb25737012b166671f516696be4fd@changeid
Signed-off-by: Lee Jones <lee@kernel.org>
drivers/mfd/mfd-core.c

index 6925bedddc80bbb733a3786c5b3054c8e9665c4b..a30f93bf030aabfeb79abc9eedc00d8abf0ad129 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/regulator/consumer.h>
 
 static LIST_HEAD(mfd_of_node_list);
+static DEFINE_MUTEX(mfd_of_node_mutex);
 
 struct mfd_of_node_entry {
        struct list_head list;
@@ -104,9 +105,11 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev,
        u64 of_node_addr;
 
        /* Skip if OF node has previously been allocated to a device */
-       list_for_each_entry(of_entry, &mfd_of_node_list, list)
-               if (of_entry->np == np)
-                       return -EAGAIN;
+       scoped_guard(mutex, &mfd_of_node_mutex) {
+               list_for_each_entry(of_entry, &mfd_of_node_list, list)
+                       if (of_entry->np == np)
+                               return -EAGAIN;
+       }
 
        if (!cell->use_of_reg)
                /* No of_reg defined - allocate first free compatible match */
@@ -128,7 +131,8 @@ allocate_of_node:
 
        of_entry->dev = &pdev->dev;
        of_entry->np = np;
-       list_add_tail(&of_entry->list, &mfd_of_node_list);
+       scoped_guard(mutex, &mfd_of_node_mutex)
+               list_add_tail(&of_entry->list, &mfd_of_node_list);
 
        of_node_get(np);
        device_set_node(&pdev->dev, of_fwnode_handle(np));
@@ -284,11 +288,13 @@ 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);
-               }
+       scoped_guard(mutex, &mfd_of_node_mutex) {
+               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);
+                       }
+       }
 fail_alias:
        regulator_bulk_unregister_supply_alias(&pdev->dev,
                                               cell->parent_supplies,
@@ -358,11 +364,13 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
        if (cell->swnode)
                device_remove_software_node(&pdev->dev);
 
-       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);
-               }
+       scoped_guard(mutex, &mfd_of_node_mutex) {
+               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);
+                       }
+       }
 
        regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
                                               cell->num_parent_supplies);