diff --git a/src/uu/date/src/format_modifiers.rs b/src/uu/date/src/format_modifiers.rs index 00e9df77817..8d123936524 100644 --- a/src/uu/date/src/format_modifiers.rs +++ b/src/uu/date/src/format_modifiers.rs @@ -145,7 +145,12 @@ fn format_with_modifiers( // Check if this specifier has modifiers if !flags.is_empty() || !width_str.is_empty() { // Apply modifiers to the formatted value - let width: usize = width_str.parse().unwrap_or(0); + // Cap width to i32::MAX to match GNU behavior, which uses int for width. + // Without this cap, very large widths (e.g., %8888888888r) cause issues. + let width: usize = width_str + .parse::() + .unwrap_or(0) + .min(i32::MAX as usize); let explicit_width = !width_str.is_empty(); let modified = apply_modifiers(&formatted, flags, width, spec, explicit_width); result.push_str(&modified); @@ -710,4 +715,23 @@ mod tests { "GNU: %_C should produce '19', not ' 19' (default width is 2, not 4)" ); } + + #[test] + fn test_large_width_capped() { + // Regression test: very large widths caused OOM. + // We cap width to i32::MAX to match GNU behavior. + // Test with a reasonable width to avoid allocating 2GB in tests. + let value = "12:00:00 AM"; + let result = apply_modifiers(value, "", 100, "r", true); + assert_eq!(result.len(), 100); + + // Verify that the width capping logic works + // The parse and min operations should handle this correctly + let width_str = "8888888888"; + let width = width_str + .parse::() + .unwrap_or(0) + .min(i32::MAX as usize); + assert_eq!(width, i32::MAX as usize); + } } diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index db4637e6273..bd9cced9abd 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -758,6 +758,28 @@ fn test_date_overflow() { .stderr_contains("invalid date"); } +#[test] +fn test_date_format_large_width_no_oom() { + // Regression: very large width like %8888888888r caused OOM. + // GNU caps width to i32::MAX; verify we don't crash. + // Use a moderate width with a fixed date to check the code path works. + new_ucmd!() + .arg("-d") + .arg("2024-01-01") + .arg("+%300S") + .succeeds() + .stdout_is(format!("{}\n", format_args!("{:0>300}", "00"))); + + // Test with a larger width to exercise the code path without producing + // gigabytes of output (the original %8888888888r would produce ~2GB). + new_ucmd!() + .arg("-d") + .arg("2024-01-01") + .arg("+%10000S") + .succeeds() + .stdout_is(format!("{}\n", format_args!("{:0>10000}", "00"))); +} + #[test] fn test_date_parse_from_format() { const FILE: &str = "file-with-dates";