Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/uu/dd/src/bufferedoutput.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ impl<'a> BufferedOutput<'a> {
/// Add partial block buffering to the given block writer.
///
/// The internal buffer size is at most the value of `obs` as
/// defined in `inner`.
pub(crate) fn new(inner: Output<'a>) -> Self {
/// defined in `inner`. `obs` may be huge, so the allocation can fail
/// without aborting: an oversized `obs` returns an error (like GNU `dd`).
pub(crate) fn new(inner: Output<'a>) -> std::io::Result<Self> {
let obs = inner.settings.obs;
Self {
inner,
buf: Vec::with_capacity(obs),
}
let mut buf = Vec::new();
buf.try_reserve(obs)?;
Ok(Self { inner, buf })
}

pub(crate) fn discard_cache(&self, offset: u64, len: u64) {
Expand Down Expand Up @@ -118,7 +118,7 @@ mod tests {
dst: Dest::Sink,
settings: &settings,
};
let mut output = BufferedOutput::new(inner);
let mut output = BufferedOutput::new(inner).unwrap();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why using unwrap here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap will introduce panics again that is i see, which we want suspose to prevent we spent hours to search for panics, so please do'nt use panic functions handle the Result

let wstat = output.write_blocks(&[]).unwrap();
assert_eq!(wstat.writes_complete, 0);
assert_eq!(wstat.writes_partial, 0);
Expand All @@ -136,7 +136,7 @@ mod tests {
dst: Dest::Sink,
settings: &settings,
};
let mut output = BufferedOutput::new(inner);
let mut output = BufferedOutput::new(inner).unwrap();
let wstat = output.write_blocks(b"ab").unwrap();
assert_eq!(wstat.writes_complete, 0);
assert_eq!(wstat.writes_partial, 0);
Expand All @@ -154,7 +154,7 @@ mod tests {
dst: Dest::Sink,
settings: &settings,
};
let mut output = BufferedOutput::new(inner);
let mut output = BufferedOutput::new(inner).unwrap();
let wstat = output.write_blocks(b"abcd").unwrap();
assert_eq!(wstat.writes_complete, 1);
assert_eq!(wstat.writes_partial, 0);
Expand Down
7 changes: 4 additions & 3 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {

// Add partial block buffering, if needed.
let mut o = if o.settings.buffered {
BlockWriter::Buffered(BufferedOutput::new(o))
BlockWriter::Buffered(BufferedOutput::new(o)?)
} else {
BlockWriter::Unbuffered(o)
};
Expand Down Expand Up @@ -1411,8 +1411,9 @@ fn read_helper(i: &mut Input, buf: &mut Vec<u8>, bsize: usize) -> io::Result<Rea
// https://en.wikipedia.org/wiki/Least_common_multiple#Using_the_greatest_common_divisor
fn calc_bsize(ibs: usize, obs: usize) -> usize {
let gcd = Gcd::gcd(ibs, obs);
// calculate the lcm from gcd
(ibs / gcd) * obs
// calculate the lcm from gcd; saturate so an oversized product fails at
// allocation instead of panicking here
(ibs / gcd).saturating_mul(obs)
}

/// Calculate the buffer size appropriate for this loop iteration, respecting
Expand Down
8 changes: 8 additions & 0 deletions src/uu/dd/src/parseargs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ impl Parser {
if num == 0 {
return Err(ParseError::InvalidNumber(val.to_string()));
}
// GNU rejects a block size >= i64::MAX (intmax_t); otherwise the value
// overflows later when the buffer is computed or allocated.
if num >= i64::MAX as u64 {
return Err(ParseError::InvalidNumberWithErrMsg(
val.to_string(),
"Value too large for defined data type".to_string(),
));
}
num.try_into()
.map_err(|_| ParseError::BsOutOfRange(arg.to_string()))
}
Expand Down
29 changes: 29 additions & 0 deletions tests/by-util/test_dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,35 @@ fn test_out_of_memory_skip() {
.stderr_contains("memory");
}

#[test]
fn test_huge_block_size_is_rejected_without_panicking() {
// Regression test for #12844: a block size >= i64::MAX used to panic
// ("attempt to multiply with overflow"); it must be rejected cleanly.
for arg in [
"bs=9223372036854775807", // i64::MAX
"ibs=99999999999999999999", // larger than u64::MAX
"obs=18446744073709551616", // u64::MAX + 1
"cbs=9223372036854775808", // i64::MAX + 1
"ibs=1778172772721772786161B", // overflows via the 'B' suffix
] {
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
// ("memory allocation of N bytes failed"); it must fail gracefully instead.
new_ucmd!()
.arg("obs=1PB")
.fails_with_code(1)
.stderr_contains("memory");
}

#[test]
fn test_stdin_stdout() {
let input = build_ascii_block(521);
Expand Down
Loading