regulator: max77802: Bounds check regulator id against opmode
authorKees Cook <keescook@chromium.org>
Fri, 27 Jan 2023 22:52:07 +0000 (14:52 -0800)
committerMark Brown <broonie@kernel.org>
Sat, 28 Jan 2023 11:27:57 +0000 (11:27 +0000)
Explicitly bounds-check the id before accessing the opmode array. Seen
with GCC 13:

../drivers/regulator/max77802-regulator.c: In function 'max77802_enable':
../drivers/regulator/max77802-regulator.c:217:29: warning: array subscript [0, 41] is outside array bounds of 'unsigned int[42]' [-Warray-bounds=]
  217 |         if (max77802->opmode[id] == MAX77802_OFF_PWRREQ)
      |             ~~~~~~~~~~~~~~~~^~~~
../drivers/regulator/max77802-regulator.c:62:22: note: while referencing 'opmode'
   62 |         unsigned int opmode[MAX77802_REG_MAX];
      |                      ^~~~~~

Cc: Javier Martinez Canillas <javier@dowhile0.org>
Cc: Liam Girdwood <lgirdwood@gmail.com>
Cc: Mark Brown <broonie@kernel.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://lore.kernel.org/r/20230127225203.never.864-kees@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/max77802-regulator.c

index 21e0eb0..befe5f3 100644 (file)
@@ -94,9 +94,11 @@ static int max77802_set_suspend_disable(struct regulator_dev *rdev)
 {
        unsigned int val = MAX77802_OFF_PWRREQ;
        struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
+       unsigned int id = rdev_get_id(rdev);
        int shift = max77802_get_opmode_shift(id);
 
+       if (WARN_ON_ONCE(id >= ARRAY_SIZE(max77802->opmode)))
+               return -EINVAL;
        max77802->opmode[id] = val;
        return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                  rdev->desc->enable_mask, val << shift);
@@ -110,7 +112,7 @@ static int max77802_set_suspend_disable(struct regulator_dev *rdev)
 static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode)
 {
        struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
+       unsigned int id = rdev_get_id(rdev);
        unsigned int val;
        int shift = max77802_get_opmode_shift(id);
 
@@ -127,6 +129,9 @@ static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode)
                return -EINVAL;
        }
 
+       if (WARN_ON_ONCE(id >= ARRAY_SIZE(max77802->opmode)))
+               return -EINVAL;
+
        max77802->opmode[id] = val;
        return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                                  rdev->desc->enable_mask, val << shift);
@@ -135,8 +140,10 @@ static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode)
 static unsigned max77802_get_mode(struct regulator_dev *rdev)
 {
        struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
+       unsigned int id = rdev_get_id(rdev);
 
+       if (WARN_ON_ONCE(id >= ARRAY_SIZE(max77802->opmode)))
+               return -EINVAL;
        return max77802_map_mode(max77802->opmode[id]);
 }
 
@@ -160,10 +167,13 @@ static int max77802_set_suspend_mode(struct regulator_dev *rdev,
                                     unsigned int mode)
 {
        struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
+       unsigned int id = rdev_get_id(rdev);
        unsigned int val;
        int shift = max77802_get_opmode_shift(id);
 
+       if (WARN_ON_ONCE(id >= ARRAY_SIZE(max77802->opmode)))
+               return -EINVAL;
+
        /*
         * If the regulator has been disabled for suspend
         * then is invalid to try setting a suspend mode.
@@ -209,9 +219,11 @@ static int max77802_set_suspend_mode(struct regulator_dev *rdev,
 static int max77802_enable(struct regulator_dev *rdev)
 {
        struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
+       unsigned int id = rdev_get_id(rdev);
        int shift = max77802_get_opmode_shift(id);
 
+       if (WARN_ON_ONCE(id >= ARRAY_SIZE(max77802->opmode)))
+               return -EINVAL;
        if (max77802->opmode[id] == MAX77802_OFF_PWRREQ)
                max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
 
@@ -495,7 +507,7 @@ static int max77802_pmic_probe(struct platform_device *pdev)
 
        for (i = 0; i < MAX77802_REG_MAX; i++) {
                struct regulator_dev *rdev;
-               int id = regulators[i].id;
+               unsigned int id = regulators[i].id;
                int shift = max77802_get_opmode_shift(id);
                int ret;
 
@@ -513,10 +525,12 @@ static int max77802_pmic_probe(struct platform_device *pdev)
                 * the hardware reports OFF as the regulator operating mode.
                 * Default to operating mode NORMAL in that case.
                 */
-               if (val == MAX77802_STATUS_OFF)
-                       max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
-               else
-                       max77802->opmode[id] = val;
+               if (id < ARRAY_SIZE(max77802->opmode)) {
+                       if (val == MAX77802_STATUS_OFF)
+                               max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+                       else
+                               max77802->opmode[id] = val;
+               }
 
                rdev = devm_regulator_register(&pdev->dev,
                                               &regulators[i], &config);