diff --git a/virtio-devices/src/vsock/packet.rs b/virtio-devices/src/vsock/packet.rs index 130cad952..ce3422473 100644 --- a/virtio-devices/src/vsock/packet.rs +++ b/virtio-devices/src/vsock/packet.rs @@ -23,7 +23,7 @@ use super::defs; use super::{Result, VsockError}; use crate::get_host_address_range; use virtio_queue::DescriptorChain; -use vm_memory::GuestMemory; +use vm_memory::{Address, GuestMemory}; use vm_virtio::{AccessPlatform, Translatable}; // The vsock packet header is defined by the C struct: @@ -133,7 +133,7 @@ impl VsockPacket { hdr: get_host_address_range( desc_chain.memory(), head.addr() - .translate_gva(access_platform, head.len() as usize), + .translate_gva(access_platform, VSOCK_PKT_HDR_SIZE), VSOCK_PKT_HDR_SIZE, ) .ok_or(VsockError::GuestMemory)?, @@ -152,32 +152,46 @@ impl VsockPacket { return Err(VsockError::InvalidPktLen(pkt.len())); } - // If the packet header showed a non-zero length, there should be a data descriptor here. - let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; + // Prior to Linux v6.3 there are two descriptors + if head.has_next() { + let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; - // TX data should be read-only. - if buf_desc.is_write_only() { - return Err(VsockError::UnreadableDescriptor); + // TX data should be read-only. + if buf_desc.is_write_only() { + return Err(VsockError::UnreadableDescriptor); + } + + // The data buffer should be large enough to fit the size of the data, as described by + // the header descriptor. + if buf_desc.len() < pkt.len() { + return Err(VsockError::BufDescTooSmall); + } + let buf_size = buf_desc.len() as usize; + pkt.buf_size = buf_size; + pkt.buf = Some( + get_host_address_range( + desc_chain.memory(), + buf_desc.addr().translate_gva(access_platform, buf_size), + pkt.buf_size, + ) + .ok_or(VsockError::GuestMemory)?, + ); + } else { + let buf_size: usize = head.len() as usize - VSOCK_PKT_HDR_SIZE; + pkt.buf_size = buf_size; + pkt.buf = Some( + get_host_address_range( + desc_chain.memory(), + head.addr() + .checked_add(VSOCK_PKT_HDR_SIZE as u64) + .unwrap() + .translate_gva(access_platform, buf_size), + buf_size, + ) + .ok_or(VsockError::GuestMemory)?, + ); } - // The data buffer should be large enough to fit the size of the data, as described by - // the header descriptor. - if buf_desc.len() < pkt.len() { - return Err(VsockError::BufDescTooSmall); - } - - pkt.buf_size = buf_desc.len() as usize; - pkt.buf = Some( - get_host_address_range( - desc_chain.memory(), - buf_desc - .addr() - .translate_gva(access_platform, buf_desc.len() as usize), - pkt.buf_size, - ) - .ok_or(VsockError::GuestMemory)?, - ); - Ok(pkt) } @@ -207,33 +221,53 @@ impl VsockPacket { return Err(VsockError::HdrDescTooSmall(head.len())); } - // All RX descriptor chains should have a header and a data descriptor. - if !head.has_next() { - return Err(VsockError::BufDescMissing); - } - let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; - let buf_size = buf_desc.len() as usize; + // Prior to Linux v6.3 there are two descriptors + if head.has_next() { + let buf_desc = desc_chain.next().ok_or(VsockError::BufDescMissing)?; + let buf_size = buf_desc.len() as usize; - Ok(Self { - hdr: get_host_address_range( - desc_chain.memory(), - head.addr() - .translate_gva(access_platform, head.len() as usize), - VSOCK_PKT_HDR_SIZE, - ) - .ok_or(VsockError::GuestMemory)?, - buf: Some( - get_host_address_range( + Ok(Self { + hdr: get_host_address_range( desc_chain.memory(), - buf_desc - .addr() - .translate_gva(access_platform, buf_desc.len() as usize), - buf_size, + head.addr() + .translate_gva(access_platform, VSOCK_PKT_HDR_SIZE), + VSOCK_PKT_HDR_SIZE, ) .ok_or(VsockError::GuestMemory)?, - ), - buf_size, - }) + buf: Some( + get_host_address_range( + desc_chain.memory(), + buf_desc.addr().translate_gva(access_platform, buf_size), + buf_size, + ) + .ok_or(VsockError::GuestMemory)?, + ), + buf_size, + }) + } else { + let buf_size: usize = head.len() as usize - VSOCK_PKT_HDR_SIZE; + Ok(Self { + hdr: get_host_address_range( + desc_chain.memory(), + head.addr() + .translate_gva(access_platform, VSOCK_PKT_HDR_SIZE), + VSOCK_PKT_HDR_SIZE, + ) + .ok_or(VsockError::GuestMemory)?, + buf: Some( + get_host_address_range( + desc_chain.memory(), + head.addr() + .checked_add(VSOCK_PKT_HDR_SIZE as u64) + .unwrap() + .translate_gva(access_platform, buf_size), + buf_size, + ) + .ok_or(VsockError::GuestMemory)?, + ), + buf_size, + }) + } } /// Provides in-place, byte-slice, access to the vsock packet header. @@ -503,16 +537,6 @@ mod tests { expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::InvalidPktLen(_)); } - // Test case: - // - packet header advertises some data length; and - // - the data descriptor is missing. - { - create_context!(test_ctx, handler_ctx); - set_pkt_len(1024, &handler_ctx.guest_txvq.dtable[0], &test_ctx.mem); - handler_ctx.guest_txvq.dtable[0].flags.set(0); - expect_asm_error!(tx, test_ctx, handler_ctx, VsockError::BufDescMissing); - } - // Test case: error on write-only buf descriptor. { create_context!(test_ctx, handler_ctx); @@ -568,15 +592,6 @@ mod tests { .set(VSOCK_PKT_HDR_SIZE as u32 - 1); expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::HdrDescTooSmall(_)); } - - // Test case: RX descriptor chain is missing the packet buffer descriptor. - { - create_context!(test_ctx, handler_ctx); - handler_ctx.guest_rxvq.dtable[0] - .flags - .set(VRING_DESC_F_WRITE.try_into().unwrap()); - expect_asm_error!(rx, test_ctx, handler_ctx, VsockError::BufDescMissing); - } } #[test]