]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
hwmon: (macsmc) Fix overflows, underflows, and sign extension
authorGuenter Roeck <linux@roeck-us.net>
Thu, 29 Jan 2026 17:51:11 +0000 (09:51 -0800)
committerGuenter Roeck <linux@roeck-us.net>
Sun, 22 Feb 2026 23:48:30 +0000 (15:48 -0800)
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>
drivers/hwmon/macsmc-hwmon.c

index 40d25c81b44356010c0cb1cb0516c42970a76e78..1500ec2cc9f83d4600b5a3cba1caffdcc9940ef0 100644 (file)
@@ -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;