diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b308518b95..4aa53837293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,10 @@ and this project adheres to Intel AMX enabling for kernels that support dynamic XSTATE features for userspace applications but not for KVM guests (e.g. kernel versions >= 5.16 and < 5.17). +- [#5485](https://github.com/firecracker-microvm/firecracker/pull/5485): Fixed a + bug causing a read/write from an iovec to be duplicated when receiving an + error on an iovec other than the first. This caused a data corruption issue in + the vsock device starting from guest kernel 6.17. ## [1.13.0] diff --git a/src/vmm/src/devices/virtio/iovec.rs b/src/vmm/src/devices/virtio/iovec.rs index 3a2703de82e..da9240a6745 100644 --- a/src/vmm/src/devices/virtio/iovec.rs +++ b/src/vmm/src/devices/virtio/iovec.rs @@ -192,23 +192,25 @@ impl IoVecBuffer { slice = slice.subslice(0, len)?; } - let bytes_read = loop { + match loop { match dst.write_volatile(&slice) { Err(VolatileMemoryError::IOError(err)) - if err.kind() == ErrorKind::Interrupted => - { - continue; - } - Ok(bytes_read) => break bytes_read, - Err(volatile_memory_error) => return Err(volatile_memory_error), + if err.kind() == ErrorKind::Interrupted => {} + result => break result, } - }; - total_bytes_read += bytes_read; + } { + Ok(bytes_read) => { + total_bytes_read += bytes_read; - if bytes_read < slice.len() { - break; + if bytes_read < slice.len() { + break; + } + len -= bytes_read; + } + // exit successfully if we previously managed to write some bytes + Err(_) if total_bytes_read > 0 => break, + Err(err) => return Err(err), } - len -= bytes_read; } Ok(total_bytes_read) @@ -457,23 +459,25 @@ impl IoVecBufferMut { slice = slice.subslice(0, len)?; } - let bytes_read = loop { + match loop { match src.read_volatile(&mut slice) { Err(VolatileMemoryError::IOError(err)) - if err.kind() == ErrorKind::Interrupted => - { - continue; - } - Ok(bytes_read) => break bytes_read, - Err(volatile_memory_error) => return Err(volatile_memory_error), + if err.kind() == ErrorKind::Interrupted => {} + result => break result, } - }; - total_bytes_read += bytes_read; + } { + Ok(bytes_read) => { + total_bytes_read += bytes_read; - if bytes_read < slice.len() { - break; + if bytes_read < slice.len() { + break; + } + len -= bytes_read; + } + // exit successfully if we previously managed to read some bytes + Err(_) if total_bytes_read > 0 => break, + Err(err) => return Err(err), } - len -= bytes_read; } Ok(total_bytes_read)