Skip to content

Commit ef3c593

Browse files
committed
date: cap format width to i32::MAX to prevent OOM
1 parent 57f69ad commit ef3c593

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

src/uu/date/src/format_modifiers.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,12 @@ fn format_with_modifiers(
145145
// Check if this specifier has modifiers
146146
if !flags.is_empty() || !width_str.is_empty() {
147147
// Apply modifiers to the formatted value
148-
let width: usize = width_str.parse().unwrap_or(0);
148+
// Cap width to i32::MAX to match GNU behavior, which uses int for width.
149+
// Without this cap, very large widths (e.g., %8888888888r) cause issues.
150+
let width: usize = width_str
151+
.parse::<usize>()
152+
.unwrap_or(0)
153+
.min(i32::MAX as usize);
149154
let explicit_width = !width_str.is_empty();
150155
let modified = apply_modifiers(&formatted, flags, width, spec, explicit_width);
151156
result.push_str(&modified);
@@ -710,4 +715,23 @@ mod tests {
710715
"GNU: %_C should produce '19', not ' 19' (default width is 2, not 4)"
711716
);
712717
}
718+
719+
#[test]
720+
fn test_large_width_capped() {
721+
// Regression test: very large widths caused OOM.
722+
// We cap width to i32::MAX to match GNU behavior.
723+
// Test with a reasonable width to avoid allocating 2GB in tests.
724+
let value = "12:00:00 AM";
725+
let result = apply_modifiers(value, "", 100, "r", true);
726+
assert_eq!(result.len(), 100);
727+
728+
// Verify that the width capping logic works
729+
// The parse and min operations should handle this correctly
730+
let width_str = "8888888888";
731+
let width = width_str
732+
.parse::<usize>()
733+
.unwrap_or(0)
734+
.min(i32::MAX as usize);
735+
assert_eq!(width, i32::MAX as usize);
736+
}
713737
}

tests/by-util/test_date.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,28 @@ fn test_date_overflow() {
758758
.stderr_contains("invalid date");
759759
}
760760

761+
#[test]
762+
fn test_date_format_large_width_no_oom() {
763+
// Regression: very large width like %8888888888r caused OOM.
764+
// GNU caps width to i32::MAX; verify we don't crash.
765+
// Use a moderate width with a fixed date to check the code path works.
766+
new_ucmd!()
767+
.arg("-d")
768+
.arg("2024-01-01")
769+
.arg("+%300S")
770+
.succeeds()
771+
.stdout_is(format!("{}\n", format_args!("{:0>300}", "00")));
772+
773+
// Test with a larger width to exercise the code path without producing
774+
// gigabytes of output (the original %8888888888r would produce ~2GB).
775+
new_ucmd!()
776+
.arg("-d")
777+
.arg("2024-01-01")
778+
.arg("+%10000S")
779+
.succeeds()
780+
.stdout_is(format!("{}\n", format_args!("{:0>10000}", "00")));
781+
}
782+
761783
#[test]
762784
fn test_date_parse_from_format() {
763785
const FILE: &str = "file-with-dates";

0 commit comments

Comments
 (0)