From 13d16c4095c95b663bdf490aab77f9ab0cb3e4fb Mon Sep 17 00:00:00 2001 From: mistaste Date: Mon, 15 Jun 2026 01:10:37 +0300 Subject: [PATCH] dd: don't panic on huge count/skip/seek operands A very large `count=`, `skip=`, `seek=`, `iseek=` or `oseek=` operand overflowed an internal `blocks * block_size` multiplication and aborted the process: $ dd if=a of=b count=177817277272177278 thread 'main' panicked at src/uu/dd/src/dd.rs: attempt to multiply with overflow `Num::to_bytes` now uses `saturating_mul`, so an out-of-range block count saturates to `u64::MAX` and is caught by the existing `> i64::MAX` (intmax_t) limit checks for `skip`/`seek` instead of panicking. The same `i64::MAX` limit is now also enforced for `count`, which previously had no check and overflowed later in `calc_loop_bsize`. These operands now report a clean error, matching GNU: $ dd count=177817277272177278 dd: invalid number: '...': Value too large for defined data type Fixes #12843 Fixes #12845 Fixes #12846 Fixes #12848 --- src/uu/dd/src/dd.rs | 5 ++++- src/uu/dd/src/parseargs.rs | 10 ++++++++++ tests/by-util/test_dd.rs | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 782d2664c21..a173a8875b2 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -161,7 +161,10 @@ impl Num { fn to_bytes(self, block_size: u64) -> u64 { match self { - Self::Blocks(n) => n * block_size, + // Saturate on overflow: a byte count above `u64::MAX` is far beyond + // the `i64::MAX` (intmax_t) limit that callers enforce, so clamping + // here turns a would-be panic into that clean "too large" error. + Self::Blocks(n) => n.saturating_mul(block_size), Self::Bytes(n) => n, } } diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index a2bc1103651..3e70cd9c70a 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -266,6 +266,16 @@ impl Parser { } let count = self.count.map(|c| c.force_bytes_if(self.iflag.count_bytes)); + // GNU coreutils has a limit of i64 (intmax_t) + if let Some(count) = count { + let count_bytes = count.to_bytes(ibs as u64); + if count_bytes > i64::MAX as u64 { + return Err(ParseError::InvalidNumberWithErrMsg( + format!("{count_bytes}"), + "Value too large for defined data type".to_string(), + )); + } + } Ok(Settings { skip, diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 309a93ac020..c77e3cb617e 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -132,6 +132,26 @@ fn test_out_of_memory_skip() { .stderr_contains("memory"); } +#[test] +fn test_large_numeric_operands_do_not_panic() { + // Regression test for GH #12843, #12845, #12846, #12848: a huge + // count/skip/seek/iseek/oseek operand overflowed an internal multiplication + // and panicked instead of reporting a clean "too large" error. + for operand in [ + "count=177817277272177278", + "skip=99999999999999999999", + "seek=99999999999999999999", + "iseek=166727721627616621762772", + "oseek=166727721627616621762772", + ] { + new_ucmd!() + .arg(operand) + .pipe_in("") + .fails_with_code(1) + .stderr_contains("Value too large for defined data type"); + } +} + #[test] fn test_stdin_stdout() { let input = build_ascii_block(521);