Both operands of div64_s64() are s64, but U-Boot's abs() is not 64-bit
safe. Unlike its Linux counterpart, which dispatches on the argument
type down to long long, U-Boot's abs() evaluates its argument as int
whenever sizeof(x) != sizeof(long) and yields a long.

The header even says so: "abs() should not be used for 64-bit types
(s64, u64, long long) - use abs64() for those."

So on BITS_PER_LONG == 32 both operands are silently truncated to 32
bits before the division. Simulating the macro with long narrowed to
32 bits shows what reaches div64_u64():

  x=         -4294967296  abs()=          0  abs64()=         4294967296
  x=         -5000000000  abs()=  705032704  abs64()=         5000000000
  x=-9223372036854775807  abs()=          1  abs64()= 9223372036854775807

A zero from the first case makes the subsequent division a divide by
zero rather than merely imprecise.

div64_s64() has no in-tree callers today, so this is a latent bug and
not a regression. Note that the abs() in div_s64_rem() is correct as-is
and deliberately left alone.

Fixes: 0342e335ba88 ("lib: div64: sync with Linux")
Signed-off-by: Alexey Charkov <[email protected]>
---
 lib/div64.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/div64.c b/lib/div64.c
index 14d402ce4c7e..f5624279086a 100644
--- a/lib/div64.c
+++ b/lib/div64.c
@@ -171,7 +171,12 @@ s64 div64_s64(s64 dividend, s64 divisor)
 {
        s64 quot, t;
 
-       quot = div64_u64(abs(dividend), abs(divisor));
+       /*
+        * Unlike Linux, U-Boot's abs() is not 64-bit safe: it evaluates its
+        * argument as int when sizeof(x) != sizeof(long), so both operands
+        * need abs64() here.
+        */
+       quot = div64_u64(abs64(dividend), abs64(divisor));
        t = (dividend ^ divisor) >> 63;
 
        return (quot ^ t) - t;

-- 
2.54.0

Reply via email to