From abcdcdca534a890ea589096705ad4d21d48a0278 Mon Sep 17 00:00:00 2001 From: weili <541602953@qq.com> Date: Fri, 5 Jun 2026 04:47:40 +0000 Subject: [PATCH] pr: reject a non-positive expand-tab width (-e0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pr -e0` on input containing a tab aborted with a remainder-by-zero panic (`chunk.len() % expand_options.width` in `apply_expand_tab`). The `-e[char][width]` parser accepted a width of `0` — and a negative width via e.g. `-eX-5` — without validation, so the zero/negative reached the chunk arithmetic. Reject `width <= 0` while parsing `-e`, emitting the same invalid-argument error GNU produces (`pr: '-e' extra characters or invalid number in the argument: '0'`, exit 1) instead of crashing. --- src/uu/pr/src/pr.rs | 53 ++++++++++++++++++++++++++++------------ tests/by-util/test_pr.rs | 13 ++++++++++ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index def105ae5..e5a48c72d 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -582,22 +582,43 @@ fn build_options( let expand_tabs = matches .get_one::(options::EXPAND_TABS) .map(|s| { - s.chars().next().map_or(Ok(ExpandTabsOptions::default()), |c| { - if c.is_ascii_digit() { - s - .parse() - .map_err(|_e| PrError::EncounteredErrors { msg: format!("{}\n{}", translate!("pr-error-invalid-expand-tab-argument", "arg" => s), translate!("pr-try-help-message")) }) - .map(|width| ExpandTabsOptions{input_char: TAB, width}) - } else if s.len() > 1 { - s[1..] - .parse() - .map_err(|_e| PrError::EncounteredErrors { msg: format!("{}\n{}", translate!("pr-error-invalid-expand-tab-argument", "arg" => &s[1..]), translate!("pr-try-help-message")) }) - .map(|width| ExpandTabsOptions{input_char: c, width}) - } else { - Ok(ExpandTabsOptions{input_char: c, width: 8}) - } - }) - }).transpose()?; + s.chars() + .next() + .map_or(Ok(ExpandTabsOptions::default()), |c| { + let invalid = |arg: &str| PrError::EncounteredErrors { + msg: format!( + "{}\n{}", + translate!("pr-error-invalid-expand-tab-argument", "arg" => arg), + translate!("pr-try-help-message") + ), + }; + if c.is_ascii_digit() { + let width: i32 = s.parse().map_err(|_e| invalid(s))?; + if width <= 0 { + return Err(invalid(s)); + } + Ok(ExpandTabsOptions { + input_char: TAB, + width, + }) + } else if s.len() > 1 { + let width: i32 = s[1..].parse().map_err(|_e| invalid(&s[1..]))?; + if width <= 0 { + return Err(invalid(&s[1..])); + } + Ok(ExpandTabsOptions { + input_char: c, + width, + }) + } else { + Ok(ExpandTabsOptions { + input_char: c, + width: 8, + }) + } + }) + }) + .transpose()?; let double_space = matches.get_flag(options::DOUBLE_SPACE); diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 26403b6f4..d1b0c7575 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -889,3 +889,16 @@ fn test_zero_columns_shortcut() { .fails_with_code(1) .stderr_contains("pr: invalid --column argument '0'"); } + +#[test] +fn test_zero_expand_tab_width() { + let expected = "pr: '-e' extra characters or invalid number in the argument: ‘0’\nTry 'pr --help' for more information.\n"; + new_ucmd!() + .arg("-e0") + .fails_with_code(1) + .stderr_only(expected); + new_ucmd!() + .arg("-eX0") + .fails_with_code(1) + .stderr_only(expected); +}