From 7b07c3a1949cf5af6356694e55d825492bfdf860 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sat, 18 Apr 2026 11:21:20 +0100 Subject: [PATCH] virtio-devices: rng: Fill the entire descriptor chain The virtio spec allows a chain of writable descriptors however the rng device was assuming just a single writable descriptor. Instead fill in all writable descriptors. There is no status byte (unlike e.g. block) and instead 0 bytes used is used to indicate error. Signed-off-by: Rob Bradford --- virtio-devices/src/rng.rs | 67 ++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/virtio-devices/src/rng.rs b/virtio-devices/src/rng.rs index 8d11a3d7b..9d86cf7d8 100644 --- a/virtio-devices/src/rng.rs +++ b/virtio-devices/src/rng.rs @@ -12,7 +12,7 @@ use std::{io, result}; use anyhow::anyhow; use event_monitor::event; -use log::{error, info}; +use log::{error, info, warn}; use seccompiler::SeccompAction; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -39,12 +39,6 @@ const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; #[derive(Error, Debug)] enum Error { - #[error("Descriptor chain too short")] - DescriptorChainTooShort, - #[error("Invalid descriptor")] - InvalidDescriptor, - #[error("Failed to write to guest memory")] - GuestMemoryWrite(#[source] vm_memory::guest_memory::Error), #[error("Failed adding used index")] QueueAddUsed(#[source] virtio_queue::Error), } @@ -66,29 +60,50 @@ impl RngEpollHandler { let mut used_descs = false; while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) { - let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; - - // The descriptor must be write-only and non-zero length - if !(desc.is_write_only() && desc.len() > 0) { - return Err(Error::InvalidDescriptor); - } - - // Fill the read with data from the random device on the host. - let len = desc_chain - .memory() - .read_volatile_from( - desc.addr() - .translate_gva(self.access_platform.as_deref(), desc.len() as usize) - .map_err(|e| { - Error::GuestMemoryWrite(vm_memory::GuestMemoryError::IOError(e)) - })?, + // virtio-rng has no status byte; on any error along the chain + // report a used length of 0 to indicate failure. + let mut total_len: usize = 0; + while let Some(desc) = desc_chain.next() { + if !desc.is_write_only() || desc.len() == 0 { + warn!( + "Skipping descriptor with write_only={} len={}", + desc.is_write_only(), + desc.len() + ); + total_len = 0; + break; + } + let addr = match desc + .addr() + .translate_gva(self.access_platform.as_deref(), desc.len() as usize) + { + Ok(a) => a, + Err(e) => { + warn!("Failed to translate descriptor address: {e}"); + total_len = 0; + break; + } + }; + match desc_chain.memory().read_volatile_from( + addr, &mut self.random_file, desc.len() as usize, - ) - .map_err(Error::GuestMemoryWrite)?; + ) { + Ok(written) => total_len += written, + Err(e) => { + warn!("Failed to read entropy into descriptor: {e}"); + total_len = 0; + break; + } + } + } queue - .add_used(desc_chain.memory(), desc_chain.head_index(), len as u32) + .add_used( + desc_chain.memory(), + desc_chain.head_index(), + total_len as u32, + ) .map_err(Error::QueueAddUsed)?; used_descs = true; }