| From 83b0302d347a49f951e904184afe57ac3723476e Mon Sep 17 00:00:00 2001 |
| From: Ashay Jaiswal <ashayj@codeaurora.org> |
| Date: Thu, 8 Jan 2015 18:54:25 +0530 |
| Subject: regulator: core: fix race condition in regulator_put() |
| |
| From: Ashay Jaiswal <ashayj@codeaurora.org> |
| |
| commit 83b0302d347a49f951e904184afe57ac3723476e upstream. |
| |
| The regulator framework maintains a list of consumer regulators |
| for a regulator device and protects it from concurrent access using |
| the regulator device's mutex lock. |
| |
| In the case of regulator_put() the consumer is removed and regulator |
| device's parameters are updated without holding the regulator device's |
| mutex. This would lead to a race condition between the regulator_put() |
| and any function which traverses the consumer list or modifies regulator |
| device's parameters. |
| Fix this race condition by holding the regulator device's mutex in case |
| of regulator_put. |
| |
| Signed-off-by: Ashay Jaiswal <ashayj@codeaurora.org> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/regulator/core.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/regulator/core.c |
| +++ b/drivers/regulator/core.c |
| @@ -1488,7 +1488,7 @@ struct regulator *regulator_get_optional |
| } |
| EXPORT_SYMBOL_GPL(regulator_get_optional); |
| |
| -/* Locks held by regulator_put() */ |
| +/* regulator_list_mutex lock held by regulator_put() */ |
| static void _regulator_put(struct regulator *regulator) |
| { |
| struct regulator_dev *rdev; |
| @@ -1503,12 +1503,14 @@ static void _regulator_put(struct regula |
| /* remove any sysfs entries */ |
| if (regulator->dev) |
| sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); |
| + mutex_lock(&rdev->mutex); |
| kfree(regulator->supply_name); |
| list_del(®ulator->list); |
| kfree(regulator); |
| |
| rdev->open_count--; |
| rdev->exclusive = 0; |
| + mutex_unlock(&rdev->mutex); |
| |
| module_put(rdev->owner); |
| } |