Behavior of Date::try_new_added with large durations
Related: https://github.com/unicode-org/icu4x/issues/7076
See fuzz results in https://github.com/unicode-org/icu4x/pull/7206
Trying to add DateDuration { is_negative: false, years: 4294901760, months: 256, weeks: 0, days: 0} to Dangi date 0-1-1 produces a panic at https://github.com/unicode-org/icu4x/blob/1fbff08420e7ff1f105de5e3760bf786924e54d9/components/calendar/src/duration.rs#L244
{months: 1281, days: 8589934592} does not produce a panic but produces a very slow result (https://github.com/unicode-org/icu4x/issues/7077 for optimizations). When we first tested this I verified that no arithmetic operation within Temporal range was slow, but I did not verify this outside of the Temporal range; thinking Temporal would filter these out. It doesn't: Temporal allows for a much wider range of Durations than needed, only erroring if the final result is out of range. This somewhat makes sense, in theory you could have a calendar where each day is a year so adding a large number of years might still keep you in range.
In https://github.com/unicode-org/icu4x/pull/7065 we got to the point where we support dates in the range of all i32 year values for a given calendar. We need to make a similar determination for durations: when do we error for duration arithmetic being out of range?
The easy thing to do is ensure that these debug assertions in duration.rs turn into errors, which might fix most issues. I'm not sure it will fix the slow code, we might want to preemptively error when we know something will be out of range.
cc @sffc @robertbastian
Note: finding some resolution to this either in temporal_rs or icu_calendar is a ship blocker for Temporal in V8. Our fix can be upstreamed as a patch though.
Annoyingly, it's not easy to turn https://github.com/unicode-org/icu4x/blob/1fbff08420e7ff1f105de5e3760bf786924e54d9/components/calendar/src/duration.rs#L244 into a proper error because it's also called transitively in until (via surpasses) where it shouldn't fail
Another crash, this time on multiplication
thread '<unnamed>' (2781843) panicked at /home/manishearth/dev/icu4x/components/calendar/src/cal/east_asian_traditional/simple.rs:89:31:
attempt to multiply with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
==2781843== ERROR: libFuzzer: deadly signal
#0 0x564e39698681 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x12b681) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#1 0x564e3970d7aa (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x1a07aa) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#2 0x564e39703a45 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x196a45) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#3 0x7f1327849def (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#4 0x7f132789e95b (/lib/x86_64-linux-gnu/libc.so.6+0x9495b) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#5 0x7f1327849cc1 (/lib/x86_64-linux-gnu/libc.so.6+0x3fcc1) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#6 0x7f13278324ab (/lib/x86_64-linux-gnu/libc.so.6+0x284ab) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#7 0x564e3976fac9 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x202ac9) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#8 0x564e39771de8 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x204de8) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#9 0x564e396c6934 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x159934) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#10 0x564e3977275e (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x20575e) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#11 0x564e39772585 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x205585) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#12 0x564e39770c68 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x203c68) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#13 0x564e3976245c (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x1f545c) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#14 0x564e397a32df (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x2362df) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#15 0x564e397a2ed6 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x235ed6) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#16 0x564e39730538 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x1c3538) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#17 0x564e3972fd5b (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x1c2d5b) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#18 0x564e3974a9a3 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x1dd9a3) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#19 0x564e396bfb18 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x152b18) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#20 0x564e396c5c04 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x158c04) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#21 0x564e396c72e5 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x15a2e5) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#22 0x564e396c8598 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x15b598) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#23 0x564e396c635d (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x15935d) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#24 0x564e39703fb4 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x196fb4) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#25 0x564e3970a24f (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x19d24f) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#26 0x564e3970b43a (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x19e43a) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#27 0x564e3970c1d7 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x19f1d7) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#28 0x564e396d9d83 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x16cd83) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#29 0x564e396c85c2 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x15b5c2) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
#30 0x7f1327833ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#31 0x7f1327833d64 (/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId: 61e1dea1f540b3b4b4d8ec76716e409cec096ece)
#32 0x564e396038a0 (/home/manishearth/dev/icu4x/components/calendar/fuzz/target/x86_64-unknown-linux-gnu/release/add+0x968a0) (BuildId: be850dc66dd0b661dc861493c5e1f24e0d156993)
NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 1 ChangeByte-; base unit: b35d39e5bca16c13eff8e5a1ebce42892884892e
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x32,
\000\000\000\000\000\000\000\000\000\000\000\000\000\000z\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\012\0002
artifact_prefix='/home/manishearth/dev/icu4x/components/calendar/fuzz/artifacts/add/'; Test unit written to /home/manishearth/dev/icu4x/components/calendar/fuzz/artifacts/add/crash-8ce616f753089f179a87a0b29fab513dabec9b65
Base64: AAAAAAAAAAAAAAAAAAB6AAAAAAAAAAAAAAAAAAAAAAAACgAy
────────────────────────────────────────────────────────────────────────────────
Failing input:
fuzz/artifacts/add/slow-unit-430c4ed1b80e79f55b6c7e9bbaf97ffebd93d326
Output of `std::fmt::Debug`:
FuzzInput {
ymd: Ymd {
year: 0,
month: 0,
day: 0,
month_interpretation: Ordinal,
},
duration: DateDuration {
is_negative: false,
years: 0,
months: 1281,
weeks: 0,
days: 8589934592,
},
overflow_constrain: false,
cal: Dangi,
}
Reproduce with:
cargo fuzz run add fuzz/artifacts/add/slow-unit-430c4ed1b80e79f55b6c7e9bbaf97ffebd93d326
Minimize test case with:
cargo fuzz tmin add fuzz/artifacts/add/slow-unit-430c4ed1b80e79f55b6c7e9bbaf97ffebd93d326
────────────────────────────────────────────────────────────────────────────────
Failing input:
fuzz/artifacts/add/crash-8ce616f753089f179a87a0b29fab513dabec9b65
Output of `std::fmt::Debug`:
FuzzInput {
ymd: Ymd {
year: 0,
month: 0,
day: 0,
month_interpretation: Ordinal,
},
duration: DateDuration {
is_negative: false,
years: 2046820352,
months: 0,
weeks: 0,
days: 0,
},
overflow_constrain: false,
cal: Dangi,
}
Reproduce with:
cargo fuzz run add fuzz/artifacts/add/crash-8ce616f753089f179a87a0b29fab513dabec9b65
Minimize test case with:
cargo fuzz tmin add fuzz/artifacts/add/crash-8ce616f753089f179a87a0b29fab513dabec9b65
https://github.com/boa-dev/temporal/pull/615 fixes this in Temporal but not ICU4X. That same fix would perhaps be something we could apply here, maybe with different numerical limits.
I really think we should fix #7076, and we should fix this issue in the same context, making sure add obeys the same constraint, existing as soon as it knows that the resulting date will be out of range.
It's great if we can justify this fix as being part of the Temporal critical path, rather than going on the pile of tech debt.