Commit a8cb3cef authored by Frank Tang's avatar Frank Tang Committed by V8 LUCI CQ

[Temporal] Avoid double overflow in AddDuration

Add a version of BalanceDuration which take two TimeDurationRecord
and add them internally after converting to BigInt as nanoseconds so it will not overflow the double.

Use "std::isinf()" instead of "!std::isfinite()"

Inspired by https://github.com/tc39/proposal-temporal/issues/2380#issuecomment-1219194995

Bug: v8:11544
Change-Id: I29e06fa857ff43f2668e1e4ffd07735ff6efee42
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3837852
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82576}
parent ff8d67c8
...@@ -310,6 +310,11 @@ V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration( ...@@ -310,6 +310,11 @@ V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration( V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
Isolate* isolate, Unit largest_unit, Handle<BigInt> nanoseconds, Isolate* isolate, Unit largest_unit, Handle<BigInt> nanoseconds,
const char* method_name); const char* method_name);
// A special version of BalanceDuration which add two TimeDurationRecord
// internally as BigInt to avoid overflow double.
V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
Isolate* isolate, Unit largest_unit, const TimeDurationRecord& dur1,
const TimeDurationRecord& dur2, const char* method_name);
// sec-temporal-balancepossiblyinfiniteduration // sec-temporal-balancepossiblyinfiniteduration
enum BalanceOverflow { enum BalanceOverflow {
...@@ -5104,6 +5109,18 @@ Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, ...@@ -5104,6 +5109,18 @@ Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
} }
} }
Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
const TimeDurationRecord& dur1,
const TimeDurationRecord& dur2,
const char* method_name) {
// Add the two TimeDurationRecord as BigInt in nanoseconds.
Handle<BigInt> nanoseconds =
BigInt::Add(isolate, TotalDurationNanoseconds(isolate, dur1, 0),
TotalDurationNanoseconds(isolate, dur2, 0))
.ToHandleChecked();
return BalanceDuration(isolate, largest_unit, nanoseconds, method_name);
}
// #sec-temporal-balanceduration // #sec-temporal-balanceduration
Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
Handle<Object> relative_to_obj, Handle<Object> relative_to_obj,
...@@ -5347,9 +5364,10 @@ Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration( ...@@ -5347,9 +5364,10 @@ Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration(
double milliseconds_value = BigInt::ToNumber(isolate, milliseconds)->Number(); double milliseconds_value = BigInt::ToNumber(isolate, milliseconds)->Number();
double microseconds_value = BigInt::ToNumber(isolate, microseconds)->Number(); double microseconds_value = BigInt::ToNumber(isolate, microseconds)->Number();
double nanoseconds_value = BigInt::ToNumber(isolate, nanoseconds)->Number(); double nanoseconds_value = BigInt::ToNumber(isolate, nanoseconds)->Number();
if (!std::isfinite(hours_value) || !std::isfinite(minutes_value) || if (std::isinf(days) || std::isinf(hours_value) ||
!std::isfinite(seconds_value) || !std::isfinite(milliseconds_value) || std::isinf(minutes_value) || std::isinf(seconds_value) ||
!std::isfinite(microseconds_value) || !std::isfinite(nanoseconds_value)) { std::isinf(milliseconds_value) || std::isinf(microseconds_value) ||
std::isinf(nanoseconds_value)) {
return Just(BalancePossiblyInfiniteDurationResult( return Just(BalancePossiblyInfiniteDurationResult(
{{0, 0, 0, 0, 0, 0, 0}, {{0, 0, 0, 0, 0, 0, 0},
sign == 1 ? BalanceOverflow::kPositive : BalanceOverflow::kNegative})); sign == 1 ? BalanceOverflow::kPositive : BalanceOverflow::kNegative}));
...@@ -8415,20 +8433,16 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1, ...@@ -8415,20 +8433,16 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(),
Nothing<DurationRecord>()); Nothing<DurationRecord>());
} }
// b. Let result be ! BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 + // b. Let result be ? BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 +
// s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). // s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
result.time_duration = // Note: We call a special version of BalanceDuration which add two duration
BalanceDuration( // internally to avoid overflow the double.
isolate, largest_unit, MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
{dur1.time_duration.days + dur2.time_duration.days, isolate, result.time_duration,
dur1.time_duration.hours + dur2.time_duration.hours, BalanceDuration(isolate, largest_unit, dur1.time_duration,
dur1.time_duration.minutes + dur2.time_duration.minutes, dur2.time_duration, method_name),
dur1.time_duration.seconds + dur2.time_duration.seconds, Nothing<DurationRecord>());
dur1.time_duration.milliseconds + dur2.time_duration.milliseconds,
dur1.time_duration.microseconds + dur2.time_duration.microseconds,
dur1.time_duration.nanoseconds + dur2.time_duration.nanoseconds},
method_name)
.ToChecked();
// c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]], // c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]],
// result.[[Hours]], result.[[Minutes]], result.[[Seconds]], // result.[[Hours]], result.[[Minutes]], result.[[Seconds]],
// result.[[Milliseconds]], result.[[Microseconds]], // result.[[Milliseconds]], result.[[Microseconds]],
...@@ -8504,20 +8518,19 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1, ...@@ -8504,20 +8518,19 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
CalendarDateUntil(isolate, calendar, relative_to, end, CalendarDateUntil(isolate, calendar, relative_to, end,
difference_options), difference_options),
Nothing<DurationRecord>()); Nothing<DurationRecord>());
// n. Let result be ! BalanceDuration(dateDifference.[[Days]], h1 + h2, min1 // n. Let result be ? BalanceDuration(dateDifference.[[Days]], h1 + h2, min1
// + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). // + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
result.time_duration = // Note: We call a special version of BalanceDuration which add two duration
BalanceDuration( // internally to avoid overflow the double.
isolate, largest_unit, TimeDurationRecord time_dur1 = dur1.time_duration;
{date_difference->days().Number(), time_dur1.days = date_difference->days().Number();
dur1.time_duration.hours + dur2.time_duration.hours, TimeDurationRecord time_dur2 = dur2.time_duration;
dur1.time_duration.minutes + dur2.time_duration.minutes, time_dur2.days = 0;
dur1.time_duration.seconds + dur2.time_duration.seconds, MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
dur1.time_duration.milliseconds + dur2.time_duration.milliseconds, isolate, result.time_duration,
dur1.time_duration.microseconds + dur2.time_duration.microseconds, BalanceDuration(isolate, largest_unit, time_dur1, time_dur2,
dur1.time_duration.nanoseconds + dur2.time_duration.nanoseconds}, method_name),
method_name) Nothing<DurationRecord>());
.ToChecked();
// l. Return ! CreateDurationRecord(dateDifference.[[Years]], // l. Return ! CreateDurationRecord(dateDifference.[[Years]],
// dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], // dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]],
// result.[[Hours]], result.[[Minutes]], result.[[Seconds]], // result.[[Hours]], result.[[Minutes]], result.[[Seconds]],
......
...@@ -703,11 +703,9 @@ ...@@ -703,11 +703,9 @@
'intl402/Temporal/Calendar/prototype/era/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL], 'intl402/Temporal/Calendar/prototype/era/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL],
'intl402/Temporal/Calendar/prototype/eraYear/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL], 'intl402/Temporal/Calendar/prototype/eraYear/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL],
'built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight': [FAIL], 'built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight': [FAIL],
'built-ins/Temporal/Duration/prototype/add/days-is-number-max-value': [FAIL],
'built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1': [FAIL], 'built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1': [FAIL],
'built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time': [FAIL], 'built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time': [FAIL],
'built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime': [PASS, FAIL], 'built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime': [PASS, FAIL],
'built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value': [FAIL],
'built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1': [FAIL], 'built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1': [FAIL],
'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign': [FAIL], 'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign': [FAIL],
'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days': [FAIL], 'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days': [FAIL],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment