From 84d4f24b8c0e2d1dd24b6319ba37ea0fcb90a9e8 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 4 Feb 2022 21:42:21 -0500 Subject: [PATCH 1/2] dd: avoid infinite loop in Input::force_fill() Avoid an infinite loop in `Input::force_fill()` when the input has fewer bytes than are being requested to be read from the input. --- src/uu/dd/src/dd.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 54e3190ce..ac7b50d1b 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -80,8 +80,7 @@ impl Input { }; if let Some(amt) = skip { - let mut buf = vec![BUF_INIT_BYTE; amt]; - i.force_fill(&mut buf, amt) + i.force_fill(amt.try_into().unwrap()) .map_err_context(|| "failed to read input".to_string())?; } @@ -264,17 +263,19 @@ impl Input { }) } - /// Force-fills a buffer, ignoring zero-length reads which would otherwise be - /// interpreted as EOF. - /// Note: This will not return unless the source (eventually) produces - /// enough bytes to meet target_len. - fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> std::io::Result { - let mut base_idx = 0; - while base_idx < target_len { - base_idx += self.read(&mut buf[base_idx..target_len])?; - } - - Ok(base_idx) + /// Read the specified number of bytes from this reader. + /// + /// On success, this method returns the number of bytes read. If + /// this reader has fewer than `n` bytes available, then it reads + /// as many as possible. In that case, this method returns a + /// number less than `n`. + /// + /// # Errors + /// + /// If there is a problem reading. + fn force_fill(&mut self, n: u64) -> std::io::Result { + let mut buf = vec![]; + self.take(n).read_to_end(&mut buf) } } From 9f8ec676c53e1f4f4a01d228972601c4e283e0ad Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 4 Feb 2022 21:44:26 -0500 Subject: [PATCH 2/2] dd: show warning if skipping past end of input Show a warning if the `skip=N` command-line argument would cause `dd` to skip past the end of the input. For example: $ printf "abcd" | dd bs=1 skip=5 count=0 status=noxfer 'standard input': cannot skip to specified offset 0+0 records in 0+0 records out --- src/uu/dd/src/dd.rs | 7 ++++++- tests/by-util/test_dd.rs | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index ac7b50d1b..13bacd946 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -42,6 +42,7 @@ use gcd::Gcd; use signal_hook::consts::signal; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::show_error; use uucore::InvalidEncodingHandling; const ABOUT: &str = "copy, and optionally convert, a file system resource"; @@ -80,8 +81,12 @@ impl Input { }; if let Some(amt) = skip { - i.force_fill(amt.try_into().unwrap()) + let num_bytes_read = i + .force_fill(amt.try_into().unwrap()) .map_err_context(|| "failed to read input".to_string())?; + if num_bytes_read < amt { + show_error!("'standard input': cannot skip to specified offset"); + } } Ok(i) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index e9a1f9468..30adb05fc 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -624,5 +624,18 @@ fn test_seek_bytes() { .stdout_is("\0\0\0\0\0\0\0\0abcdefghijklm\n"); } +/// Test for skipping beyond the number of bytes in a file. +#[test] +fn test_skip_beyond_file() { + new_ucmd!() + .args(&["bs=1", "skip=5", "count=0", "status=noxfer"]) + .pipe_in("abcd") + .succeeds() + .no_stdout() + .stderr_contains( + "'standard input': cannot skip to specified offset\n0+0 records in\n0+0 records out\n", + ); +} + // conv=[ascii,ebcdic,ibm], conv=[ucase,lcase], conv=[block,unblock], conv=sync // TODO: Move conv tests from unit test module