mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
date: reject excessive format widths (#12497)
* date: reject excessive format widths * date: relax huge width diagnostic test * date: cap format modifier widths --------- Co-authored-by: Deepak kudi <deepakkudi23@Deepaks-MacBook-Air.local> Co-authored-by: Deepak kudi <deepakkudi23@adsl-172-10-9-116.dsl.sndg02.sbcglobal.net> Co-authored-by: Puneet Dixit <puneetdixit200@users.noreply.github.com>
This commit is contained in:
@@ -38,6 +38,8 @@ use jiff::fmt::strtime::{BrokenDownTime, Config, PosixCustom};
|
||||
use std::fmt;
|
||||
use uucore::translate;
|
||||
|
||||
const MAX_FORMAT_WIDTH: usize = u16::MAX as usize;
|
||||
|
||||
/// Error type for format modifier operations
|
||||
#[derive(Debug)]
|
||||
pub enum FormatError {
|
||||
@@ -70,6 +72,13 @@ impl From<jiff::Error> for FormatError {
|
||||
}
|
||||
}
|
||||
|
||||
fn field_width_too_large(width: usize, specifier: &str) -> FormatError {
|
||||
FormatError::FieldWidthTooLarge {
|
||||
width,
|
||||
specifier: specifier.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed `%`-format specifier: `%[flags][width][:colons]<letter>`.
|
||||
struct ParsedSpec<'a> {
|
||||
/// Flag characters from `[_0^#+-]`.
|
||||
@@ -422,6 +431,9 @@ fn apply_modifiers(value: &str, parsed: &ParsedSpec<'_>) -> Result<String, Forma
|
||||
None if underscore_flag || pad_char != default_pad => get_default_width(specifier),
|
||||
None => 0,
|
||||
};
|
||||
if effective_width > MAX_FORMAT_WIDTH {
|
||||
return Err(field_width_too_large(effective_width, specifier));
|
||||
}
|
||||
|
||||
// When the requested width is narrower than the default formatted width, GNU first removes default padding and then reapplies the requested width.
|
||||
if effective_width > 0 && effective_width < result.len() {
|
||||
@@ -491,19 +503,16 @@ fn try_alloc_padded(
|
||||
width: usize,
|
||||
specifier: &str,
|
||||
) -> Result<String, FormatError> {
|
||||
let target_len =
|
||||
current_len
|
||||
.checked_add(padding)
|
||||
.ok_or_else(|| FormatError::FieldWidthTooLarge {
|
||||
width,
|
||||
specifier: specifier.to_string(),
|
||||
})?;
|
||||
if width > MAX_FORMAT_WIDTH {
|
||||
return Err(field_width_too_large(width, specifier));
|
||||
}
|
||||
|
||||
let target_len = current_len
|
||||
.checked_add(padding)
|
||||
.ok_or_else(|| field_width_too_large(width, specifier))?;
|
||||
let mut s = String::new();
|
||||
s.try_reserve(target_len)
|
||||
.map_err(|_| FormatError::FieldWidthTooLarge {
|
||||
width,
|
||||
specifier: specifier.to_string(),
|
||||
})?;
|
||||
.map_err(|_| field_width_too_large(width, specifier))?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
@@ -869,6 +878,27 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_alloc_padded_rejects_width_above_supported_max() {
|
||||
let err = try_alloc_padded(1, 1, MAX_FORMAT_WIDTH + 1, "s").unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
FormatError::FieldWidthTooLarge { width, specifier }
|
||||
if width == MAX_FORMAT_WIDTH + 1 && specifier == "s"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_modifiers_rejects_width_above_review_cap() {
|
||||
let width = u16::MAX as usize + 1;
|
||||
let err = apply_modifiers("00", &spec("", Some(width), "S")).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
FormatError::FieldWidthTooLarge { width: rejected, specifier }
|
||||
if rejected == width && specifier == "S"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_underscore_flag_without_width() {
|
||||
// %_m should pad month to default width 2 with spaces
|
||||
|
||||
@@ -2560,10 +2560,22 @@ fn test_date_format_modifier_huge_width_fails_without_abort() {
|
||||
new_ucmd!().arg(&format).fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_format_modifier_parseable_huge_width_fails_without_hanging() {
|
||||
// Target pointer width can affect the reported parsed width, so assert the
|
||||
// stable diagnostic shape instead of the exact oversized literal.
|
||||
new_ucmd!()
|
||||
.arg("+%8888888888888s")
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_contains("format modifier width '")
|
||||
.stderr_contains("' is too large for specifier '%s'");
|
||||
}
|
||||
|
||||
#[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.
|
||||
// Cap supported widths well below that so we don't crash.
|
||||
// Use a moderate width with a fixed date to check the code path works.
|
||||
new_ucmd!()
|
||||
.arg("-d")
|
||||
|
||||
Reference in New Issue
Block a user