mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
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: 785205fd81 ("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>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mfd/macsmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@@ -130,7 +131,7 @@ static int macsmc_hwmon_read_ioft_scaled(struct apple_smc *smc, smc_key key,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*p = mult_frac(val, scale, 65536);
|
||||
*p = mul_u64_u32_div(val, scale, 65536);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ static int macsmc_hwmon_read_ioft_scaled(struct apple_smc *smc, smc_key key,
|
||||
* them.
|
||||
*/
|
||||
static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key,
|
||||
int *p, int scale)
|
||||
long *p, int scale)
|
||||
{
|
||||
u32 fval;
|
||||
u64 val;
|
||||
@@ -162,21 +163,21 @@ static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key,
|
||||
val = 0;
|
||||
else if (exp < 0)
|
||||
val >>= -exp;
|
||||
else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */
|
||||
else if (exp != 0 && (val & ~((1ULL << (64 - exp)) - 1))) /* overflow */
|
||||
val = U64_MAX;
|
||||
else
|
||||
val <<= exp;
|
||||
|
||||
if (fval & FLT_SIGN_MASK) {
|
||||
if (val > (-(s64)INT_MIN))
|
||||
*p = INT_MIN;
|
||||
if (val > (u64)LONG_MAX + 1)
|
||||
*p = LONG_MIN;
|
||||
else
|
||||
*p = -val;
|
||||
*p = -(long)val;
|
||||
} else {
|
||||
if (val > INT_MAX)
|
||||
*p = INT_MAX;
|
||||
if (val > (u64)LONG_MAX)
|
||||
*p = LONG_MAX;
|
||||
else
|
||||
*p = val;
|
||||
*p = (long)val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -195,7 +196,7 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
|
||||
switch (sensor->info.type_code) {
|
||||
/* 32-bit IEEE 754 float */
|
||||
case __SMC_KEY('f', 'l', 't', ' '): {
|
||||
u32 flt_ = 0;
|
||||
long flt_ = 0;
|
||||
|
||||
ret = macsmc_hwmon_read_f32_scaled(smc, sensor->macsmc_key,
|
||||
&flt_, scale);
|
||||
@@ -214,7 +215,10 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (long)ioft;
|
||||
if (ioft > LONG_MAX)
|
||||
*val = LONG_MAX;
|
||||
else
|
||||
*val = (long)ioft;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -224,7 +228,7 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, int value)
|
||||
static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, long value)
|
||||
{
|
||||
u64 val;
|
||||
u32 fval = 0;
|
||||
|
||||
Reference in New Issue
Block a user