Skip to contentSkip to conversation

regulator: core: avoid memory access after freeing it

PATCH In review Laxman Dewangan 3 participants 8 messages Nov 6, 2012 – Nov 14, 2012 Voltage and current regulator framework

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 regulator: core: avoid memory access after freeing it

When regulator_register() failed due to non availability of supply then it exits through cleanup path. In this case it checks for supply and if it is valid then unregister the supply.

The change done by Charles Keepax:
regulator: core: Move regulator release to avoid deadlock

puts the release of supply regualtor after device_unregister(). The device_unregister() frees the rdev and hence accessing rdev member pointer after this calls gives incorrect pointer and cause the system to crash.

Adding the locked version of regulator_put() as __regulator_put_locked() so that it can be called in locked context also to avoid the deadlock to address the above issue.

---
 drivers/regulator/core.c |   30 ++++++++++++++++++------------
 1 files changed, 18 insertions(+), 12 deletions(-)
drivers/regulator/core.c +18 -12
index b4a425a..59280b6 100644
@@ -1382,23 +1382,16 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
 EXPORT_SYMBOL_GPL(regulator_get_exclusive);
 
 /**
- * regulator_put - "free" the regulator source
- * @regulator: regulator source
- *
- * Note: drivers must ensure that all regulator_enable calls made on this
- * regulator source are balanced by regulator_disable calls prior to calling
- * this function.
+ * Locked version of regulator_put
  */
-void regulator_put(struct regulator *regulator)
+static void __regulator_put_locked(struct regulator *regulator)
 {
 	struct regulator_dev *rdev;
 
 	if (regulator == NULL || IS_ERR(regulator))
 		return;
 
-	mutex_lock(&regulator_list_mutex);
 	rdev = regulator->rdev;
-
 	debugfs_remove_recursive(regulator->debugfs);
 
 	/* remove any sysfs entries */
@@ -1412,6 +1405,20 @@ void regulator_put(struct regulator *regulator)
 	rdev->exclusive = 0;
 
 	module_put(rdev->owner);
+}
+
+/**
+ * regulator_put - "free" the regulator source
+ * @regulator: regulator source
+ *
+ * Note: drivers must ensure that all regulator_enable calls made on this
+ * regulator source are balanced by regulator_disable calls prior to calling
+ * this function.
+ */
+void regulator_put(struct regulator *regulator)
+{
+	mutex_lock(&regulator_list_mutex);
+	__regulator_put_locked(regulator);
 	mutex_unlock(&regulator_list_mutex);
 }
 EXPORT_SYMBOL_GPL(regulator_put);
@@ -3453,11 +3460,10 @@ scrub:
 		gpio_free(rdev->ena_gpio);
 	kfree(rdev->constraints);
 wash:
+	if (rdev->supply)
+		__regulator_put_locked(rdev->supply);
 	device_unregister(&rdev->dev);
-
 	mutex_unlock(&regulator_list_mutex);
-	if (rdev->supply)
-		regulator_put(rdev->supply);
 
 	/* device core frees rdev */
 	rdev = ERR_PTR(ret);
Signature · 1 line
1.7.1.1
#1 lore raw
Quoted 2 lines · Laxman Dewangan

When regulator_register() failed due to non availability of
mutex_unlock(&regulator_list_mutex);

...

Quoted 6 lines

}
EXPORT_SYMBOL_GPL(regulator_put);

drivers/regulator/core.c +0 -0
@@ -3453,11 +3460,10 @@ scrub:
 		gpio_free(rdev->ena_gpio);
 	kfree(rdev->constraints);
 wash:

drivers/regulator/core.c scrub: · line 3469

Quoted 10 lines
drivers/regulator/core.c +2 -3
+	if (rdev->supply)
+		__regulator_put_locked(rdev->supply);
 	device_unregister(&rdev->dev);
-
 	mutex_unlock(&regulator_list_mutex);
-	if (rdev->supply)
-		regulator_put(rdev->supply);
 
 	/* device core frees rdev */
 	rdev = ERR_PTR(ret);

