From: Gui-Dong Han <hanguidong02@gmail.com>
To: Guenter Roeck <linux@roeck-us.net>
Cc: linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, Gui-Dong Han <hanguidong02@gmail.com>
Subject: [PATCH 1/3] hwmon: (adm1031) Convert macros to functions to avoid TOCTOU
Date: Thu, 16 Apr 2026 17:17:52 +0800 [thread overview]
Message-ID: <20260416091754.310-1-hanguidong02@gmail.com> (raw)
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@mail.gmail.com/
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 49dc9efed05a ("hwmon: (adm1031) Add sysfs files for temperature offsets")
Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
---
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
next reply other threads:[~2026-04-16 9:18 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-16 9:17 Gui-Dong Han [this message]
2026-04-16 9:17 ` [PATCH 2/3] hwmon: (adm1031) Hold lock while reading cached data Gui-Dong Han
2026-04-16 11:57 ` sashiko-bot
2026-04-16 13:02 ` Gui-Dong Han
2026-04-16 9:17 ` [PATCH 3/3] hwmon: (adm1031) Serialize update rate changes Gui-Dong Han
2026-04-16 12:21 ` sashiko-bot
2026-04-16 13:05 ` Gui-Dong Han
2026-04-16 14:05 ` Guenter Roeck
2026-04-16 14:32 ` Gui-Dong Han
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260416091754.310-1-hanguidong02@gmail.com \
--to=hanguidong02@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox