From 0ca776f97a19c1e52baa88b370f3fa3eb1fd6f0a Mon Sep 17 00:00:00 2001 From: Angadi Yashaswini Date: Mon, 20 Jul 2026 23:00:27 +0530 Subject: [PATCH 1/3] dd: avoid overflow converting a block count to bytes --- src/uu/dd/src/dd.rs | 7 +++++-- src/uu/dd/src/parseargs.rs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 0599963e6e3..6ee4c0208af 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 so a block count that exceeds u64 is caught + // by the i64::MAX bound check in `Parser::validate` rather than + // wrapping past it. + Self::Blocks(n) => n.saturating_mul(block_size), Self::Bytes(n) => n, } } @@ -1429,7 +1432,7 @@ fn calc_loop_bsize( Some(Num::Blocks(rmax)) => { let rsofar = rstat.reads_complete + rstat.reads_partial; let rremain = rmax - rsofar; - cmp::min(ideal_bsize as u64, rremain * ibs as u64) as usize + cmp::min(ideal_bsize as u64, rremain.saturating_mul(ibs as u64)) as usize } Some(Num::Bytes(bmax)) => { let bmax: u128 = bmax.into(); diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index cd20771c185..70e3ae79cba 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -655,4 +655,19 @@ mod tests { assert!(matches!(Parser::parse_n(arg), Ok(Num::Bytes(_)))); } } + + #[test] + fn test_skip_seek_block_count_byte_overflow_rejected() { + // A skip/seek in blocks is multiplied by the block size (default 512) + // before the i64::MAX bound check. 2^55 blocks * 512 == 2^64, which + // wrapped past that check to a small value (and panicked in debug). + // It must be rejected as too large instead. + use crate::parseargs::ParseError; + for operand in ["skip=36028797018963968", "seek=36028797018963968"] { + assert!(matches!( + Parser::new().parse([operand]), + Err(ParseError::InvalidNumberWithErrMsg(_, _)) + )); + } + } } From 3797e97cfd79099f9b1f7b639798901e25cc77e4 Mon Sep 17 00:00:00 2001 From: Angadi Yashaswini Date: Tue, 21 Jul 2026 12:11:26 +0530 Subject: [PATCH 2/3] tests: dd: add CLI regression test for skip/seek block count overflow --- tests/by-util/test_dd.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 849c47e227f..829c53118f1 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -146,6 +146,21 @@ fn test_huge_block_size_is_rejected_without_panicking() { } } +#[test] +fn test_huge_skip_seek_block_count_is_rejected_without_panicking() { + // A skip/seek given in blocks is multiplied by the block size (default + // 512) before the i64::MAX bound check. 2^55 blocks * 512 == 2^64 used to + // wrap past that check (panicking in debug builds, silently skipping + // nothing in release builds); it must be rejected cleanly instead. + for arg in ["skip=36028797018963968", "seek=36028797018963968"] { + new_ucmd!() + .arg(arg) + .fails_with_code(1) + .no_stdout() + .stderr_contains("Value too large for defined data type"); + } +} + #[test] fn test_huge_obs_reports_memory_error_instead_of_aborting() { // Regression test for #12847: a valid but huge `obs` used to abort From ef5e5b6792beb14726d2a7f0e1cec3329685d875 Mon Sep 17 00:00:00 2001 From: Angadi Yashaswini Date: Thu, 23 Jul 2026 03:54:16 +0530 Subject: [PATCH 3/3] tests: dd: add CLI regression test for count= block overflow (#12843) --- tests/by-util/test_dd.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 829c53118f1..4ec5479c22e 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -161,6 +161,20 @@ fn test_huge_skip_seek_block_count_is_rejected_without_panicking() { } } +#[test] +fn test_huge_count_block_count_copies_without_panicking() { + // Regression test for #12843: with `count=` given in blocks, the loop + // buffer size was computed as `remaining_blocks * ibs` unchecked, so + // u64::MAX blocks with ibs=2 panicked in debug builds ("attempt to + // multiply with overflow"). The product must saturate instead, letting + // dd copy until EOF as usual. + new_ucmd!() + .args(&["status=none", "ibs=2", "count=18446744073709551615"]) + .pipe_in("a") + .succeeds() + .stdout_only("a"); +} + #[test] fn test_huge_obs_reports_memory_error_instead_of_aborting() { // Regression test for #12847: a valid but huge `obs` used to abort