Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
21 changes: 20 additions & 1 deletion src/uucore/src/lib/features/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl Display for FormatError {
Self::InvalidPrecision(precision) => write!(f, "invalid precision: '{precision}'"),
// TODO: Error message below needs some work
Self::WrongSpecType => write!(f, "wrong % directive type was given"),
Self::IoError(_) => write!(f, "io error"),
Self::IoError(_) => write!(f, "write error"),
Self::NoMoreArguments => write!(f, "no more arguments"),
Self::InvalidArgument(_) => write!(f, "invalid argument"),
Self::MissingHex => write!(f, "missing hexadecimal number in escape"),
Expand All @@ -127,6 +127,25 @@ impl Display for FormatError {
}
}

/// Maximum width for formatting to prevent memory allocation panics.
/// Rust's formatter will panic when trying to allocate memory for very large widths.
/// This limit is somewhat arbitrary but should be well above any practical use case
/// while still preventing formatter panics.
const MAX_FORMAT_WIDTH: usize = 1_000_000;

/// Check if a width is too large for formatting.
/// Returns an error if the width exceeds MAX_FORMAT_WIDTH.
fn check_width(width: usize) -> std::io::Result<()> {
if width > MAX_FORMAT_WIDTH {
Err(std::io::Error::new(
std::io::ErrorKind::OutOfMemory,
"formatting width too large",
))
} else {
Ok(())
}
}

/// A single item to format
pub enum FormatItem<C: FormatChar> {
/// A format specifier
Expand Down
8 changes: 6 additions & 2 deletions src/uucore/src/lib/features/format/num_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ fn strip_fractional_zeroes_and_dot(s: &mut String) {
fn write_output(
mut writer: impl Write,
sign_indicator: String,
mut s: String,
s: String,
width: usize,
alignment: NumberAlignment,
) -> std::io::Result<()> {
Expand All @@ -706,13 +706,17 @@ fn write_output(
// by storing remaining_width indicating the actual width needed.
// Using min() because self.width could be 0, 0usize - 1usize should be avoided
let remaining_width = width - min(width, sign_indicator.len());

// Check if the width is too large for formatting
super::check_width(remaining_width)?;

match alignment {
NumberAlignment::Left => write!(writer, "{sign_indicator}{s:<remaining_width$}"),
NumberAlignment::RightSpace => {
let is_sign = sign_indicator.starts_with('-') || sign_indicator.starts_with('+'); // When sign_indicator is in ['-', '+']
if is_sign && remaining_width > 0 {
// Make sure sign_indicator is just next to number, e.g. "% +5.1f" 1 ==> $ +1.0
s = sign_indicator + s.as_str();
let s = sign_indicator + s.as_str();
write!(writer, "{s:>width$}", width = remaining_width + 1) // Since we now add sign_indicator and s together, plus 1
} else {
write!(writer, "{sign_indicator}{s:>remaining_width$}")
Expand Down
4 changes: 4 additions & 0 deletions src/uucore/src/lib/features/format/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,10 @@ fn write_padded(
left: bool,
) -> Result<(), FormatError> {
let padlen = width.saturating_sub(text.len());

// Check if the padding length is too large for formatting
super::check_width(padlen).map_err(FormatError::IoError)?;

if left {
writer.write_all(text)?;
write!(writer, "{: <padlen$}", "")
Expand Down
21 changes: 21 additions & 0 deletions tests/by-util/test_printf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1461,3 +1461,24 @@ fn test_emoji_formatting() {
.succeeds()
.stdout_only("Status: Success 🚀 🎯 Count: 42\n");
}

#[test]
fn test_large_width_format() {
// Test that extremely large width specifications fail gracefully with an error
// rather than panicking. This tests the fix for the printf-surprise.sh GNU test.
// When printf tries to format with a width of 20 million, it should return
// an error message and exit code 1, not panic with exit code 101.
let test_cases = [
("%20000000f", "0"), // float formatting
("%10000000s", "test"), // string formatting
("%15000000d", "42"), // integer formatting
];

for (format, arg) in test_cases {
new_ucmd!()
.args(&[format, arg])
.fails_with_code(1)
.stderr_contains("write error")
.stdout_is("");
}
}
Loading