Skip to content
Open
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
36 changes: 24 additions & 12 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,25 +520,37 @@ impl Input<'_> {
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read follows the previous one.
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> io::Result<ReadStat> {
fn fill_consecutive(&mut self, buf: &mut Vec<u8>, bsize: usize) -> io::Result<ReadStat> {
let mut reads_complete = 0;
let mut reads_partial = 0;
let mut bytes_total = 0;

for chunk in buf.chunks_mut(self.settings.ibs) {
match self.read(chunk)? {
buf.clear();
let spare = buf.spare_capacity_mut();
let spare_len = spare.len();
let target_area = &mut spare[..bsize.min(spare_len)];

for chunk in target_area.chunks_mut(self.settings.ibs) {
// std cannot fix https://github.com/uutils/coreutils/issues/11544 without unsafe yet
// SAFETY: write-only to reserved area of buf and becomes unaccessible at self.read(slice)? soon
let slice = unsafe {
std::slice::from_raw_parts_mut(chunk.as_mut_ptr().cast::<u8>(), chunk.len())
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.

This unsafe code is the equivalent of chunk.assume_init_mut(), which causes undefined behavior when the content is not fully initialized.

};
match self.read(slice)? {
rlen if rlen == self.settings.ibs => {
bytes_total += rlen;
reads_complete += 1;
}
rlen if rlen > 0 => {
bytes_total += rlen;
reads_partial += 1;
break;
}
_ => break,
_ => break, // end of file
}
}
buf.truncate(bytes_total);
// todo: use wrapper to extend buf with read at same time without unsafe
// SAFETY: we just checked that bytes_total of spare was filled
unsafe { buf.set_len(bytes_total) };
Ok(ReadStat {
reads_complete,
reads_partial,
Expand All @@ -551,7 +563,10 @@ impl Input<'_> {
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte.
fn fill_blocks(&mut self, buf: &mut Vec<u8>, pad: u8) -> io::Result<ReadStat> {
fn fill_blocks(&mut self, buf: &mut Vec<u8>, bsize: usize, pad: u8) -> io::Result<ReadStat> {
// Resize the buffer to the bsize. Any garbage data in the buffer is overwritten or truncated, so there is no need to fill with BUF_INIT_BYTE first.
// resizing buf cause serious performance drop https://github.com/uutils/coreutils/issues/11544
buf.resize(bsize, BUF_INIT_BYTE);
let mut reads_complete = 0;
let mut reads_partial = 0;
let mut base_idx = 0;
Expand Down Expand Up @@ -1366,13 +1381,10 @@ fn read_helper(i: &mut Input, buf: &mut Vec<u8>, bsize: usize) -> io::Result<Rea
}
// ------------------------------------------------------------------
// Read
// Resize the buffer to the bsize. Any garbage data in the buffer is overwritten or truncated, so there is no need to fill with BUF_INIT_BYTE first.
// resizing buf cause serious performance drop https://github.com/uutils/coreutils/issues/11544
buf.resize(bsize, BUF_INIT_BYTE);

let mut rstat = match i.settings.iconv.sync {
Some(ch) => i.fill_blocks(buf, ch)?,
_ => i.fill_consecutive(buf)?,
Some(ch) => i.fill_blocks(buf, bsize, ch)?,
_ => i.fill_consecutive(buf, bsize)?,
};
// Return early if no data
if rstat.reads_complete == 0 && rstat.reads_partial == 0 {
Expand Down
Loading