diff options
author | Jorge E. Moreira <jemoreira@google.com> | 2019-07-11 16:08:25 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-07-17 22:04:23 +0000 |
commit | cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37 (patch) | |
tree | a59ee66f1c94231d061c86f07b303583be6bfef5 /devices/src/virtio/input/mod.rs | |
parent | 009392ac767a11425943803360248a869db38794 (diff) | |
download | crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar.gz crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar.bz2 crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar.lz crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar.xz crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.tar.zst crosvm-cb3ec5ed2b20fb92b7c43d7617d5cfa5c8128a37.zip |
Refactor input devices interactions with buffers in guest memory
Input devices were using GuestMemory's read_to_memory and write_from_memory under the (incorrect) assumption that these function used the io::Read and io::Write traits, when they in fact use AsRawFd. BUG=b/137138116 TEST=ran cuttlefish in workstation Change-Id: I7ab1e2d0ab685dd25dcc91e794766c2f210665f7 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1700418 Reviewed-by: Dylan Reid <dgreid@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Jorge Moreira Broche <jemoreira@google.com>
Diffstat (limited to 'devices/src/virtio/input/mod.rs')
-rw-r--r-- | devices/src/virtio/input/mod.rs | 138 |
1 files changed, 111 insertions, 27 deletions
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs index 205d82f..195a2e0 100644 --- a/devices/src/virtio/input/mod.rs +++ b/devices/src/virtio/input/mod.rs @@ -13,11 +13,10 @@ use self::constants::*; use std::os::unix::io::{AsRawFd, RawFd}; use data_model::{DataInit, Le16, Le32}; -use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken}; +use sys_util::{error, warn, EventFd, GuestAddress, GuestMemory, PollContext, PollToken}; use self::event_source::{input_event, EvdevEventSource, EventSource, SocketEventSource}; use super::{Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_INPUT}; -use std::cmp::min; use std::collections::BTreeMap; use std::fmt::{self, Display}; use std::io::Read; @@ -34,7 +33,7 @@ const QUEUE_SIZES: &[u16] = &[EVENT_QUEUE_SIZE, STATUS_QUEUE_SIZE]; #[derive(Debug)] pub enum InputError { /// Failed to write events to the source - EventsWriteError(sys_util::GuestMemoryError), + EventsWriteError(std::io::Error), /// Failed to read events from the source EventsReadError(std::io::Error), // Failed to get name of event device @@ -51,6 +50,8 @@ pub enum InputError { EvdevAbsInfoError(sys_util::Error), // Failed to grab event device EvdevGrabError(sys_util::Error), + // Detected error on guest side + GuestError(String), } pub type Result<T> = std::result::Result<T, InputError>; @@ -72,6 +73,7 @@ impl Display for InputError { write!(f, "failed to get axis information of event device: {}", e) } EvdevGrabError(e) => write!(f, "failed to grab event device: {}", e), + GuestError(s) => write!(f, "detected error on guest side: {}", s), } } } @@ -354,7 +356,7 @@ impl VirtioInputConfig { #[derive(Copy, Clone, Debug, Default)] #[repr(C)] -struct virtio_input_event { +pub struct virtio_input_event { type_: Le16, code: Le16, value: Le32, @@ -365,6 +367,27 @@ unsafe impl DataInit for virtio_input_event {} impl virtio_input_event { const EVENT_SIZE: usize = size_of::<virtio_input_event>(); + + fn write_to_guest_memory(&self, guest_memory: &GuestMemory, addr: GuestAddress) -> Result<()> { + let evt_slice = self.as_slice(); + let mut bytes_written = 0usize; + while bytes_written < evt_slice.len() { + bytes_written += guest_memory + .write_at_addr( + &evt_slice[bytes_written..], + addr.checked_add(bytes_written as u64) + .ok_or(InputError::GuestError(format!( + "Writing an event at address {} would overflow", + addr + )))?, + ) + .map_err(|e| { + InputError::GuestError(format!("Failed to write event to guest memory: {}", e)) + })?; + } + Ok(()) + } + fn new(type_: u16, code: u16, value: u32) -> virtio_input_event { virtio_input_event { type_: Le16::from(type_), @@ -380,6 +403,29 @@ impl virtio_input_event { value: Le32::from(other.value), } } + + fn from_guest_memory( + guest_memory: &GuestMemory, + addr: GuestAddress, + ) -> Result<virtio_input_event> { + let mut vio_evt = virtio_input_event::new(0, 0, 0); + let mut bytes_read = 0usize; + while bytes_read < virtio_input_event::EVENT_SIZE { + bytes_read += guest_memory + .read_at_addr( + &mut vio_evt.as_mut_slice()[bytes_read..], + addr.checked_add(bytes_read as u64) + .ok_or(InputError::GuestError(format!( + "Reading an event from address {} would overflow", + addr + )))?, + ) + .map_err(|e| { + InputError::GuestError(format!("Failed to read event from guest memory: {}", e)) + })?; + } + Ok(vio_evt) + } } struct Worker<T: EventSource> { @@ -401,33 +447,50 @@ impl<T: EventSource> Worker<T> { // Send events from the source to the guest fn send_events(&mut self) -> bool { - let queue = &mut self.event_queue; let mut needs_interrupt = false; // Only consume from the queue iterator if we know we have events to send while self.event_source.available_events_count() > 0 { - match queue.pop(&self.guest_memory) { + match self.event_queue.pop(&self.guest_memory) { None => { break; } Some(avail_desc) => { if !avail_desc.is_write_only() { - panic!("Received a read only descriptor on event queue"); + error!("Received a read only descriptor on event queue"); + continue; } - let avail_events_size = - self.event_source.available_events_count() * virtio_input_event::EVENT_SIZE; - let len = min(avail_desc.len as usize, avail_events_size); - if let Err(e) = - self.guest_memory - .read_to_memory(avail_desc.addr, &self.event_source, len) - { - // Read is guaranteed to succeed here, so the only possible failure would be - // writing outside the guest memory region, which would mean the address and - // length given in the queue descriptor are wrong. - panic!("failed reading events into guest memory: {}", e); + + let cnt = avail_desc.len as usize / virtio_input_event::EVENT_SIZE; + let mut len = 0usize; + for idx in 0..cnt { + match self.event_source.pop_available_event() { + Some(evt) => { + if let Err(e) = evt.write_to_guest_memory( + &self.guest_memory, + // This won't overflow as long as the length specified + // on the queue descriptor is valid since idx < cnt and + // cnt = buffer_len / EVENT_SIZE. In any case, + // write_to_guest_memory will check for overflow for + // good measure. + avail_desc.addr.unchecked_add( + (idx * virtio_input_event::EVENT_SIZE) as u64, + ), + ) { + // An error here would mean the address and length given + // in the queue descriptor are wrong: Don't try to write + // to this buffer anymore. + error!("Could not write event to guest memory: {}", e); + break; + } + } + None => break, + } + len += virtio_input_event::EVENT_SIZE; } - queue.add_used(&self.guest_memory, avail_desc.index, len as u32); + self.event_queue + .add_used(&self.guest_memory, avail_desc.index, len as u32); needs_interrupt = true; } } @@ -437,26 +500,47 @@ impl<T: EventSource> Worker<T> { } fn process_status_queue(&mut self) -> Result<bool> { - let queue = &mut self.status_queue; - let mut needs_interrupt = false; - while let Some(avail_desc) = queue.pop(&self.guest_memory) { + while let Some(avail_desc) = self.status_queue.pop(&self.guest_memory) { if !avail_desc.is_read_only() { - panic!("Received a writable descriptor on status queue"); + error!("Received a writable descriptor on status queue"); + continue; } let len = avail_desc.len as usize; + let count = len / virtio_input_event::EVENT_SIZE; if len % virtio_input_event::EVENT_SIZE != 0 { warn!( "Ignoring buffer of unexpected size on status queue: {:0}", len ); } else { - self.guest_memory - .write_from_memory(avail_desc.addr, &self.event_source, len) - .map_err(InputError::EventsWriteError)?; + for idx in 0usize..count { + match virtio_input_event::from_guest_memory( + &self.guest_memory, + avail_desc + .addr + // This won't overflow as long as the buffer length specified + // in the queue descriptor is valid because idx < count and + // count = buffer_len / EVENT_SIZE. In any case, + // from_guest_memory will check for overflow anyways. + .unchecked_add((idx * virtio_input_event::EVENT_SIZE) as u64), + ) { + Ok(evt) => { + self.event_source.send_event(&evt)?; + } + Err(e) => { + // An error here would mean the address or length in the buffer + // descriptor was wrong: Don't try to read from this buffer + // anymore. + error!("Unable to read status event from guest memory: {}", e); + break; + } + } + } } - queue.add_used(&self.guest_memory, avail_desc.index, len as u32); + self.status_queue + .add_used(&self.guest_memory, avail_desc.index, len as u32); needs_interrupt = true; } |