Here would it not make sense to go slightly further and basically revert my original change. This feels sensible as it keeps it at only one location where the mutex is unlocked and removes the second exit point I added. This would effectively make the code look like:

scrub:

	if (rdev->supply)
		__regulator_put_locked(rdev->supply);
	if (rdev->ena_gpio)
		gpio_free(rdev->ena_gpio);
	kfree(rdev->constraints);
	device_unregister(&rdev->dev);
	/* device core frees rdev */
	rdev = ERR_PTR(ret);
	goto out;

Otherwise the change looks great to me.

Quoted 3 lines
Signature · 1 line
1.7.1.1
#2 lore raw
Quoted 5 lines · Charles Keepax

Here would it not make sense to go slightly further and basically revert my original change. This feels sensible as it keeps it at only one location where the mutex is unlocked and removes the second exit point I added. This would effectively make the code look like:

Given the bug that was reported with your original change and the lack of any fix I'm actually just going to discard it anyway, just didn't get around to it yet.

A single exit path would be simpler and hence better.

#3 lore raw

PATCH 1/1 regulator: core: Add locked version of regulator_put to avoid deadlock

When regulator_register fails and exits through the scrub path the regulator_put function was called whilst holding the regulator_list_mutex, which is also locked from regulator_put, causing deadlock.

This patch adds a locked version of the regulator_put function which can be safely called whilst holding the mutex, replacing the aforementioned call.

---

I hope this isn't bad etiquette but I took the liberaty of fixing up the patch as per the comments and taking into account the removal of my original patch. Apologies if this is not the done thing, do let me know if you would rather submit a version yourself but I thought best to address the issue and get a fix in.

 drivers/regulator/core.c |   26 +++++++++++++++++---------
 1 files changed, 17 insertions(+), 9 deletions(-)
drivers/regulator/core.c +17 -9
index 5c4829c..854d18f 100644
@@ -1382,21 +1382,15 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
 EXPORT_SYMBOL_GPL(regulator_get_exclusive);
 
 /**
- * regulator_put - "free" the regulator source
- * @regulator: regulator source
- *
- * Note: drivers must ensure that all regulator_enable calls made on this
- * regulator source are balanced by regulator_disable calls prior to calling
- * this function.
+ * Locked version of regulator_put
  */
-void regulator_put(struct regulator *regulator)
+static void __regulator_put_locked(struct regulator *regulator)
 {
 	struct regulator_dev *rdev;
 
 	if (regulator == NULL || IS_ERR(regulator))
 		return;
 
-	mutex_lock(&regulator_list_mutex);
 	rdev = regulator->rdev;
 
 	debugfs_remove_recursive(regulator->debugfs);
@@ -1412,6 +1406,20 @@ void regulator_put(struct regulator *regulator)
 	rdev->exclusive = 0;
 
 	module_put(rdev->owner);
+}
+
+/**
+ * regulator_put - "free" the regulator source
+ * @regulator: regulator source
+ *
+ * Note: drivers must ensure that all regulator_enable calls made on this
+ * regulator source are balanced by regulator_disable calls prior to calling
+ * this function.
+ */
+void regulator_put(struct regulator *regulator)
+{
+	mutex_lock(&regulator_list_mutex);
+	__regulator_put_locked(regulator);
 	mutex_unlock(&regulator_list_mutex);
 }
 EXPORT_SYMBOL_GPL(regulator_put);
@@ -3445,7 +3453,7 @@ unset_supplies:
 
 scrub:
 	if (rdev->supply)
-		regulator_put(rdev->supply);
+		__regulator_put_locked(rdev->supply);
 	if (rdev->ena_gpio)
 		gpio_free(rdev->ena_gpio);
 	kfree(rdev->constraints);
Signature · 1 line
1.7.2.5
author Acked-by on v1 1/1
#4 lore raw

Re: [PATCH] regulator: core: Add locked version of regulator_put to avoid deadlock

Quoted 11 lines · Charles Keepax

When regulator_register fails and exits through the scrub path the regulator_put function was called whilst holding the regulator_list_mutex, which is also locked from regulator_put, causing deadlock.

