Unverified Commit 0cc15a10 authored by sparkhuang's avatar sparkhuang Committed by Mark Brown
Browse files

regulator: core: Protect regulator_supply_alias_list with regulator_list_mutex



regulator_supply_alias_list was accessed without any locking in
regulator_supply_alias(), regulator_register_supply_alias(), and
regulator_unregister_supply_alias(). Concurrent registration,
unregistration and lookups can race, leading to:

1 use-after-free if an alias entry is removed while being read,
2 duplicate entries when two threads register the same alias,
3 inconsistent alias mappings observed by consumers.

Protect all traversals, insertions and deletions on
regulator_supply_alias_list with the existing regulator_list_mutex.

Fixes: a06ccd9c ("regulator: core: Add ability to create a lookup alias for supply")
Signed-off-by: default avatarsparkhuang <huangshaobo3@xiaomi.com>
Reviewed-by: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20251127025716.5440-1-huangshaobo3@xiaomi.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent c67bb844
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -2058,6 +2058,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply)
{
	struct regulator_supply_alias *map;

	mutex_lock(&regulator_list_mutex);
	map = regulator_find_supply_alias(*dev, *supply);
	if (map) {
		dev_dbg(*dev, "Mapping supply %s to %s,%s\n",
@@ -2066,6 +2067,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply)
		*dev = map->alias_dev;
		*supply = map->alias_supply;
	}
	mutex_unlock(&regulator_list_mutex);
}

static int regulator_match(struct device *dev, const void *data)
@@ -2618,22 +2620,26 @@ int regulator_register_supply_alias(struct device *dev, const char *id,
				    const char *alias_id)
{
	struct regulator_supply_alias *map;
	struct regulator_supply_alias *new_map;

	map = regulator_find_supply_alias(dev, id);
	if (map)
		return -EEXIST;

	map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL);
	if (!map)
	new_map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL);
	if (!new_map)
		return -ENOMEM;

	map->src_dev = dev;
	map->src_supply = id;
	map->alias_dev = alias_dev;
	map->alias_supply = alias_id;

	list_add(&map->list, &regulator_supply_alias_list);
	mutex_lock(&regulator_list_mutex);
	map = regulator_find_supply_alias(dev, id);
	if (map) {
		mutex_unlock(&regulator_list_mutex);
		kfree(new_map);
		return -EEXIST;
	}

	new_map->src_dev = dev;
	new_map->src_supply = id;
	new_map->alias_dev = alias_dev;
	new_map->alias_supply = alias_id;
	list_add(&new_map->list, &regulator_supply_alias_list);
	mutex_unlock(&regulator_list_mutex);
	pr_info("Adding alias for supply %s,%s -> %s,%s\n",
		id, dev_name(dev), alias_id, dev_name(alias_dev));

@@ -2653,11 +2659,13 @@ void regulator_unregister_supply_alias(struct device *dev, const char *id)
{
	struct regulator_supply_alias *map;

	mutex_lock(&regulator_list_mutex);
	map = regulator_find_supply_alias(dev, id);
	if (map) {
		list_del(&map->list);
		kfree(map);
	}
	mutex_unlock(&regulator_list_mutex);
}
EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias);