[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251210113002.1.I6ceaca2cfb7eb25737012b166671f516696be4fd@changeid>
Date: Wed, 10 Dec 2025 11:30:03 -0800
From: Douglas Anderson <dianders@...omium.org>
To: Lee Jones <lee@...nel.org>
Cc: Douglas Anderson <dianders@...omium.org>,
stable@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH] mfd: core: Add locking around `mfd_of_node_list`
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@...r.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@...omium.org>
---
While I have no definitive way to reproduce the crash we've been
seeing, it's clear that the `mfd_of_node_list` isn't right at the time
of the crash. Code inspection shows the lack of locking.
drivers/mfd/mfd-core.c | 36 ++++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 7d14a1e7631e..c55223ce4327 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -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 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev,
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 @@ static int mfd_add_device(struct device *parent, int id,
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);
--
2.52.0.223.gf5cc29aaa4-goog
Powered by blists - more mailing lists