Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hwmon: (macsmc) Fix overflows, underflows, and sign extension

The macsmc-hwmon driver experienced several issues related to value
scaling and type conversion:

1. macsmc_hwmon_read_f32_scaled() clipped values to INT_MAX/INT_MIN.
On 64-bit systems, hwmon supports long values, so clipping to
32-bit range was premature and caused loss of range for high-power
sensors. Changed it to use long and clip to LONG_MAX/LONG_MIN.
2. The overflow check in macsmc_hwmon_read_f32_scaled() used 1UL,
which is 32-bit on some platforms. Switched to 1ULL.
3. macsmc_hwmon_read_key() used a u32 temporary variable for f32
values. When assigned to a 64-bit long, negative values were
zero-extended instead of sign-extended, resulting in large
positive numbers.
4. macsmc_hwmon_read_ioft_scaled() used mult_frac() which could
overflow during intermediate multiplication. Switched to
mul_u64_u32_div() to handle the 64-bit multiplication safely.
5. ioft values (unsigned 48.16) could overflow long when scaled
by 1,000,000. Added explicit clipping to LONG_MAX in the caller.
6. macsmc_hwmon_write_f32() truncated its long argument to int,
potentially causing issues for large values.

Fix these issues by using appropriate types and helper functions.

Fixes: 785205fd8139 ("hwmon: Add Apple Silicon SMC hwmon driver")
Cc: James Calligeros <jcalligeros99@gmail.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Neal Gompa <neal@gompa.dev>
Cc: Janne Grunau <j@jannau.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20260129175112.3751907-3-linux@roeck-us.net
Reviewed-by: James Calligeros <jcalligeros99@gmail.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

+16 -12
+16 -12
drivers/hwmon/macsmc-hwmon.c
··· 22 22 23 23 #include <linux/bitfield.h> 24 24 #include <linux/hwmon.h> 25 + #include <linux/math64.h> 25 26 #include <linux/mfd/macsmc.h> 26 27 #include <linux/module.h> 27 28 #include <linux/of.h> ··· 131 130 if (ret < 0) 132 131 return ret; 133 132 134 - *p = mult_frac(val, scale, 65536); 133 + *p = mul_u64_u32_div(val, scale, 65536); 135 134 136 135 return 0; 137 136 } ··· 141 140 * them. 142 141 */ 143 142 static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key, 144 - int *p, int scale) 143 + long *p, int scale) 145 144 { 146 145 u32 fval; 147 146 u64 val; ··· 163 162 val = 0; 164 163 else if (exp < 0) 165 164 val >>= -exp; 166 - else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */ 165 + else if (exp != 0 && (val & ~((1ULL << (64 - exp)) - 1))) /* overflow */ 167 166 val = U64_MAX; 168 167 else 169 168 val <<= exp; 170 169 171 170 if (fval & FLT_SIGN_MASK) { 172 - if (val > (-(s64)INT_MIN)) 173 - *p = INT_MIN; 171 + if (val > (u64)LONG_MAX + 1) 172 + *p = LONG_MIN; 174 173 else 175 - *p = -val; 174 + *p = -(long)val; 176 175 } else { 177 - if (val > INT_MAX) 178 - *p = INT_MAX; 176 + if (val > (u64)LONG_MAX) 177 + *p = LONG_MAX; 179 178 else 180 - *p = val; 179 + *p = (long)val; 181 180 } 182 181 183 182 return 0; ··· 196 195 switch (sensor->info.type_code) { 197 196 /* 32-bit IEEE 754 float */ 198 197 case __SMC_KEY('f', 'l', 't', ' '): { 199 - u32 flt_ = 0; 198 + long flt_ = 0; 200 199 201 200 ret = macsmc_hwmon_read_f32_scaled(smc, sensor->macsmc_key, 202 201 &flt_, scale); ··· 215 214 if (ret) 216 215 return ret; 217 216 218 - *val = (long)ioft; 217 + if (ioft > LONG_MAX) 218 + *val = LONG_MAX; 219 + else 220 + *val = (long)ioft; 219 221 break; 220 222 } 221 223 default: ··· 228 224 return 0; 229 225 } 230 226 231 - static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, int value) 227 + static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, long value) 232 228 { 233 229 u64 val; 234 230 u32 fval = 0;