From 1b26cd8a12f2635678638d0bbb427f5c5a49bc09 Mon Sep 17 00:00:00 2001 From: weili <541602953@qq.com> Date: Fri, 5 Jun 2026 04:17:51 +0000 Subject: [PATCH] split: reject a zero chunk count for -n l/N and r/N `split -n l/0` and `split -n r/0` aborted with a divide-by-zero panic (`num_bytes / num_chunks` in `n_chunks_by_line` and `i % num_chunks` in `n_chunks_by_line_round_robin`). `NumberType::from` validated `N > 0` for the bare `N` / `K/N` forms but not for the `l/N` and `r/N` forms, so a zero count reached the chunk arithmetic. Reject `num_chunks == 0` in those two branches, returning `NumberTypeError::NumberOfChunks` like the bare-`N` form, which matches GNU (`split: invalid number of chunks: '0'`, exit 1) and validates up front rather than crashing while reading input. --- src/uu/split/src/strategy.rs | 18 ++++++++++++++++++ tests/by-util/test_split.rs | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/uu/split/src/strategy.rs b/src/uu/split/src/strategy.rs index 6d567637d07..e35f62d3a7f 100644 --- a/src/uu/split/src/strategy.rs +++ b/src/uu/split/src/strategy.rs @@ -144,6 +144,9 @@ impl NumberType { (Some("l"), Some(n_str), None, None) => { let num_chunks = parse_size_u64(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; + if num_chunks == 0 { + return Err(NumberTypeError::NumberOfChunks(n_str.to_string())); + } Ok(Self::Lines(num_chunks)) } (Some("l"), Some(k_str), Some(n_str), None) => { @@ -159,6 +162,9 @@ impl NumberType { (Some("r"), Some(n_str), None, None) => { let num_chunks = parse_size_u64(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; + if num_chunks == 0 { + return Err(NumberTypeError::NumberOfChunks(n_str.to_string())); + } Ok(Self::RoundRobin(num_chunks)) } (Some("r"), Some(k_str), Some(n_str), None) => { @@ -345,6 +351,18 @@ mod tests { NumberType::from("r/xyz").unwrap_err(), NumberTypeError::NumberOfChunks("xyz".to_string()) ); + assert_eq!( + NumberType::from("0").unwrap_err(), + NumberTypeError::NumberOfChunks("0".to_string()) + ); + assert_eq!( + NumberType::from("l/0").unwrap_err(), + NumberTypeError::NumberOfChunks("0".to_string()) + ); + assert_eq!( + NumberType::from("r/0").unwrap_err(), + NumberTypeError::NumberOfChunks("0".to_string()) + ); assert_eq!( NumberType::from("r/123/xyz").unwrap_err(), NumberTypeError::NumberOfChunks("xyz".to_string()) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index a5d67402396..8b598c49e92 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -633,6 +633,22 @@ fn test_split_obs_lines_as_other_option_value() { .stderr_contains("split: invalid number of chunks: '-e200'\n"); } +#[test] +fn test_split_chunks_by_line_or_round_robin_zero_chunks() { + let scene = TestScenario::new(util_name!()); + scene.fixtures.write("file", "a\nb\nc\nd\n"); + scene + .ucmd() + .args(&["-n", "l/0", "file"]) + .fails_with_code(1) + .stderr_only("split: invalid number of chunks: '0'\n"); + scene + .ucmd() + .args(&["-n", "r/0", "file"]) + .fails_with_code(1) + .stderr_only("split: invalid number of chunks: '0'\n"); +} + /// Test for using more than one obsolete lines option (standalone) /// last one wins #[test]