This patch adds a locked version of the regulator_put function which can be safely called whilst holding the mutex, replacing the aforementioned call.

Quoted 7 lines

---

I hope this isn't bad etiquette but I took the liberaty of fixing up the patch as per the comments and taking into account the removal of my original patch. Apologies if this is not the done thing, do let me know if you would rather submit a version yourself but I thought best to address the issue and get a fix in.

This looks fine.

#5 lore raw

Re: [PATCH] regulator: core: Add locked version of regulator_put to avoid deadlock

In reply to Charles Keepax

drivers/regulator/core.c struct regulator *regulator_get_exclusive(struct device *dev, const char *id) · line 1387

Quoted 2 lines · Charles Keepax
drivers/regulator/core.c +1 -1
-void regulator_put(struct regulator *regulator)
+static void __regulator_put_locked(struct regulator *regulator)

Please do follow the coding style of the rest of the code; nothing else is using __ for private functions.

Please also clarify the changelog - you're not adding a new API here, you're fiddling about with the internals. Obviously regulator_put() is always locked, you're just rearranging code inside the core here.

#6 lore raw

Re: [PATCH v2] regulator: core: Avoid deadlock when regulator_register fails

When regulator_register fails and exits through the scrub path the regulator_put function was called whilst holding the regulator_list_mutex, causing deadlock.

This patch adds a private version of the regulator_put function which can be safely called whilst holding the mutex, replacing the aforementioned call.

---
 drivers/regulator/core.c |   28 +++++++++++++++++-----------
 1 files changed, 17 insertions(+), 11 deletions(-)
drivers/regulator/core.c +17 -11
index 1a35251..390e173 100644
@@ -1381,22 +1381,14 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
 }
 EXPORT_SYMBOL_GPL(regulator_get_exclusive);
 
-/**
- * regulator_put - "free" the regulator source
- * @regulator: regulator source
- *
- * Note: drivers must ensure that all regulator_enable calls made on this
- * regulator source are balanced by regulator_disable calls prior to calling
- * this function.
- */
-void regulator_put(struct regulator *regulator)
+/* Locks held by regulator_put() */
+static void _regulator_put(struct regulator *regulator)
 {
 	struct regulator_dev *rdev;
 
 	if (regulator == NULL || IS_ERR(regulator))
 		return;
 
-	mutex_lock(&regulator_list_mutex);
 	rdev = regulator->rdev;
 
 	debugfs_remove_recursive(regulator->debugfs);
@@ -1412,6 +1404,20 @@ void regulator_put(struct regulator *regulator)
 	rdev->exclusive = 0;
 
 	module_put(rdev->owner);
+}
+
+/**
+ * regulator_put - "free" the regulator source
+ * @regulator: regulator source
+ *
+ * Note: drivers must ensure that all regulator_enable calls made on this
+ * regulator source are balanced by regulator_disable calls prior to calling
+ * this function.
+ */
+void regulator_put(struct regulator *regulator)
+{
+	mutex_lock(&regulator_list_mutex);
+	_regulator_put(regulator);
 	mutex_unlock(&regulator_list_mutex);
 }
 EXPORT_SYMBOL_GPL(regulator_put);
@@ -3450,7 +3456,7 @@ unset_supplies:
 
 scrub:
 	if (rdev->supply)
-		regulator_put(rdev->supply);
+		_regulator_put(rdev->supply);
 	if (rdev->ena_gpio)
 		gpio_free(rdev->ena_gpio);
 	kfree(rdev->constraints);
Signature · 1 line
1.7.2.5
Applied
#7 lore raw

Re: [PATCH v2] regulator: core: Avoid deadlock when regulator_register fails

Quoted 3 lines · Charles Keepax

When regulator_register fails and exits through the scrub path the regulator_put function was called whilst holding the regulator_list_mutex, causing deadlock.

Applied, thanks.

Quoted 1 line
Cc Laxman Dewangan <[email protected]>

Please avoid doing this. I know a lot of people do but it makes for hand editing of the commit log to remove it.