[PATCH 2/3] hwmon: (adm1031) Hold lock while reading cached data
From: Gui-Dong Han
Date: Thu Apr 16 2026 - 05:22:26 EST
The functions fan_show(), fan_min_show(), and temp_show() read shared
cached values multiple times without holding data->update_lock.
fan_auto_channel_store() also reads data->conf1 before taking the lock.
Those cached values can change in adm1031_update_device(), resulting in
inconsistent snapshots and TOCTOU races.
Hold data->update_lock across those reads so that the cached values stay
stable while the results are calculated.
Check the remaining functions in the driver as well. Keep them unchanged
because they either do not access shared cached values multiple times
or already do so under lock.
Link: https://lore.kernel.org/linux-hwmon/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@xxxxxxxxxxxxxx/
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
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 | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 0551f815233d..887fba9ea149 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -350,9 +350,8 @@ fan_auto_channel_store(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- old_fan_mode = data->conf1;
-
mutex_lock(&data->update_lock);
+ old_fan_mode = data->conf1;
ret = get_fan_auto_nearest(data, nr, val, data->conf1);
if (ret < 0) {
@@ -568,8 +567,10 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
struct adm1031_data *data = adm1031_update_device(dev);
int value;
+ mutex_lock(&data->update_lock);
value = trust_fan_readings(data, nr) ? fan_from_reg(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
+ mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", value);
}
@@ -585,9 +586,13 @@ 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_DIV_FROM_REG(data->fan_div[nr])));
+ int value;
+
+ mutex_lock(&data->update_lock);
+ value = fan_from_reg(data->fan_min[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr]));
+ mutex_unlock(&data->update_lock);
+ return sprintf(buf, "%d\n", value);
}
static ssize_t fan_min_store(struct device *dev,
struct device_attribute *attr, const char *buf,
@@ -677,10 +682,15 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
int nr = to_sensor_dev_attr(attr)->index;
struct adm1031_data *data = adm1031_update_device(dev);
int ext;
+ int temp;
+
+ mutex_lock(&data->update_lock);
ext = nr == 0 ?
((data->ext_temp[nr] >> 6) & 0x3) * 2 :
(((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
- return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
+ temp = TEMP_FROM_REG_EXT(data->temp[nr], ext);
+ mutex_unlock(&data->update_lock);
+ return sprintf(buf, "%d\n", temp);
}
static ssize_t temp_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
--
2.43.0