| From 0a7416f94707c60b9f66b01c0a505b7e41375f3a Mon Sep 17 00:00:00 2001 |
| From: Dmitry Osipenko <digetx@gmail.com> |
| Date: Mon, 31 Aug 2020 23:43:35 +0300 |
| Subject: regulator: core: Fix slab-out-of-bounds in regulator_unlock_recursive() |
| |
| From: Dmitry Osipenko <digetx@gmail.com> |
| |
| commit 0a7416f94707c60b9f66b01c0a505b7e41375f3a upstream. |
| |
| The recent commit 7d8196641ee1 ("regulator: Remove pointer table |
| overallocation") changed the size of coupled_rdevs and now KASAN is able |
| to detect slab-out-of-bounds problem in regulator_unlock_recursive(), |
| which is a legit problem caused by a typo in the code. The recursive |
| unlock function uses n_coupled value of a parent regulator for unlocking |
| supply regulator, while supply's n_coupled should be used. In practice |
| problem may only affect platforms that use coupled regulators. |
| |
| Cc: stable@vger.kernel.org # 5.0+ |
| Fixes: f8702f9e4aa7 ("regulator: core: Use ww_mutex for regulators locking") |
| Signed-off-by: Dmitry Osipenko <digetx@gmail.com> |
| Link: https://lore.kernel.org/r/20200831204335.19489-1-digetx@gmail.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/regulator/core.c | 15 +++++++++------ |
| 1 file changed, 9 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/regulator/core.c |
| +++ b/drivers/regulator/core.c |
| @@ -235,8 +235,8 @@ static bool regulator_supply_is_couple(s |
| static void regulator_unlock_recursive(struct regulator_dev *rdev, |
| unsigned int n_coupled) |
| { |
| - struct regulator_dev *c_rdev; |
| - int i; |
| + struct regulator_dev *c_rdev, *supply_rdev; |
| + int i, supply_n_coupled; |
| |
| for (i = n_coupled; i > 0; i--) { |
| c_rdev = rdev->coupling_desc.coupled_rdevs[i - 1]; |
| @@ -244,10 +244,13 @@ static void regulator_unlock_recursive(s |
| if (!c_rdev) |
| continue; |
| |
| - if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) |
| - regulator_unlock_recursive( |
| - c_rdev->supply->rdev, |
| - c_rdev->coupling_desc.n_coupled); |
| + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) { |
| + supply_rdev = c_rdev->supply->rdev; |
| + supply_n_coupled = supply_rdev->coupling_desc.n_coupled; |
| + |
| + regulator_unlock_recursive(supply_rdev, |
| + supply_n_coupled); |
| + } |
| |
| regulator_unlock(c_rdev); |
| } |