From 3844edaaedcaa0260e7c07d9c0f0a00580df8e79 Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Wed, 15 Apr 2026 18:03:02 -0500 Subject: [PATCH] AK: Avoid overflow in Duration::to_time_units() with fraction near 1 With numerators or denominators approaching NumericLimits::max(), we could overflow in the sum of the remainder and the rounding contribution. Instead, divide them separately and sum them afterward. --- AK/Time.cpp | 14 ++++++++++---- Tests/AK/TestTime.cpp | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/AK/Time.cpp b/AK/Time.cpp index 876fa383d59..2e1ecca6931 100644 --- a/AK/Time.cpp +++ b/AK/Time.cpp @@ -231,11 +231,17 @@ i64 Duration::to_time_units(u32 numerator, u32 denominator) const auto seconds_product = saturating_mul(m_seconds, static_cast(denominator)); auto time_units = seconds_product / numerator; - auto remainder = seconds_product % numerator; + auto seconds_remainder_dividend = seconds_product % numerator; - auto remainder_in_nanoseconds = remainder * 1'000'000'000; - auto rounding_half = static_cast(numerator) * 500'000'000; - time_units = saturating_add(time_units, ((static_cast(m_nanoseconds) * denominator + remainder_in_nanoseconds + rounding_half) / numerator) / 1'000'000'000); + auto seconds_remainder_nanosecond_dividend = seconds_remainder_dividend * 1'000'000'000; + auto rounding_half_nanosecond_dividend = static_cast(numerator) * 500'000'000; + auto sub_seconds_nanosecond_dividend = (static_cast(m_nanoseconds) * denominator) + seconds_remainder_nanosecond_dividend; + + auto sub_seconds_nanosecond_units = sub_seconds_nanosecond_dividend / numerator; + auto sub_seconds_units_remainder_nanosecond_dividend = sub_seconds_nanosecond_dividend % numerator; + auto rounding_adjustment_nanosecond_units = (rounding_half_nanosecond_dividend + sub_seconds_units_remainder_nanosecond_dividend) / numerator; + + time_units = saturating_add(time_units, (sub_seconds_nanosecond_units + rounding_adjustment_nanosecond_units) / 1'000'000'000); return time_units; } diff --git a/Tests/AK/TestTime.cpp b/Tests/AK/TestTime.cpp index 055d2762a10..bc47c9c7697 100644 --- a/Tests/AK/TestTime.cpp +++ b/Tests/AK/TestTime.cpp @@ -986,6 +986,8 @@ TEST_CASE(time_units) EXPECT_EQ(Duration::from_seconds(2'147'483'649).to_time_units(1, NumericLimits::max()), NumericLimits::max()); EXPECT_EQ(Duration::from_seconds(2'147'483'648).to_time_units(1, NumericLimits::max()), NumericLimits::max() - (NumericLimits::max() / 2)); + EXPECT_EQ((Duration::from_seconds(1) + Duration::from_nanoseconds(999'999'999)).to_time_units(NumericLimits::max(), NumericLimits::max() - 1), 2); + EXPECT_DEATH("From time units with zero numerator", (void)Duration::from_time_units(1, 0, 1)); EXPECT_DEATH("From time units with zero denominator", (void)Duration::from_time_units(1, 1, 0)); }