From b6b59a1b052718aca81e4645cee5ae28adb6453b Mon Sep 17 00:00:00 2001 From: Coro Date: Wed, 12 Aug 2026 14:37:02 -0600 Subject: [PATCH] nl: reject line number width above INT_MAX via clap value_parser Per review feedback, bound -w with clap value_parser to match GNU's C int limit instead of a manual check, so a huge width is rejected at parse time rather than aborting later with a capacity overflow. Adds a regression test. Fixes #13347. --- src/uu/nl/src/helper.rs | 6 ++++-- src/uu/nl/src/nl.rs | 5 ++++- tests/by-util/test_nl.rs | 11 +++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/uu/nl/src/helper.rs b/src/uu/nl/src/helper.rs index d801ca713e1..28274ca8e62 100644 --- a/src/uu/nl/src/helper.rs +++ b/src/uu/nl/src/helper.rs @@ -67,9 +67,11 @@ pub fn parse_options(settings: &mut crate::Settings, opts: &clap::ArgMatches) -> Some(Ok(style)) => settings.footer_numbering = style, Some(Err(message)) => errs.push(message), } - match opts.get_one::(options::NUMBER_WIDTH) { + // The upper bound is enforced by clap (see the argument definition); only + // the zero case is reported here, to match GNU's message for it. + match opts.get_one::(options::NUMBER_WIDTH) { None => {} - Some(num) if *num > 0 => settings.number_width = *num, + Some(num) if *num > 0 => settings.number_width = *num as usize, Some(_) => errs.push(translate!("nl-error-invalid-line-width", "value" => "0")), } if let Some(num) = opts.get_one::(options::JOIN_BLANK_LINES) { diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index b2ce91872d2..382ee0b0c49 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -376,7 +376,10 @@ pub fn uu_app() -> Command { .long(options::NUMBER_WIDTH) .help(translate!("nl-help-number-width")) .value_name("NUMBER") - .value_parser(clap::value_parser!(usize)), + // Bound the width to a C int like GNU. This rejects a huge width + // up front instead of letting a later `" ".repeat(width + 1)` + // abort with a capacity overflow (#13347). + .value_parser(clap::value_parser!(u64).range(..=i32::MAX as u64)), ) } diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index dcfafd32018..62b7b6bee01 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -187,6 +187,17 @@ fn test_number_width_zero() { } } +#[test] +fn test_number_width_above_int_max_is_rejected() { + // A field width above i32::MAX is rejected up front by clap, matching GNU's + // C-int bound, instead of aborting with a capacity overflow (#13347). + new_ucmd!() + .args(&["-w", "2147483648"]) + .pipe_in("x\n") + .fails() + .stderr_contains("is not in 0..=2147483647"); +} + #[test] fn test_invalid_number_width() { for arg in ["-winvalid", "--number-width=invalid"] {