diff options
| author | Gui-Dong Han <hanguidong02@gmail.com> | 2025-11-20 12:13:31 +0800 |
|---|---|---|
| committer | Guenter Roeck <linux@roeck-us.net> | 2025-11-21 07:50:18 -0800 |
| commit | 96710819d0ce6d1e42be751f6bbed18b6e9ea6d1 (patch) | |
| tree | 0c9d65a92484de99e095f0eb747c0193fd9e4753 | |
| parent | d56933e74d0d691dcd642876feda0b30f397e2ad (diff) | |
hwmon: (vt1211) Convert macros to functions to avoid TOCTOU
The macros IN_FROM_REG, TEMP_FROM_REG, and RPM_FROM_REG evaluate their
arguments multiple times. These macros are used in lockless show functions
involving shared driver data, leading to Time-of-Check to Time-of-Use race
conditions.
For example, RPM_FROM_REG checks if a value is 0 or 255, and then uses it
in a division. If the value is modified by another thread to 0 after the
check but before the division, it causes a divide-by-zero error.
Convert these macros to static functions. This guarantees that arguments
are evaluated only once (pass-by-value), fixing the race conditions.
Adhere to the principle of minimal changes by only converting the specific
macros involved in these lockless contexts.
Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/
Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
Link: https://lore.kernel.org/r/20251120041331.1917570-1-hanguidong02@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
| -rw-r--r-- | drivers/hwmon/vt1211.c | 53 |
1 files changed, 35 insertions, 18 deletions
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 386edea6b69e..1e52cabd6e24 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -142,9 +142,15 @@ struct vt1211_data { * in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the * driver according to the VT1211 BIOS porting guide */ -#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \ - (((reg) - 3) * 15882 + 479) / 958 : \ - (((reg) - 3) * 10000 + 479) / 958) +static int in_from_reg(int ix, int reg) +{ + if (reg < 3) + return 0; + if (ix == 5) + return ((reg - 3) * 15882 + 479) / 958; + return ((reg - 3) * 10000 + 479) / 958; +} + #define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \ ((val) * 958 + 7941) / 15882 + 3 : \ ((val) * 958 + 5000) / 10000 + 3, 0, 255)) @@ -156,10 +162,15 @@ struct vt1211_data { * temp3-7 are thermistor based so the driver returns the voltage measured at * the pin (range 0V - 2.2V). */ -#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \ - (ix) == 1 ? (reg) < 51 ? 0 : \ - ((reg) - 51) * 1000 : \ - ((253 - (reg)) * 2200 + 105) / 210) +static int temp_from_reg(int ix, int reg) +{ + if (ix == 0) + return reg * 1000; + if (ix == 1) + return reg < 51 ? 0 : (reg - 51) * 1000; + return ((253 - reg) * 2200 + 105) / 210; +} + #define TEMP_TO_REG(ix, val) clamp_val( \ ((ix) == 0 ? ((val) + 500) / 1000 : \ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \ @@ -167,8 +178,14 @@ struct vt1211_data { #define DIV_FROM_REG(reg) (1 << (reg)) -#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \ - 1310720 / (reg) / DIV_FROM_REG(div)) +static int rpm_from_reg(int reg, int div) +{ + if (reg == 0 || reg == 255) + return 0; + + return 1310720 / reg / DIV_FROM_REG(div); +} + #define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \ clamp_val((1310720 / (val) / \ DIV_FROM_REG(div)), 1, 254)) @@ -343,13 +360,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_IN_INPUT: - res = IN_FROM_REG(ix, data->in[ix]); + res = in_from_reg(ix, data->in[ix]); break; case SHOW_SET_IN_MIN: - res = IN_FROM_REG(ix, data->in_min[ix]); + res = in_from_reg(ix, data->in_min[ix]); break; case SHOW_SET_IN_MAX: - res = IN_FROM_REG(ix, data->in_max[ix]); + res = in_from_reg(ix, data->in_max[ix]); break; case SHOW_IN_ALARM: res = (data->alarms >> bitalarmin[ix]) & 1; @@ -417,13 +434,13 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_TEMP_INPUT: - res = TEMP_FROM_REG(ix, data->temp[ix]); + res = temp_from_reg(ix, data->temp[ix]); break; case SHOW_SET_TEMP_MAX: - res = TEMP_FROM_REG(ix, data->temp_max[ix]); + res = temp_from_reg(ix, data->temp_max[ix]); break; case SHOW_SET_TEMP_MAX_HYST: - res = TEMP_FROM_REG(ix, data->temp_hyst[ix]); + res = temp_from_reg(ix, data->temp_hyst[ix]); break; case SHOW_TEMP_ALARM: res = (data->alarms >> bitalarmtemp[ix]) & 1; @@ -493,10 +510,10 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_FAN_INPUT: - res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]); + res = rpm_from_reg(data->fan[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_MIN: - res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]); + res = rpm_from_reg(data->fan_min[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_DIV: res = DIV_FROM_REG(data->fan_div[ix]); @@ -751,7 +768,7 @@ static ssize_t show_pwm_auto_point_temp(struct device *dev, int ix = sensor_attr_2->index; int ap = sensor_attr_2->nr; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7, + return sprintf(buf, "%d\n", temp_from_reg(data->pwm_ctl[ix] & 7, data->pwm_auto_temp[ap])); } |