Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/uu/date/src/format_modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<usize>()
.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);
Expand Down Expand Up @@ -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::<usize>()
.unwrap_or(0)
.min(i32::MAX as usize);
assert_eq!(width, i32::MAX as usize);
}
}
22 changes: 22 additions & 0 deletions tests/by-util/test_date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading