Add large-year coverage to fuzz target

Add a dedicated fuzz_large_year target with structured inputs biased
toward large years (near 9999 boundary, up to GNU_MAX_YEAR). Keep the
original fuzz_parse_datetime target unchanged for raw-bytes fuzzing.
This commit is contained in:
Sylvestre Ledru
2026-03-31 09:32:40 +02:00
parent e9b8f7a43e
commit 42233b0c81
3 changed files with 88 additions and 0 deletions
+16
View File
@@ -7,6 +7,9 @@ name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "autocfg"
@@ -32,6 +35,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -42,6 +56,8 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
name = "fuzz_parse_datetime"
version = "0.2.0"
dependencies = [
"arbitrary",
"jiff",
"libfuzzer-sys",
"parse_datetime",
]
+8
View File
@@ -8,6 +8,8 @@ cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4.7"
arbitrary = { version = "1", features = ["derive"] }
jiff = "0.2"
[dependencies.parse_datetime]
path = "../"
@@ -17,3 +19,9 @@ name = "fuzz_parse_datetime"
path = "fuzz_targets/parse_datetime.rs"
test = false
doc = false
[[bin]]
name = "fuzz_large_year"
path = "fuzz_targets/large_year.rs"
test = false
doc = false
+64
View File
@@ -0,0 +1,64 @@
#![no_main]
use arbitrary::Arbitrary;
use jiff::{civil::DateTime, tz::TimeZone};
use libfuzzer_sys::fuzz_target;
#[derive(Arbitrary, Debug)]
struct Input {
/// Year for the base date (biased toward boundary years).
base_year_selector: u8,
/// Year to embed in a constructed large-year input string.
input_year: u32,
month: u8,
day: u8,
/// Suffix appended after the constructed date (e.g. relative items).
suffix: String,
/// Whether to also call parse_datetime (no base).
try_no_base: bool,
}
fn base_year(selector: u8) -> i16 {
match selector % 6 {
0 => 2024,
1 => 9998,
2 => 9999,
3 => 1,
4 => 100,
_ => (selector as i16) * 40,
}
}
fn clamp_year(y: u32) -> u32 {
// Focus on the interesting range: 9990..=100_000 and 0..=20_000
match y % 4 {
0 => 9990 + (y % 20), // near boundary
1 => 10000 + (y % 90_000), // large years
2 => y % 20_000, // general range
_ => 2_147_485_540 + (y % 10), // near GNU_MAX_YEAR
}
}
fuzz_target!(|input: Input| {
let year = clamp_year(input.input_year);
let month = (input.month % 12) + 1;
let day = (input.day % 28) + 1;
let date_str = format!("{year:04}-{month:02}-{day:02} {}", input.suffix);
// Test parse_datetime (uses current time as base).
if input.try_no_base {
let _ = parse_datetime::parse_datetime(&date_str);
}
// Test parse_datetime_at_date with a controlled base.
let by = base_year(input.base_year_selector);
if let Ok(base) = DateTime::new(by, 1, 1, 0, 0, 0, 0) {
if let Ok(base) = base.to_zoned(TimeZone::UTC) {
let _ = parse_datetime::parse_datetime_at_date(base, &date_str);
}
}
// Also try a bare large year as a pure number.
let bare = format!("{year}");
let _ = parse_datetime::parse_datetime(&bare);
});