[PATCH 7.1 118/271] hwmon: (ltc4282) Avoid overflow in maximum power calculation
Greg Kroah-Hartman <[email protected]>
| Newsgroups | dev.linux.lists.patches,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
7.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Guenter Roeck <[email protected]> [ Upstream commit edd11a94335747423569500a194c6eaa915f2963 ] During device initialization in ltc4282_set_max_limits(), the calculation of the maximum power limit can suffer from a 32-bit integer overflow. static int ltc4282_set_max_limits(struct ltc4282_state *st) { ... st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out; ... } The result of DIV_ROUND_CLOSEST() evaluates to a 32-bit unsigned integer on 32-bit architectures. This result is then multiplied by st->vfs_out, which is a 16-bit unsigned integer. According to C promotion rules, since both operands are 32-bit or smaller, the multiplication is performed in 32-bit precision. If the device is configured with a low sense resistor value via the device tree (for example, 100 nano-ohms, resulting in st->rsense = 1) and the voltage is high, the division result can reach 343,750,000 and st->vfs_out can be 33,280. The product of these values is approximately 11.44 trillion, which exceeds the maximum capacity of a 32-bit integer and overflows before being stored in st->power_max. This overflow causes a truncated value to be assigned to st->power_max and written to the hardware limit register. An incorrect maximum power limit can trigger spurious power-bad faults or alarms, which may lead to the shutdown of the monitored power rail. Avoid the problem by calculating and storing the maximum power using 64-bit variables. Reported-by: Sashiko <[email protected]> Fixes: cbc29538dbf7d ("hwmon: Add driver for LTC4282") Cc: Nuno Sa <[email protected]> Reviewed-by: Nuno Sá <[email protected]> Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Sasha Levin <[email protected]> --- drivers/hwmon/ltc4282.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index bdbf370233d71..9ccad99f42e7f 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -138,7 +138,7 @@ struct ltc4282_state { */ struct ltc4282_cache in0_1_cache[LTC4282_CHAN_VGPIO]; u32 vsense_max; - long power_max; + s64 power_max; u32 rsense; u16 vdd; u16 vfs_out; @@ -614,13 +614,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type, } static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg, - long val) + s64 val) { u32 power; u64 temp; - if (val > st->power_max) - val = st->power_max; + val = clamp(val, 0, st->power_max); temp = val * int_pow(U8_MAX, 2) * st->rsense; power = DIV64_U64_ROUND_CLOSEST(temp, @@ -630,7 +629,7 @@ static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg, } static int ltc4282_write_power_word(const struct ltc4282_state *st, u32 reg, - long val) + u64 val) { u64 temp = int_pow(U16_MAX, 2) * st->rsense, temp_2; __be16 __raw; @@ -1223,7 +1222,8 @@ static int ltc4282_set_max_limits(struct ltc4282_state *st) return ret; /* Power is given by ISENSE * Vout. */ - st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out; + st->power_max = DIV_ROUND_CLOSEST_ULL((u64)st->vsense_max * DECA * MILLI, + st->rsense) * st->vfs_out; ret = ltc4282_write_power_byte(st, LTC4282_POWER_MAX, st->power_max); if (ret) return ret; -- 2.53.0