| From 2549373d2e884f463410754196104b7c398e2b18 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 30 Jun 2021 18:55:49 -0700 |
| Subject: lib/math/rational.c: fix divide by zero |
| |
| From: Trent Piepho <tpiepho@gmail.com> |
| |
| [ Upstream commit 65a0d3c14685663ba111038a35db70f559e39336 ] |
| |
| If the input is out of the range of the allowed values, either larger than |
| the largest value or closer to zero than the smallest non-zero allowed |
| value, then a division by zero would occur. |
| |
| In the case of input too large, the division by zero will occur on the |
| first iteration. The best result (largest allowed value) will be found by |
| always choosing the semi-convergent and excluding the denominator based |
| limit when finding it. |
| |
| In the case of the input too small, the division by zero will occur on the |
| second iteration. The numerator based semi-convergent should not be |
| calculated to avoid the division by zero. But the semi-convergent vs |
| previous convergent test is still needed, which effectively chooses |
| between 0 (the previous convergent) vs the smallest allowed fraction (best |
| semi-convergent) as the result. |
| |
| Link: https://lkml.kernel.org/r/20210525144250.214670-1-tpiepho@gmail.com |
| Fixes: 323dd2c3ed0 ("lib/math/rational.c: fix possible incorrect result from rational fractions helper") |
| Signed-off-by: Trent Piepho <tpiepho@gmail.com> |
| Reported-by: Yiyuan Guo <yguoaz@gmail.com> |
| Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| Cc: Oskar Schirmer <oskar@scara.com> |
| Cc: Daniel Latypov <dlatypov@google.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| lib/math/rational.c | 16 +++++++++++----- |
| 1 file changed, 11 insertions(+), 5 deletions(-) |
| |
| diff --git a/lib/math/rational.c b/lib/math/rational.c |
| index 9781d521963d..c0ab51d8fbb9 100644 |
| --- a/lib/math/rational.c |
| +++ b/lib/math/rational.c |
| @@ -12,6 +12,7 @@ |
| #include <linux/compiler.h> |
| #include <linux/export.h> |
| #include <linux/minmax.h> |
| +#include <linux/limits.h> |
| |
| /* |
| * calculate best rational approximation for a given fraction |
| @@ -78,13 +79,18 @@ void rational_best_approximation( |
| * found below as 't'. |
| */ |
| if ((n2 > max_numerator) || (d2 > max_denominator)) { |
| - unsigned long t = min((max_numerator - n0) / n1, |
| - (max_denominator - d0) / d1); |
| + unsigned long t = ULONG_MAX; |
| |
| - /* This tests if the semi-convergent is closer |
| - * than the previous convergent. |
| + if (d1) |
| + t = (max_denominator - d0) / d1; |
| + if (n1) |
| + t = min(t, (max_numerator - n0) / n1); |
| + |
| + /* This tests if the semi-convergent is closer than the previous |
| + * convergent. If d1 is zero there is no previous convergent as this |
| + * is the 1st iteration, so always choose the semi-convergent. |
| */ |
| - if (2u * t > a || (2u * t == a && d0 * dp > d1 * d)) { |
| + if (!d1 || 2u * t > a || (2u * t == a && d0 * dp > d1 * d)) { |
| n1 = n0 + t * n1; |
| d1 = d0 + t * d1; |
| } |
| -- |
| 2.30.2 |
| |