lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20251120041331.1917570-1-hanguidong02@gmail.com>
Date: Thu, 20 Nov 2025 12:13:31 +0800
From: Gui-Dong Han <hanguidong02@...il.com>
To: juergh@...ton.me,
	linux@...ck-us.net
Cc: linux-hwmon@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	baijiaju1990@...il.com,
	Gui-Dong Han <hanguidong02@...il.com>
Subject: [PATCH] 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@...il.com>
---
Based on the discussion in the link, I will submit a series of patches to
address TOCTOU issues in the hwmon subsystem by converting macros to
functions or adjusting locking where appropriate.
---
 drivers/hwmon/vt1211.c | 53 ++++++++++++++++++++++++++++--------------
 1 file 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]));
 }
 
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