[PATCH 1/3] hwmon: (adm1031) Convert macros to functions to avoid TOCTOU

From: Gui-Dong Han

Date: Thu Apr 16 2026 - 05:21:52 EST


The macros TEMP_OFFSET_FROM_REG, FAN_FROM_REG, and
AUTO_TEMP_MAX_FROM_REG evaluate their arguments multiple times. When
used in lockless code accessing shared driver data, this can cause
Time-of-Check to Time-of-Use (TOCTOU) races. In the case of
FAN_FROM_REG, it can also result in a divide-by-zero error.

Convert those macros to static functions so that their arguments are
always evaluated only once.

Check the remaining conversion macros in the driver as well. Keep them
unchanged because they either do not evaluate arguments multiple times
or are only used from locked code paths.

Link: https://lore.kernel.org/linux-hwmon/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@xxxxxxxxxxxxxx/
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 49dc9efed05a ("hwmon: (adm1031) Add sysfs files for temperature offsets")
Signed-off-by: Gui-Dong Han <hanguidong02@xxxxxxxxx>
---
While learning the hwmon driver code, I found a few more potential
TOCTOU problems in drivers still using the older non-_with_info() APIs.
Fix them.
---
drivers/hwmon/adm1031.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 343118532cdb..0551f815233d 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -203,16 +203,24 @@ static struct adm1031_data *adm1031_update_device(struct device *dev)
#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)

#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f)
-#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \
- (val) | 0x70 : (val))

-#define FAN_FROM_REG(reg, div) ((reg) ? \
- (11250 * 60) / ((reg) * (div)) : 0)
+static int temp_offset_from_reg(int val)
+{
+ return TEMP_FROM_REG(val < 0 ? val | 0x70 : val);
+}
+
+static int fan_from_reg(int reg, int div)
+{
+ if (!reg)
+ return 0;
+
+ return (11250 * 60) / (reg * div);
+}

static int FAN_TO_REG(int reg, int div)
{
int tmp;
- tmp = FAN_FROM_REG(clamp_val(reg, 0, 65535), div);
+ tmp = fan_from_reg(clamp_val(reg, 0, 65535), div);
return tmp > 255 ? 255 : tmp;
}

@@ -235,9 +243,10 @@ static int FAN_TO_REG(int reg, int div)
#define AUTO_TEMP_OFF_FROM_REG(reg) \
(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)

-#define AUTO_TEMP_MAX_FROM_REG(reg) \
- (AUTO_TEMP_RANGE_FROM_REG(reg) + \
- AUTO_TEMP_MIN_FROM_REG(reg))
+static int auto_temp_max_from_reg(int reg)
+{
+ return AUTO_TEMP_RANGE_FROM_REG(reg) + AUTO_TEMP_MIN_FROM_REG(reg);
+}

static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
{
@@ -426,7 +435,7 @@ static ssize_t auto_temp_max_show(struct device *dev,
int nr = to_sensor_dev_attr(attr)->index;
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
- AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
+ auto_temp_max_from_reg(data->auto_temp[nr]));
}
static ssize_t
auto_temp_max_store(struct device *dev, struct device_attribute *attr,
@@ -559,7 +568,7 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
struct adm1031_data *data = adm1031_update_device(dev);
int value;

- value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
+ value = trust_fan_readings(data, nr) ? fan_from_reg(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
return sprintf(buf, "%d\n", value);
}
@@ -577,7 +586,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
int nr = to_sensor_dev_attr(attr)->index;
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
- FAN_FROM_REG(data->fan_min[nr],
+ fan_from_reg(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
static ssize_t fan_min_store(struct device *dev,
@@ -679,7 +688,7 @@ static ssize_t temp_offset_show(struct device *dev,
int nr = to_sensor_dev_attr(attr)->index;
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
- TEMP_OFFSET_FROM_REG(data->temp_offset[nr]));
+ temp_offset_from_reg(data->temp_offset[nr]));
}
static ssize_t temp_min_show(struct device *dev,
struct device_attribute *attr, char *buf)
--
2.43.0