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