Skip to content

fix: avoid panic in duration parser on out-of-range values - #67

Merged
badboy merged 1 commit into
badboy:mainfrom
ChrisJr404:fix-duration-parser-panic-on-large-values
Aug 19, 2026
Merged

fix: avoid panic in duration parser on out-of-range values#67
badboy merged 1 commit into
badboy:mainfrom
ChrisJr404:fix-duration-parser-panic-on-large-values

Conversation

@ChrisJr404

Copy link
Copy Markdown
Contributor

While parsing untrusted input I hit a panic in iso8601::duration().

Any duration whose numeric component is larger than u32::MAX crashes the
parser:

iso8601::duration("P99999999999Y"); // panics
thread 'main' panicked at src/parsers.rs:40:
Invalid string, expected ASCII representation of a number: ParseIntError { kind: PosOverflow }

Cause

Every duration component is read by take_digits, which collects a run of
ASCII digits and then converts it with .parse().expect(...):

let res = s
    .parse()
    .expect("Invalid string, expected ASCII representation of a number");

take_digits accepts an unbounded number of digits, so a value that does not
fit into a u32 makes the conversion return Err and the .expect() panics.
Because the panic is a failed Result::expect (not an arithmetic overflow), it
fires in both debug and release builds.

Only the duration grammar uses the unbounded take_digits; the date, time and
datetime parsers use the fixed-width take_n_digits helper (max 4 digits), so
they are not affected. That is also why the existing parse_dates fuzz target
never surfaced this — it exercises date/time/datetime but not duration.

Fix

take_digits now returns a normal parser error when the digits do not fit into
a u32, so the parse fails gracefully instead of panicking. Values up to
u32::MAX still parse unchanged. Added a regression test covering each
duration component and confirming u32::MAX still parses.

cargo test, cargo fmt --check and cargo clippy all pass.

…ion values

The duration parser reads each numeric component with take_digits, which
parsed the collected ASCII digits into a u32 with .expect(). A component
whose value exceeds u32::MAX (e.g. "P99999999999Y") made the u32 conversion
fail and the .expect() panic, so iso8601::duration() could panic on
untrusted input.

The date/time/datetime parsers use fixed-width digit helpers and are
unaffected, which is why the existing fuzz targets did not surface this.

take_digits now returns a nom parser error when the number does not fit
into a u32, so the parse fails gracefully. Values up to u32::MAX still
parse. Adds a regression test.
@badboy
badboy merged commit ec40db1 into badboy:main Aug 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants