power: supply: core: Fix boundary conditions in interpolation
authorDorian Rudolph <mail@dorianrudolph.com>
Sat, 14 May 2022 15:23:40 +0000 (17:23 +0200)
committerSebastian Reichel <sre@kernel.org>
Thu, 9 Jun 2022 19:08:38 +0000 (21:08 +0200)
The functions power_supply_temp2resist_simple and power_supply_ocv2cap_simple
handle boundary conditions incorrectly.
The change was introduced in a4585ba2050f460f749bbaf2b67bd56c41e30283
("power: supply: core: Use library interpolation").
There are two issues: First, the lines "high = i - 1" and "high = i" in ocv2cap
have the wrong order compared to temp2resist. As a consequence, ocv2cap
sets high=-1 if ocv>table[0].ocv, which causes an out-of-bounds read.
Second, the logic of temp2resist is also not correct.
Consider the case table[] = {{20, 100}, {10, 80}, {0, 60}}.
For temp=5, we expect a resistance of 70% by interpolation.
However, temp2resist sets high=low=2 and returns 60.

Cc: stable@vger.kernel.org
Signed-off-by: Dorian Rudolph <mail@dorianrudolph.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Fixes: a4585ba2050f ("power: supply: core: Use library interpolation")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
drivers/power/supply/power_supply_core.c

index fad5890c899e2d976dd68383c8fcae66e43da2f8..470253c337c7306c0e8b430df2cd28b580247d26 100644 (file)
@@ -846,17 +846,17 @@ int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *t
 {
        int i, high, low;
 
-       /* Break loop at table_len - 1 because that is the highest index */
-       for (i = 0; i < table_len - 1; i++)
+       for (i = 0; i < table_len; i++)
                if (temp > table[i].temp)
                        break;
 
        /* The library function will deal with high == low */
-       if ((i == 0) || (i == (table_len - 1)))
-               high = i;
+       if (i == 0)
+               high = low = i;
+       else if (i == table_len)
+               high = low = i - 1;
        else
-               high = i - 1;
-       low = i;
+               high = (low = i) - 1;
 
        return fixp_linear_interpolate(table[low].temp,
                                       table[low].resistance,
@@ -958,17 +958,17 @@ int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
 {
        int i, high, low;
 
-       /* Break loop at table_len - 1 because that is the highest index */
-       for (i = 0; i < table_len - 1; i++)
+       for (i = 0; i < table_len; i++)
                if (ocv > table[i].ocv)
                        break;
 
        /* The library function will deal with high == low */
-       if ((i == 0) || (i == (table_len - 1)))
-               high = i - 1;
+       if (i == 0)
+               high = low = i;
+       else if (i == table_len)
+               high = low = i - 1;
        else
-               high = i; /* i.e. i == 0 */
-       low = i;
+               high = (low = i) - 1;
 
        return fixp_linear_interpolate(table[low].ocv,
                                       table[low].capacity,