diff options
author | Liam Beguin <liambeguin@gmail.com> | 2022-02-12 21:57:34 -0500 |
---|---|---|
committer | Jonathan Cameron <Jonathan.Cameron@huawei.com> | 2022-02-27 13:38:22 +0000 |
commit | 2eb30577f2533463afed3456141d4becc4f79e91 (patch) | |
tree | f509210b0d40fd695bdda219b2d0f653e0f4145f /drivers/iio | |
parent | f5fc003d48033559314f1c9de8198f58f14ed557 (diff) |
iio: afe: rescale: reduce risk of integer overflow
Reduce the risk of integer overflow by doing the scale calculation on
a 64-bit integer. Since the rescaling is only performed on *val, reuse
the IIO_VAL_FRACTIONAL_LOG2 case.
Signed-off-by: Liam Beguin <liambeguin@gmail.com>
Reviewed-by: Peter Rosin <peda@axentia.se>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220213025739.2561834-6-liambeguin@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/afe/iio-rescale.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 5d78f0cf47d2..46947c68d3a9 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -24,21 +24,31 @@ int rescale_process_scale(struct rescale *rescale, int scale_type, int *val, int *val2) { s64 tmp; + int _val, _val2; s32 rem, rem2; u32 mult; u32 neg; switch (scale_type) { - case IIO_VAL_FRACTIONAL: - *val *= rescale->numerator; - *val2 *= rescale->denominator; - return scale_type; case IIO_VAL_INT: *val *= rescale->numerator; if (rescale->denominator == 1) return scale_type; *val2 = rescale->denominator; return IIO_VAL_FRACTIONAL; + case IIO_VAL_FRACTIONAL: + /* + * When the product of both scales doesn't overflow, avoid + * potential accuracy loss (for in kernel consumers) by + * keeping a fractional representation. + */ + if (!check_mul_overflow(*val, rescale->numerator, &_val) && + !check_mul_overflow(*val2, rescale->denominator, &_val2)) { + *val = _val; + *val2 = _val2; + return IIO_VAL_FRACTIONAL; + } + fallthrough; case IIO_VAL_FRACTIONAL_LOG2: tmp = (s64)*val * 1000000000LL; tmp = div_s64(tmp, rescale->denominator); @@ -50,7 +60,10 @@ int rescale_process_scale(struct rescale *rescale, int scale_type, if (!rem) return scale_type; - tmp = 1 << *val2; + if (scale_type == IIO_VAL_FRACTIONAL) + tmp = *val2; + else + tmp = ULL(1) << *val2; rem2 = *val % (int)tmp; *val = *val / (int)tmp; |