]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
authorLuiz Angelo Daros de Luca <luizluca@gmail.com>
Tue, 28 Jul 2026 00:22:23 +0000 (21:22 -0300)
committerGuenter Roeck <linux@roeck-us.net>
Tue, 28 Jul 2026 00:58:05 +0000 (17:58 -0700)
If the fan data becomes 0 between the FAN_DATA_VALID() check and the
FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash
due to a race with a concurrent update of the cached fan value.

Fix a TOCTOU issue by reading fan data once.

Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/
Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API")
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/adt7470.c

index 1fbca4869b7b6507bcf96a564d0d3cd6b93afb93..772d2a409bb5c6a4f8cfb80b661a7cc9234cbb4a 100644 (file)
@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
 static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
 {
        struct adt7470_data *data = adt7470_update_device(dev);
+       u16 fan_data;
 
        if (IS_ERR(data))
                return PTR_ERR(data);
 
        switch (attr) {
        case hwmon_fan_input:
-               if (FAN_DATA_VALID(data->fan[channel]))
-                       *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
-               else
-                       *val = 0;
+               fan_data = READ_ONCE(data->fan[channel]);
                break;
        case hwmon_fan_min:
-               if (FAN_DATA_VALID(data->fan_min[channel]))
-                       *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
-               else
-                       *val = 0;
+               fan_data = READ_ONCE(data->fan_min[channel]);
                break;
        case hwmon_fan_max:
-               if (FAN_DATA_VALID(data->fan_max[channel]))
-                       *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
-               else
-                       *val = 0;
+               fan_data = READ_ONCE(data->fan_max[channel]);
                break;
        case hwmon_fan_alarm:
                *val = !!(data->alarm & FAN_ALARM_BIT(channel));
-               break;
+               return 0;
        default:
                return -EOPNOTSUPP;
        }
 
+       if (FAN_DATA_VALID(fan_data))
+               *val = FAN_PERIOD_TO_RPM(fan_data);
+       else
+               *val = 0;
+
        return 0;
 }