Skip to content

Commit 9c1cbca

Browse files
committed
date: extract try_alloc_padded helper and add large-width test
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
1 parent a51cf70 commit 9c1cbca

2 files changed

Lines changed: 59 additions & 26 deletions

File tree

src/uu/date/src/format_modifiers.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -395,38 +395,14 @@ fn apply_modifiers(
395395
// Zero padding: sign first, then zeros (e.g., "-0022")
396396
let sign = result.chars().next().unwrap();
397397
let rest = &result[1..];
398-
let target_len = result.len().checked_add(padding).ok_or_else(|| {
399-
FormatError::FieldWidthTooLarge {
400-
width: effective_width,
401-
specifier: specifier.to_string(),
402-
}
403-
})?;
404-
let mut padded = String::new();
405-
padded
406-
.try_reserve(target_len)
407-
.map_err(|_| FormatError::FieldWidthTooLarge {
408-
width: effective_width,
409-
specifier: specifier.to_string(),
410-
})?;
398+
let mut padded = try_alloc_padded(result.len(), padding, effective_width, specifier)?;
411399
padded.push(sign);
412400
padded.extend(std::iter::repeat_n('0', padding));
413401
padded.push_str(rest);
414402
result = padded;
415403
} else {
416404
// Default: pad on the left (e.g., " -22" or " 1999")
417-
let target_len = result.len().checked_add(padding).ok_or_else(|| {
418-
FormatError::FieldWidthTooLarge {
419-
width: effective_width,
420-
specifier: specifier.to_string(),
421-
}
422-
})?;
423-
let mut padded = String::new();
424-
padded
425-
.try_reserve(target_len)
426-
.map_err(|_| FormatError::FieldWidthTooLarge {
427-
width: effective_width,
428-
specifier: specifier.to_string(),
429-
})?;
405+
let mut padded = try_alloc_padded(result.len(), padding, effective_width, specifier)?;
430406
padded.extend(std::iter::repeat_n(pad_char, padding));
431407
padded.push_str(&result);
432408
result = padded;
@@ -436,6 +412,30 @@ fn apply_modifiers(
436412
Ok(result)
437413
}
438414

415+
/// Allocate a `String` with enough capacity for `current_len + padding`,
416+
/// returning `FieldWidthTooLarge` on arithmetic overflow or allocation failure.
417+
fn try_alloc_padded(
418+
current_len: usize,
419+
padding: usize,
420+
width: usize,
421+
specifier: &str,
422+
) -> Result<String, FormatError> {
423+
let target_len =
424+
current_len
425+
.checked_add(padding)
426+
.ok_or_else(|| FormatError::FieldWidthTooLarge {
427+
width,
428+
specifier: specifier.to_string(),
429+
})?;
430+
let mut s = String::new();
431+
s.try_reserve(target_len)
432+
.map_err(|_| FormatError::FieldWidthTooLarge {
433+
width,
434+
specifier: specifier.to_string(),
435+
})?;
436+
Ok(s)
437+
}
438+
439439
#[cfg(test)]
440440
mod tests {
441441
use super::*;

tests/by-util/test_date.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2432,6 +2432,39 @@ fn test_date_format_modifier_huge_width_fails_without_abort() {
24322432
new_ucmd!().arg(&format).fails().code_is(1);
24332433
}
24342434

2435+
#[test]
2436+
fn test_date_format_large_width_no_oom() {
2437+
// Regression: very large width like %8888888888r caused OOM.
2438+
// GNU caps width to i32::MAX; verify we don't crash.
2439+
// Use a moderate width with a fixed date to check the code path works.
2440+
new_ucmd!()
2441+
.arg("-d")
2442+
.arg("2024-01-01")
2443+
.arg("+%300S")
2444+
.succeeds()
2445+
.stdout_is(format!("{}\n", format_args!("{:0>300}", "00")));
2446+
2447+
// Test with a larger width to exercise the code path without producing
2448+
// gigabytes of output (the original %8888888888r would produce ~2GB).
2449+
new_ucmd!()
2450+
.arg("-d")
2451+
.arg("2024-01-01")
2452+
.arg("+%10000S")
2453+
.succeeds()
2454+
.stdout_is(format!("{}\n", format_args!("{:0>10000}", "00")));
2455+
2456+
// Mixed literal text with multiple width-modified specifiers.
2457+
// 2024-01-01 is Monday (day-of-week 1).
2458+
// %2u → "01", literal "ueuu", %6666u → "1" zero-padded to 6666, literal "-r".
2459+
let expected = format!("01ueuu{}-r\n", format_args!("{:0>6666}", "1"));
2460+
new_ucmd!()
2461+
.arg("-d")
2462+
.arg("2024-01-01")
2463+
.arg("+%2uueuu%6666u-r")
2464+
.succeeds()
2465+
.stdout_is(expected);
2466+
}
2467+
24352468
// Tests for format modifier edge cases (flags without explicit width)
24362469
#[test]
24372470
fn test_date_format_modifier_edge_cases() {

0 commit comments

Comments
 (0)