Skip to content

Commit 12b30cb

Browse files
mattsu2020sylvestre
authored andcommitted
fix(date): add error handling for format modifier width overflow
Added proper error handling for format modifier width calculations that could overflow or fail allocation. The change replaces generic error messages with a specific error type `FieldWidthTooLarge` that includes the problematic width and specifier values, improving error reporting for date format operations.
1 parent 23bec54 commit 12b30cb

4 files changed

Lines changed: 39 additions & 25 deletions

File tree

src/uu/date/locales/en-US.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ date-error-setting-date-not-supported-redox = setting the date is not supported
106106
date-error-cannot-set-date = cannot set date
107107
date-error-extra-operand = extra operand '{$operand}'
108108
date-error-write = write error: {$error}
109+
date-error-format-modifier-width-too-large = format modifier width '{$width}' is too large for specifier '%{$specifier}'
109110
date-error-format-missing-plus = the argument {$arg} lacks a leading '+';
110111
when using an option to specify date(s), any non-option
111112
argument must be a format string beginning with '+'

src/uu/date/locales/fr-FR.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ date-error-setting-date-not-supported-redox = la définition de la date n'est pa
101101
date-error-cannot-set-date = impossible de définir la date
102102
date-error-extra-operand = opérande supplémentaire '{$operand}'
103103
date-error-write = erreur d'écriture: {$error}
104+
date-error-format-modifier-width-too-large = la largeur du modificateur de format '{$width}' est trop grande pour le spécificateur '%{$specifier}'
104105
date-error-format-missing-plus = l'argument {$arg} ne commence pas par un signe '+';
105106
lorsqu'une option est utilisée pour spécifier une ou plusieurs dates, tout argument autre
106107
qu'une option doit être une chaîne de format commençant par un signe '+'.

src/uu/date/src/format_modifiers.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,30 @@ use jiff::fmt::strtime::{BrokenDownTime, Config, PosixCustom};
3838
use regex::Regex;
3939
use std::fmt;
4040
use std::sync::OnceLock;
41+
use uucore::translate;
4142

4243
/// Error type for format modifier operations
4344
#[derive(Debug)]
4445
pub enum FormatError {
4546
/// Error from the underlying jiff library
4647
JiffError(jiff::Error),
47-
/// Custom error message
48-
Custom(String),
48+
/// Field width calculation overflowed or required allocation failed
49+
FieldWidthTooLarge { width: usize, specifier: String },
4950
}
5051

5152
impl fmt::Display for FormatError {
5253
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5354
match self {
5455
Self::JiffError(e) => write!(f, "{e}"),
55-
Self::Custom(s) => write!(f, "{s}"),
56+
Self::FieldWidthTooLarge { width, specifier } => write!(
57+
f,
58+
"{}",
59+
translate!(
60+
"date-error-format-modifier-width-too-large",
61+
"width" => width,
62+
"specifier" => specifier
63+
)
64+
),
5665
}
5766
}
5867
}
@@ -63,12 +72,6 @@ impl From<jiff::Error> for FormatError {
6372
}
6473
}
6574

66-
const ERR_FIELD_WIDTH_TOO_LARGE: &str = "field width too large";
67-
68-
fn width_too_large_error() -> FormatError {
69-
FormatError::Custom(ERR_FIELD_WIDTH_TOO_LARGE.to_string())
70-
}
71-
7275
/// Regex to match format specifiers with optional modifiers
7376
/// Pattern: % \[flags\] \[width\] specifier
7477
/// Flags: -, _, 0, ^, #, +
@@ -392,28 +395,38 @@ fn apply_modifiers(
392395
// Zero padding: sign first, then zeros (e.g., "-0022")
393396
let sign = result.chars().next().unwrap();
394397
let rest = &result[1..];
395-
let target_len = result
396-
.len()
397-
.checked_add(padding)
398-
.ok_or_else(width_too_large_error)?;
398+
let target_len = result.len().checked_add(padding).ok_or_else(|| {
399+
FormatError::FieldWidthTooLarge {
400+
width,
401+
specifier: specifier.to_string(),
402+
}
403+
})?;
399404
let mut padded = String::new();
400405
padded
401406
.try_reserve(target_len)
402-
.map_err(|_| width_too_large_error())?;
407+
.map_err(|_| FormatError::FieldWidthTooLarge {
408+
width,
409+
specifier: specifier.to_string(),
410+
})?;
403411
padded.push(sign);
404412
padded.extend(std::iter::repeat_n('0', padding));
405413
padded.push_str(rest);
406414
result = padded;
407415
} else {
408416
// Default: pad on the left (e.g., " -22" or " 1999")
409-
let target_len = result
410-
.len()
411-
.checked_add(padding)
412-
.ok_or_else(width_too_large_error)?;
417+
let target_len = result.len().checked_add(padding).ok_or_else(|| {
418+
FormatError::FieldWidthTooLarge {
419+
width,
420+
specifier: specifier.to_string(),
421+
}
422+
})?;
413423
let mut padded = String::new();
414424
padded
415425
.try_reserve(target_len)
416-
.map_err(|_| width_too_large_error())?;
426+
.map_err(|_| FormatError::FieldWidthTooLarge {
427+
width,
428+
specifier: specifier.to_string(),
429+
})?;
417430
padded.extend(std::iter::repeat_n(pad_char, padding));
418431
padded.push_str(&result);
419432
result = padded;
@@ -775,7 +788,8 @@ mod tests {
775788
let err = apply_modifiers("x", "", usize::MAX, "c", true).unwrap_err();
776789
assert!(matches!(
777790
err,
778-
FormatError::Custom(message) if message == ERR_FIELD_WIDTH_TOO_LARGE
791+
FormatError::FieldWidthTooLarge { width, specifier }
792+
if width == usize::MAX && specifier == "c"
779793
));
780794
}
781795
}

tests/by-util/test_date.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2509,12 +2509,10 @@ fn test_date_format_modifier_edge_cases() {
25092509

25102510
#[test]
25112511
fn test_date_format_modifier_huge_width_fails_without_abort() {
2512+
// GNU date also exits with failure for extremely large width.
2513+
// Assert exit code only to avoid coupling to implementation-specific error text.
25122514
let format = format!("+%{}c", usize::MAX);
2513-
new_ucmd!()
2514-
.arg(&format)
2515-
.fails()
2516-
.code_is(1)
2517-
.stderr_contains("field width too large");
2515+
new_ucmd!().arg(&format).fails().code_is(1);
25182516
}
25192517

25202518
// Tests for --debug flag

0 commit comments

Comments
 (0)