diff mbox series

hwmon: (tmp513) Fix division of negative numbers

Message ID 20250114-fix-si-prefix-macro-sign-bugs-v1-1-696fd8d10f00@baylibre.com (mailing list archive)
State Accepted
Headers show
Series hwmon: (tmp513) Fix division of negative numbers | expand

Commit Message

David Lechner Jan. 14, 2025, 9:45 p.m. UTC
Fix several issues with division of negative numbers in the tmp513
driver.

The docs on the DIV_ROUND_CLOSEST macro explain that dividing a negative
value by an unsigned type is undefined behavior. The driver was doing
this in several places, i.e. data->shunt_uohms has type of u32. The
actual "undefined" behavior is that it converts both values to unsigned
before doing the division, for example:

    int ret = DIV_ROUND_CLOSEST(-100, 3U);

results in ret == 1431655732 instead of -33.

Furthermore the MILLI macro has a type of unsigned long. Multiplying a
signed long by an unsigned long results in an unsigned long.

So, we need to cast both MILLI and data data->shunt_uohms to long when
using the DIV_ROUND_CLOSEST macro.

Fixes: f07f9d2467f4 ("hwmon: (tmp513) Use SI constants from units.h")
Fixes: 59dfa75e5d82 ("hwmon: Add driver for Texas Instruments TMP512/513 sensor chips.")
Signed-off-by: David Lechner <dlechner@baylibre.com>
---
 drivers/hwmon/tmp513.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)


---
base-commit: dab2734f8e9ecba609d66d1dd087a392a7774c04
change-id: 20250114-fix-si-prefix-macro-sign-bugs-307673e6ea45

Best regards,

Comments

Guenter Roeck Jan. 14, 2025, 11:41 p.m. UTC | #1
On Tue, Jan 14, 2025 at 03:45:52PM -0600, David Lechner wrote:
> Fix several issues with division of negative numbers in the tmp513
> driver.
> 
> The docs on the DIV_ROUND_CLOSEST macro explain that dividing a negative
> value by an unsigned type is undefined behavior. The driver was doing
> this in several places, i.e. data->shunt_uohms has type of u32. The
> actual "undefined" behavior is that it converts both values to unsigned
> before doing the division, for example:
> 
>     int ret = DIV_ROUND_CLOSEST(-100, 3U);
> 
> results in ret == 1431655732 instead of -33.
> 
> Furthermore the MILLI macro has a type of unsigned long. Multiplying a
> signed long by an unsigned long results in an unsigned long.
> 
> So, we need to cast both MILLI and data data->shunt_uohms to long when
> using the DIV_ROUND_CLOSEST macro.
> 
> Fixes: f07f9d2467f4 ("hwmon: (tmp513) Use SI constants from units.h")
> Fixes: 59dfa75e5d82 ("hwmon: Add driver for Texas Instruments TMP512/513 sensor chips.")
> Signed-off-by: David Lechner <dlechner@baylibre.com>

Applied.

Thanks,
Guenter

> ---
>  drivers/hwmon/tmp513.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> 
> ---
> base-commit: dab2734f8e9ecba609d66d1dd087a392a7774c04
> change-id: 20250114-fix-si-prefix-macro-sign-bugs-307673e6ea45
> 
> Best regards,
> 
> diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c
> index 1c2cb12071b80866b751b71bf39292580cd47929..1c7601de47d0720352d729e35a5b7eeaf109355f 100644
> --- a/drivers/hwmon/tmp513.c
> +++ b/drivers/hwmon/tmp513.c
> @@ -207,7 +207,9 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
>  		*val = sign_extend32(regval,
>  				     reg == TMP51X_SHUNT_CURRENT_RESULT ?
>  				     16 - tmp51x_get_pga_shift(data) : 15);
> -		*val = DIV_ROUND_CLOSEST(*val * 10 * MILLI, data->shunt_uohms);
> +		*val = DIV_ROUND_CLOSEST(*val * 10 * (long)MILLI,
> +					 (long)data->shunt_uohms);
> +
>  		break;
>  	case TMP51X_BUS_VOLTAGE_RESULT:
>  	case TMP51X_BUS_VOLTAGE_H_LIMIT:
> @@ -223,7 +225,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
>  	case TMP51X_BUS_CURRENT_RESULT:
>  		// Current = (ShuntVoltage * CalibrationRegister) / 4096
>  		*val = sign_extend32(regval, 15) * (long)data->curr_lsb_ua;
> -		*val = DIV_ROUND_CLOSEST(*val, MILLI);
> +		*val = DIV_ROUND_CLOSEST(*val, (long)MILLI);
>  		break;
>  	case TMP51X_LOCAL_TEMP_RESULT:
>  	case TMP51X_REMOTE_TEMP_RESULT_1:
> @@ -263,7 +265,8 @@ static int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val)
>  		 * The user enter current value and we convert it to
>  		 * voltage. 1lsb = 10uV
>  		 */
> -		val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10 * MILLI);
> +		val = DIV_ROUND_CLOSEST(val * (long)data->shunt_uohms,
> +					10 * (long)MILLI);
>  		max_val = U16_MAX >> tmp51x_get_pga_shift(data);
>  		regval = clamp_val(val, -max_val, max_val);
>  		break;
diff mbox series

Patch

diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c
index 1c2cb12071b80866b751b71bf39292580cd47929..1c7601de47d0720352d729e35a5b7eeaf109355f 100644
--- a/drivers/hwmon/tmp513.c
+++ b/drivers/hwmon/tmp513.c
@@ -207,7 +207,9 @@  static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
 		*val = sign_extend32(regval,
 				     reg == TMP51X_SHUNT_CURRENT_RESULT ?
 				     16 - tmp51x_get_pga_shift(data) : 15);
-		*val = DIV_ROUND_CLOSEST(*val * 10 * MILLI, data->shunt_uohms);
+		*val = DIV_ROUND_CLOSEST(*val * 10 * (long)MILLI,
+					 (long)data->shunt_uohms);
+
 		break;
 	case TMP51X_BUS_VOLTAGE_RESULT:
 	case TMP51X_BUS_VOLTAGE_H_LIMIT:
@@ -223,7 +225,7 @@  static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
 	case TMP51X_BUS_CURRENT_RESULT:
 		// Current = (ShuntVoltage * CalibrationRegister) / 4096
 		*val = sign_extend32(regval, 15) * (long)data->curr_lsb_ua;
-		*val = DIV_ROUND_CLOSEST(*val, MILLI);
+		*val = DIV_ROUND_CLOSEST(*val, (long)MILLI);
 		break;
 	case TMP51X_LOCAL_TEMP_RESULT:
 	case TMP51X_REMOTE_TEMP_RESULT_1:
@@ -263,7 +265,8 @@  static int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val)
 		 * The user enter current value and we convert it to
 		 * voltage. 1lsb = 10uV
 		 */
-		val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10 * MILLI);
+		val = DIV_ROUND_CLOSEST(val * (long)data->shunt_uohms,
+					10 * (long)MILLI);
 		max_val = U16_MAX >> tmp51x_get_pga_shift(data);
 		regval = clamp_val(val, -max_val, max_val);
 		break;