Commit 579b86f3 authored by Guenter Roeck's avatar Guenter Roeck
Browse files

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: 785205fd ("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: default avatarGuenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20260129175112.3751907-3-linux@roeck-us.net


Reviewed-by: default avatarJames Calligeros <jcalligeros99@gmail.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 5dd69b86
Loading
Loading
Loading
Loading
+16 −12
Original line number Diff line number Diff line
@@ -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,6 +215,9 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
		if (ret)
			return ret;

		if (ioft > LONG_MAX)
			*val = LONG_MAX;
		else
			*val = (long)ioft;
		break;
	}
@@ -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;