iio: afe: rescale: reduce risk of integer overflow
authorLiam Beguin <liambeguin@gmail.com>
Sun, 13 Feb 2022 02:57:34 +0000 (21:57 -0500)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 27 Feb 2022 13:38:22 +0000 (13:38 +0000)
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>
drivers/iio/afe/iio-rescale.c

index 5d78f0c..46947c6 100644 (file)
@@ -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;