]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerSasha Levin <sashal@kernel.org>
Wed, 4 Mar 2026 12:21:12 +0000 (07:21 -0500)
[ Upstream commit 20117c92bcf9c11afd64d7481d8f94fdf410726e ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/mfd/mfd-core.c

index 7d14a1e7631ee8d5e91b228a07b2d05695e41b6e..c55223ce4327a5e1fe0276f1cca60556e328ab54 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;
@@ -105,9 +106,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 */
@@ -129,7 +132,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));
@@ -286,11 +290,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,
@@ -360,11 +366,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);