OSDN Git Service

regulator: core: Fix slab-out-of-bounds in regulator_unlock_recursive()
authorDmitry Osipenko <digetx@gmail.com>
Mon, 31 Aug 2020 20:43:35 +0000 (23:43 +0300)
committerMark Brown <broonie@kernel.org>
Tue, 1 Sep 2020 12:26:28 +0000 (13:26 +0100)
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>
drivers/regulator/core.c

index 6789d1e..0e76459 100644 (file)
@@ -236,8 +236,8 @@ static bool regulator_supply_is_couple(struct regulator_dev *rdev)
 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];
@@ -245,10 +245,13 @@ static void regulator_unlock_recursive(struct regulator_dev *rdev,
                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);
        }