mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
hwmon: (ltc4282) Avoid overflow in maximum power calculation
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 <sashiko-bot@kernel.org>
Fixes: cbc29538db ("hwmon: Add driver for LTC4282")
Cc: Nuno Sa <nuno.sa@analog.com>
Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
fddb5ceaf9
commit
edd11a9433
|
|
@ -137,7 +137,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;
|
||||
|
|
@ -613,13 +613,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,
|
||||
|
|
@ -629,7 +628,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;
|
||||
|
|
@ -1222,7 +1221,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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user