On Sat, 29 Nov 2025 08:33:42 +0800 Gui-Dong Han hanguidong02@gmail.com wrote:
On Sat, Nov 29, 2025 at 3:37 AM david laight david.laight@runbox.com wrote:
On Fri, 28 Nov 2025 20:38:16 +0800 Gui-Dong Han hanguidong02@gmail.com wrote:
The macros FAN_FROM_REG and TEMP_FROM_REG evaluate their arguments multiple times. When used in lockless contexts involving shared driver data, this causes Time-of-Check to Time-of-Use (TOCTOU) race conditions.
Convert the macros to static functions. This guarantees that arguments are evaluated only once (pass-by-value), preventing the race conditions.
Adhere to the principle of minimal changes by only converting macros that evaluate arguments multiple times and are used in lockless contexts.
Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8... Fixes: 85f03bccd6e0 ("hwmon: Add support for Winbond W83L786NG/NR") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han hanguidong02@gmail.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/w83l786ng.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 9b81bd406e05..1d9109ca1585 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -76,15 +76,25 @@ FAN_TO_REG(long rpm, int div) return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); }
-#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
((val) == 255 ? 0 : \1350000 / ((val) * (div))))+static int fan_from_reg(int val, int div) +{
if (val == 0)return -1;if (val == 255)return 0;return 1350000 / (val * div);+}
/* for temp */ #define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \ : (val)) / 1000, 0, 0xff))
Can you change TEMP_TO_REG() as well. And just use plain clamp() while you are at it. Both these temperature conversion functions have to work with negative temperatures. But the signed-ness gets passed through from the parameter - which may not be right. IIRC some come from FIELD_GET() and will be 'unsigned long' unless cast somewhere. The function parameter 'corrects' the type to a signed one.
So you are fixing potential bugs as well.
Hi David,
Thanks for your feedback on TEMP_TO_REG and the detailed explanation regarding macro risks.
Guenter has already applied this patch.
Patches are supposed to be posted for review and applied a few days later.
Since the primary scope here was strictly addressing TOCTOU race conditions (and TEMP_TO_REG is not used in lockless contexts), it wasn't included.
However, I appreciate your point regarding type safety. I will look into addressing that in a future separate patch.
It's not just type safety, and #define that evaluates an argument more than one is just a bug waiting to happen. We've been removing (or trying not to write) those since the 1980s.
You also just didn't read the code:
-#define TEMP_FROM_REG(val) (((val) & 0x80 ? \ - (val) - 0x100 : (val)) * 1000) + +static int temp_from_reg(int val) +{ + if (val & 0x80) + return (val - 0x100) * 1000; + return val * 1000; +}
Both those only work if 'val' is 8 bits. They are just ((s8)(val) * 1000) and generate a milli-centigrade value from the 8-bit signed centigrade value the hardware provides.
TEMP_TO_REG() is the opposite, so is: (u8)clamp((val) / 1000, -128, 127) That is subtly different since it truncates negative values towards zero rather than -infinity. The more complicated: (u8)(clamp(((val) + 128 * 1000)/1000, 0, 0xff) - 128) would exactly match the original (and generate less code) but I suspect it doesn't matter here.
David
Best regards, Gui-Dong Han