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 |
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 --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;
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,