summary refs log tree commit diff
diff options
context:
space:
mode:
authorChirantan Ekbote <chirantan@chromium.org>2019-08-16 15:40:48 +0900
committerCommit Bot <commit-bot@chromium.org>2019-10-15 18:26:29 +0000
commitb5964164c4d6187971495ccf82fd64a1d5bde232 (patch)
tree29342794c8f3adbcb8f1d6657cd897aad1d70a59
parent2515b756302235e69fbc1b07ae70270be204a91d (diff)
downloadcrosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.gz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.bz2
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.lz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.xz
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.tar.zst
crosvm-b5964164c4d6187971495ccf82fd64a1d5bde232.zip
devices: Refactor DescriptorChainConsumer, Reader, and Writer
Refactor the Reader and Writer implementations for DescriptorChains.
This has several changes:

  * Change the DescriptorChainConsumer to keep a
    VecDeque<VolatileSlice> instead of an iterator.  This delegates the
    fiddly business of sub-slicing chunks of memory to the VolatileSlice
    implementation.
  * Read in the entire DescriptorChain once when the Reader or Writer is
    first constructed.  This allows us to validate the DescriptorChain
    in the beginning rather than having to deal with an invalid
    DescriptorChain in the middle of the device operating on it.
    Combined with the check that enforces the ordering of read/write
    descriptors in a previous change we can be sure that the entire
    descriptor chain that we have copied in is valid.
  * Add a new `split_at` method so that we can split the Reader/Writer
    into multiple pieces, each responsible for reading/writing a
    separate part of the DescriptorChain.  This is particularly useful
    for implementing zero-copy data transfer as we sometimes need to
    write the data first and then update an earlier part of the buffer
    with the number of bytes written.
  * Stop caching the available bytes in the DescriptorChain.  The
    previous implementation iterated over the remaining descriptors in
    the chain and then only updated the cached value.  If a mis-behaving
    guest then changed one of the later descriptors, the cached value
    would no longer be valid.
  * Check for integer overflow when calculating the number of bytes
    available in the chain.  A guest could fill a chain with five 1GB
    descriptors and cause an integer overflow on a 32-bit machine.
    This would previously crash the device process since we compile with
    integer overflow checks enabled but it would be better to return an
    error instead.
  * Clean up the Read/Write impls.  Having 2 different functions called
    `read`, with different behavior is just confusing.  Consolidate on
    the Read/Write traits from `std::io`.
  * Change the `read_to` and `write_from` functions to be generic over
    types that implement `FileReadWriteVolatile` since we are not
    allowed to assume that it's safe to call read or write on something
    just because it implements `AsRawFd`.  Also add `*at` variants that
    read or write to a particular offset rather than the kernel offset.
  * Change the callback passed to the `consume` function of
    `DescriptorChainConsumer` to take a `&[VolatileSlice]` instead.
    This way we can use the `*vectored` versions of some methods to
    reduce the number of I/O syscalls we need to make.
  * Change the `Result` types that are returned.  Functions that perform
    I/O return an `io::Result`.  Functions that only work on guest
    memory return a `guest_memory::Result`.  This makes it easier to
    inter-operate with the functions from `std::io`.
  * Change some u64/u32 parameters to usize to avoid having to convert
    back and forth between the two in various places.

BUG=b:136128319
TEST=unit tests

Change-Id: I15102f7b4035d66b5ce0891df42b656411e8279f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1757240
Auto-Submit: Chirantan Ekbote <chirantan@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
-rw-r--r--devices/src/virtio/balloon.rs22
-rw-r--r--devices/src/virtio/block.rs111
-rw-r--r--devices/src/virtio/descriptor_utils.rs965
-rw-r--r--devices/src/virtio/gpu/mod.rs78
-rw-r--r--devices/src/virtio/gpu/protocol.rs25
-rw-r--r--devices/src/virtio/input/mod.rs117
-rw-r--r--devices/src/virtio/net.rs78
-rw-r--r--devices/src/virtio/p9.rs14
-rw-r--r--devices/src/virtio/pmem.rs76
-rw-r--r--devices/src/virtio/rng.rs9
-rw-r--r--devices/src/virtio/tpm.rs23
-rw-r--r--sys_util/src/file_traits.rs4
-rw-r--r--sys_util/src/guest_memory.rs9
13 files changed, 984 insertions, 547 deletions
diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs
index 633b3fc..ec16f88 100644
--- a/devices/src/virtio/balloon.rs
+++ b/devices/src/virtio/balloon.rs
@@ -94,11 +94,29 @@ impl Worker {
             let index = avail_desc.index;
 
             if inflate {
-                let mut reader = Reader::new(&self.mem, avail_desc);
-                let data_length = reader.available_bytes();
+                let mut reader = match Reader::new(&self.mem, avail_desc) {
+                    Ok(r) => r,
+                    Err(e) => {
+                        error!("balloon: failed to create reader: {}", e);
+                        queue.add_used(&self.mem, index, 0);
+                        needs_interrupt = true;
+                        continue;
+                    }
+                };
+                let data_length = match reader.available_bytes() {
+                    Ok(l) => l,
+                    Err(e) => {
+                        error!("balloon: failed to get available bytes: {}", e);
+                        queue.add_used(&self.mem, index, 0);
+                        needs_interrupt = true;
+                        continue;
+                    }
+                };
 
                 if data_length % 4 != 0 {
                     error!("invalid inflate buffer size: {}", data_length);
+                    queue.add_used(&self.mem, index, 0);
+                    needs_interrupt = true;
                     continue;
                 }
 
diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs
index 73fd416..b171864 100644
--- a/devices/src/virtio/block.rs
+++ b/devices/src/virtio/block.rs
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 use std::fmt::{self, Display};
-use std::io::{self, Seek, SeekFrom};
+use std::io::{self, Seek, SeekFrom, Write};
 use std::mem::size_of;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::result;
@@ -129,12 +129,14 @@ unsafe impl DataInit for virtio_blk_discard_write_zeroes {}
 #[derive(Debug)]
 enum ExecuteError {
     Descriptor(DescriptorError),
+    Read(io::Error),
+    WriteStatus(io::Error),
     /// Error arming the flush timer.
     Flush(io::Error),
     ReadIo {
         length: usize,
         sector: u64,
-        desc_error: DescriptorError,
+        desc_error: io::Error,
     },
     ShortRead {
         sector: u64,
@@ -149,7 +151,7 @@ enum ExecuteError {
     WriteIo {
         length: usize,
         sector: u64,
-        desc_error: DescriptorError,
+        desc_error: io::Error,
     },
     ShortWrite {
         sector: u64,
@@ -176,6 +178,8 @@ impl Display for ExecuteError {
 
         match self {
             Descriptor(e) => write!(f, "virtio descriptor error: {}", e),
+            Read(e) => write!(f, "failed to read message: {}", e),
+            WriteStatus(e) => write!(f, "failed to write request status: {}", e),
             Flush(e) => write!(f, "failed to flush: {}", e),
             ReadIo {
                 length,
@@ -247,6 +251,8 @@ impl ExecuteError {
     fn status(&self) -> u8 {
         match self {
             ExecuteError::Descriptor(_) => VIRTIO_BLK_S_IOERR,
+            ExecuteError::Read(_) => VIRTIO_BLK_S_IOERR,
+            ExecuteError::WriteStatus(_) => VIRTIO_BLK_S_IOERR,
             ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
             ExecuteError::ReadIo { .. } => VIRTIO_BLK_S_IOERR,
             ExecuteError::ShortRead { .. } => VIRTIO_BLK_S_IOERR,
@@ -275,6 +281,50 @@ struct Worker {
 }
 
 impl Worker {
+    fn process_one_request(
+        avail_desc: DescriptorChain,
+        read_only: bool,
+        disk: &mut DiskFile,
+        disk_size: u64,
+        flush_timer: &mut TimerFd,
+        flush_timer_armed: &mut bool,
+        mem: &GuestMemory,
+    ) -> result::Result<usize, ExecuteError> {
+        let mut status_writer =
+            Writer::new(mem, avail_desc.clone()).map_err(ExecuteError::Descriptor)?;
+        let available_bytes = status_writer
+            .available_bytes()
+            .map_err(ExecuteError::Descriptor)?;
+        let status_offset = available_bytes
+            .checked_sub(1)
+            .ok_or(ExecuteError::MissingStatus)?;
+
+        status_writer = status_writer
+            .split_at(status_offset)
+            .map_err(ExecuteError::Descriptor)?;
+
+        let status = match Block::execute_request(
+            avail_desc,
+            read_only,
+            disk,
+            disk_size,
+            flush_timer,
+            flush_timer_armed,
+            mem,
+        ) {
+            Ok(()) => VIRTIO_BLK_S_OK,
+            Err(e) => {
+                error!("failed executing disk request: {}", e);
+                e.status()
+            }
+        };
+
+        status_writer
+            .write_all(&[status])
+            .map_err(ExecuteError::WriteStatus)?;
+        Ok(available_bytes)
+    }
+
     fn process_queue(
         &mut self,
         queue_index: usize,
@@ -288,9 +338,8 @@ impl Worker {
         let mut needs_interrupt = false;
         while let Some(avail_desc) = queue.pop(&self.mem) {
             let desc_index = avail_desc.index;
-            let mut status_writer = Writer::new(&self.mem, avail_desc.clone());
 
-            let status = match Block::execute_request(
+            let len = match Worker::process_one_request(
                 avail_desc,
                 self.read_only,
                 &mut *self.disk_image,
@@ -299,24 +348,11 @@ impl Worker {
                 flush_timer_armed,
                 &self.mem,
             ) {
-                Ok(()) => VIRTIO_BLK_S_OK,
+                Ok(len) => len,
                 Err(e) => {
-                    error!("failed executing disk request: {}", e);
-                    e.status()
-                }
-            };
-
-            let len = if let Ok(status_offset) = status_writer.seek(SeekFrom::End(-1)) {
-                match status_writer.write_all(&[status]) {
-                    Ok(_) => status_offset + 1,
-                    Err(e) => {
-                        error!("failed to write status: {}", e);
-                        0
-                    }
+                    error!("block: failed to handle request: {}", e);
+                    0
                 }
-            } else {
-                error!("failed to seek to status location");
-                0
             };
 
             queue.add_used(&self.mem, desc_index, len as u32);
@@ -541,11 +577,10 @@ impl Block {
         flush_timer_armed: &mut bool,
         mem: &GuestMemory,
     ) -> result::Result<(), ExecuteError> {
-        let mut reader = Reader::new(mem, avail_desc.clone());
-        let mut writer = Writer::new(mem, avail_desc);
+        let mut reader = Reader::new(mem, avail_desc.clone()).map_err(ExecuteError::Descriptor)?;
+        let mut writer = Writer::new(mem, avail_desc).map_err(ExecuteError::Descriptor)?;
 
-        let req_header: virtio_blk_req_header =
-            reader.read_obj().map_err(ExecuteError::Descriptor)?;
+        let req_header: virtio_blk_req_header = reader.read_obj().map_err(ExecuteError::Read)?;
 
         let req_type = req_header.req_type.to_native();
         let sector = req_header.sector.to_native();
@@ -580,6 +615,7 @@ impl Block {
                 // The last byte of writer is virtio_blk_req::status, so subtract it from data_len.
                 let data_len = writer
                     .available_bytes()
+                    .map_err(ExecuteError::Descriptor)?
                     .checked_sub(1)
                     .ok_or(ExecuteError::MissingStatus)?;
                 let offset = sector
@@ -588,14 +624,13 @@ impl Block {
                 check_range(offset, data_len as u64, disk_size)?;
                 disk.seek(SeekFrom::Start(offset))
                     .map_err(|e| ExecuteError::Seek { ioerr: e, sector })?;
-                let actual_length =
-                    writer
-                        .write_from_volatile(disk, data_len)
-                        .map_err(|desc_error| ExecuteError::ReadIo {
-                            length: data_len,
-                            sector,
-                            desc_error,
-                        })?;
+                let actual_length = writer.write_from(disk, data_len).map_err(|desc_error| {
+                    ExecuteError::ReadIo {
+                        length: data_len,
+                        sector,
+                        desc_error,
+                    }
+                })?;
                 if actual_length < data_len {
                     return Err(ExecuteError::ShortRead {
                         sector,
@@ -605,7 +640,7 @@ impl Block {
                 }
             }
             VIRTIO_BLK_T_OUT => {
-                let data_len = reader.available_bytes();
+                let data_len = reader.available_bytes().map_err(ExecuteError::Descriptor)?;
                 let offset = sector
                     .checked_shl(u32::from(SECTOR_SHIFT))
                     .ok_or(ExecuteError::OutOfRange)?;
@@ -614,7 +649,7 @@ impl Block {
                     .map_err(|e| ExecuteError::Seek { ioerr: e, sector })?;
                 let actual_length =
                     reader
-                        .read_to_volatile(disk, data_len)
+                        .read_to(disk, data_len)
                         .map_err(|desc_error| ExecuteError::WriteIo {
                             length: data_len,
                             sector,
@@ -635,9 +670,11 @@ impl Block {
                 }
             }
             VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => {
-                while reader.available_bytes() >= size_of::<virtio_blk_discard_write_zeroes>() {
+                while reader.available_bytes().map_err(ExecuteError::Descriptor)?
+                    >= size_of::<virtio_blk_discard_write_zeroes>()
+                {
                     let seg: virtio_blk_discard_write_zeroes =
-                        reader.read_obj().map_err(ExecuteError::Descriptor)?;
+                        reader.read_obj().map_err(ExecuteError::Read)?;
 
                     let sector = seg.sector.to_native();
                     let num_sectors = seg.num_sectors.to_native();
diff --git a/devices/src/virtio/descriptor_utils.rs b/devices/src/virtio/descriptor_utils.rs
index 409134e..3c8ff87 100644
--- a/devices/src/virtio/descriptor_utils.rs
+++ b/devices/src/virtio/descriptor_utils.rs
@@ -3,23 +3,24 @@
 // found in the LICENSE file.
 
 use std::cmp;
-use std::convert::TryFrom;
+use std::collections::VecDeque;
 use std::fmt::{self, Display};
-use std::io;
-use std::os::unix::io::AsRawFd;
+use std::io::{self, Read, Write};
+use std::mem::{size_of, MaybeUninit};
 use std::result;
 
-use data_model::{DataInit, Le16, Le32, Le64, VolatileMemory, VolatileMemoryError};
-use sys_util::guest_memory::Error as GuestMemoryError;
-use sys_util::{FileReadWriteVolatile, GuestAddress, GuestMemory};
+use data_model::{DataInit, Le16, Le32, Le64, VolatileMemory, VolatileMemoryError, VolatileSlice};
+use sys_util::{FileReadWriteAtVolatile, FileReadWriteVolatile, GuestAddress, GuestMemory};
 
 use super::DescriptorChain;
 
 #[derive(Debug)]
 pub enum Error {
+    DescriptorChainOverflow,
     GuestMemoryError(sys_util::GuestMemoryError),
     InvalidChain,
     IoError(io::Error),
+    SplitOutOfBounds(usize),
     VolatileMemoryError(VolatileMemoryError),
 }
 
@@ -28,9 +29,14 @@ impl Display for Error {
         use self::Error::*;
 
         match self {
+            DescriptorChainOverflow => write!(
+                f,
+                "the combined length of all the buffers in a `DescriptorChain` would overflow"
+            ),
             GuestMemoryError(e) => write!(f, "descriptor guest memory error: {}", e),
             InvalidChain => write!(f, "invalid descriptor chain"),
             IoError(e) => write!(f, "descriptor I/O error: {}", e),
+            SplitOutOfBounds(off) => write!(f, "`DescriptorChain` split is out of bounds: {}", off),
             VolatileMemoryError(e) => write!(f, "volatile memory error: {}", e),
         }
     }
@@ -40,157 +46,133 @@ pub type Result<T> = result::Result<T, Error>;
 
 impl std::error::Error for Error {}
 
-#[derive(Clone, PartialEq, Eq)]
-enum DescriptorFilter {
-    OnlyReadable,
-    OnlyWritable,
-}
-
 #[derive(Clone)]
 struct DescriptorChainConsumer<'a> {
-    offset: usize,
-    desc_chain: Option<DescriptorChain<'a>>,
-    desc_chain_start: Option<DescriptorChain<'a>>,
+    buffers: VecDeque<VolatileSlice<'a>>,
     bytes_consumed: usize,
-    avail_bytes: Option<usize>,
-    filter: DescriptorFilter,
 }
 
 impl<'a> DescriptorChainConsumer<'a> {
-    fn new(
-        desc_chain: Option<DescriptorChain<'a>>,
-        filter: DescriptorFilter,
-    ) -> DescriptorChainConsumer<'a> {
-        DescriptorChainConsumer {
-            offset: 0,
-            desc_chain: desc_chain.clone(),
-            desc_chain_start: desc_chain,
-            bytes_consumed: 0,
-            avail_bytes: None,
-            filter,
-        }
-    }
-
-    fn available_bytes(&mut self) -> usize {
-        if let Some(bytes) = self.avail_bytes {
-            bytes
-        } else {
-            let mut chain = self.desc_chain.clone();
-            let mut count = 0;
-            while let Some(desc) = chain {
-                count += desc.len as usize;
-                chain = self.advance(desc);
-            }
-            let bytes = count - self.offset;
-            self.avail_bytes = Some(bytes);
-            bytes
-        }
+    fn available_bytes(&self) -> Result<usize> {
+        self.buffers
+            .iter()
+            .try_fold(0usize, |count, vs| count.checked_add(vs.size() as usize))
+            .ok_or(Error::DescriptorChainOverflow)
     }
 
     fn bytes_consumed(&self) -> usize {
         self.bytes_consumed
     }
 
-    fn consume<F>(&mut self, mut fnc: F, mut count: usize) -> Result<usize>
+    /// Consumes at most `count` bytes from the `DescriptorChain`. Callers must provide a function
+    /// that takes a `&[VolatileSlice]` and returns the total number of bytes consumed. This
+    /// function guarantees that the combined length of all the slices in the `&[VolatileSlice]` is
+    /// less than or equal to `count`.
+    ///
+    /// # Errors
+    ///
+    /// If the provided function returns any error then no bytes are consumed from the buffer and
+    /// the error is returned to the caller.
+    fn consume<F>(&mut self, count: usize, f: F) -> io::Result<usize>
     where
-        F: FnMut(GuestAddress, usize) -> Result<()>,
+        F: FnOnce(&[VolatileSlice]) -> io::Result<usize>,
     {
-        let mut bytes_consumed = 0;
-        while count > 0 {
-            if let Some(current) = &self.desc_chain {
-                let addr = current
-                    .addr
-                    .checked_add(self.offset as u64)
-                    .ok_or_else(|| {
-                        Error::GuestMemoryError(GuestMemoryError::InvalidGuestAddress(current.addr))
-                    })?;
-                let len = cmp::min(count, current.len as usize - self.offset);
-                fnc(addr, len)?;
-
-                self.offset += len;
-                self.avail_bytes = self.avail_bytes.map(|av| av - len);
-                self.bytes_consumed += len;
-                bytes_consumed += len;
-                count -= len;
-
-                if self.offset == current.len as usize {
-                    self.offset = 0;
-                    if let Some(desc_chain) = self.desc_chain.take() {
-                        self.desc_chain = self.advance(desc_chain);
-                    }
-                }
-            } else {
-                // Nothing left to read.
+        let mut buflen = 0;
+        let mut bufs = Vec::with_capacity(self.buffers.len());
+        for &vs in &self.buffers {
+            if buflen >= count {
                 break;
             }
-        }
-        Ok(bytes_consumed)
-    }
 
-    fn advance(&self, desc_chain: DescriptorChain<'a>) -> Option<DescriptorChain<'a>> {
-        let mut desc_chain = desc_chain.next_descriptor();
-        // TODO(jstaron): Update this code to take the indirect descriptors into account.
-        if self.filter == DescriptorFilter::OnlyReadable {
-            // When encounter first write-only descriptor set `desc_chain` to None to stop
-            // further processing.
-            desc_chain = desc_chain.filter(DescriptorChain::is_read_only);
+            let rem = count - buflen;
+            if (rem as u64) < vs.size() {
+                let buf = vs.sub_slice(0, rem as u64).map_err(|e| {
+                    io::Error::new(io::ErrorKind::InvalidData, Error::VolatileMemoryError(e))
+                })?;
+                bufs.push(buf);
+                buflen += rem;
+            } else {
+                bufs.push(vs);
+                buflen += vs.size() as usize;
+            }
         }
-        desc_chain
-    }
 
-    fn seek_from_start(&mut self, offset: usize) -> Result<()> {
-        if offset < self.bytes_consumed {
-            // Restart from the beginning of the descriptor chain.
-            self.bytes_consumed = 0;
-            self.avail_bytes = None;
-            self.desc_chain = self.desc_chain_start.clone();
+        if bufs.is_empty() {
+            return Ok(0);
         }
 
-        let mut count = offset - self.bytes_consumed;
-        while count > 0 {
-            let bytes_consumed = self.consume(|_, _| Ok(()), count)?;
-            if bytes_consumed == 0 {
+        let bytes_consumed = f(&*bufs)?;
+
+        // This can happen if a driver tricks a device into reading/writing more data than
+        // fits in a `usize`.
+        let total_bytes_consumed =
+            self.bytes_consumed
+                .checked_add(bytes_consumed)
+                .ok_or_else(|| {
+                    io::Error::new(io::ErrorKind::InvalidData, Error::DescriptorChainOverflow)
+                })?;
+
+        let mut rem = bytes_consumed;
+        while let Some(vs) = self.buffers.pop_front() {
+            if (rem as u64) < vs.size() {
+                // Split the slice and push the remainder back into the buffer list. Safe because we
+                // know that `rem` is not out of bounds due to the check and we checked the bounds
+                // on `vs` when we added it to the buffer list.
+                self.buffers.push_front(vs.offset(rem as u64).unwrap());
                 break;
             }
-            count -= bytes_consumed;
+
+            // No need for checked math because we know that `vs.size() <= rem`.
+            rem -= vs.size() as usize;
         }
 
-        Ok(())
+        self.bytes_consumed = total_bytes_consumed;
+
+        Ok(bytes_consumed)
     }
 
-    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
-        fn apply_signed_offset(base: usize, offset: i64) -> io::Result<u64> {
-            let base = i64::try_from(base).map_err(|_| {
-                io::Error::new(io::ErrorKind::InvalidData, "seek position out of i64 range")
-            })?;
-            let result = base.checked_add(offset).ok_or(io::Error::new(
-                io::ErrorKind::InvalidData,
-                "seek offset overflowed",
-            ))?;
-            if result < 0 {
-                return Err(io::Error::new(
-                    io::ErrorKind::InvalidData,
-                    "seek offset < 0",
-                ));
+    fn split_at(&mut self, offset: usize) -> Result<DescriptorChainConsumer<'a>> {
+        let mut rem = offset;
+        let pos = self.buffers.iter().position(|vs| {
+            if (rem as u64) < vs.size() {
+                true
+            } else {
+                rem -= vs.size() as usize;
+                false
             }
-            Ok(result as u64)
-        }
-
-        let offset = match pos {
-            io::SeekFrom::Start(o) => o,
-            io::SeekFrom::Current(o) => apply_signed_offset(self.bytes_consumed(), o)?,
-            io::SeekFrom::End(o) => {
-                apply_signed_offset(self.bytes_consumed() + self.available_bytes(), o)?
+        });
+
+        if let Some(at) = pos {
+            let mut other = self.buffers.split_off(at);
+
+            if rem > 0 {
+                // There must be at least one element in `other` because we checked
+                // its `size` value in the call to `position` above.
+                let front = other.pop_front().expect("empty VecDeque after split");
+                self.buffers.push_back(
+                    front
+                        .sub_slice(0, rem as u64)
+                        .map_err(Error::VolatileMemoryError)?,
+                );
+                other.push_front(
+                    front
+                        .offset(rem as u64)
+                        .map_err(Error::VolatileMemoryError)?,
+                );
             }
-        };
 
-        let offset = usize::try_from(offset).map_err(|_| {
-            io::Error::new(io::ErrorKind::InvalidData, "seek offset overflowed usize")
-        })?;
-        self.seek_from_start(offset)
-            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
-
-        Ok(self.bytes_consumed() as u64)
+            Ok(DescriptorChainConsumer {
+                buffers: other,
+                bytes_consumed: 0,
+            })
+        } else if rem == 0 {
+            Ok(DescriptorChainConsumer {
+                buffers: VecDeque::new(),
+                bytes_consumed: 0,
+            })
+        } else {
+            Err(Error::SplitOutOfBounds(offset))
+        }
     }
 }
 
@@ -203,107 +185,98 @@ impl<'a> DescriptorChainConsumer<'a> {
 /// descriptor is encountered.
 #[derive(Clone)]
 pub struct Reader<'a> {
-    mem: &'a GuestMemory,
     buffer: DescriptorChainConsumer<'a>,
 }
 
 impl<'a> Reader<'a> {
     /// Construct a new Reader wrapper over `desc_chain`.
-    pub fn new(mem: &'a GuestMemory, desc_chain: DescriptorChain<'a>) -> Reader<'a> {
+    pub fn new(mem: &'a GuestMemory, desc_chain: DescriptorChain<'a>) -> Result<Reader<'a>> {
         // TODO(jstaron): Update this code to take the indirect descriptors into account.
-        let desc_chain = if desc_chain.is_read_only() {
-            Some(desc_chain)
-        } else {
-            None
-        };
-        Reader {
-            mem,
-            buffer: DescriptorChainConsumer::new(desc_chain, DescriptorFilter::OnlyReadable),
-        }
-    }
-
-    /// Reads to a slice from the descriptor chain buffer.
-    /// Reads as many bytes as necessary to completely fill
-    /// the specified slice or to consume all bytes from the
-    /// descriptor chain buffer. Returns number of copied bytes.
-    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-        let mem = self.mem;
-        let len = buf.len();
-        let mut read_count = 0;
-        self.buffer.consume(
-            move |addr, count| {
-                let result = mem.read_exact_at_addr(&mut buf[read_count..read_count + count], addr);
-                if result.is_ok() {
-                    read_count += count;
-                }
-                result.map_err(Error::GuestMemoryError)
+        let buffers = desc_chain
+            .into_iter()
+            .readable()
+            .map(|desc| {
+                mem.get_slice(desc.addr.offset(), desc.len.into())
+                    .map_err(Error::VolatileMemoryError)
+            })
+            .collect::<Result<VecDeque<VolatileSlice<'a>>>>()?;
+        Ok(Reader {
+            buffer: DescriptorChainConsumer {
+                buffers,
+                bytes_consumed: 0,
             },
-            len,
-        )
-    }
-
-    /// Reads to a slice from the descriptor chain.
-    /// Returns an error if there isn't enough data in the
-    /// descriptor chain buffer to fill the entire slice. Part of
-    /// the slice may have been filled nevertheless.
-    pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
-        let count = self.read(buf)?;
-        if count == buf.len() {
-            Ok(())
-        } else {
-            Err(Error::GuestMemoryError(GuestMemoryError::ShortRead {
-                expected: buf.len(),
-                completed: count,
-            }))
-        }
+        })
     }
 
     /// Reads an object from the descriptor chain buffer.
-    pub fn read_obj<T: DataInit + Default>(&mut self) -> Result<T> {
-        let mut object: T = Default::default();
-        self.read_exact(object.as_mut_slice()).map(|_| object)
+    pub fn read_obj<T: DataInit>(&mut self) -> io::Result<T> {
+        let mut obj = MaybeUninit::<T>::uninit();
+
+        // Safe because `MaybeUninit` guarantees that the pointer is valid for
+        // `size_of::<T>()` bytes.
+        let buf = unsafe {
+            ::std::slice::from_raw_parts_mut(obj.as_mut_ptr() as *mut u8, size_of::<T>())
+        };
+
+        self.read_exact(buf)?;
+
+        // Safe because any type that implements `DataInit` can be considered initialized
+        // even if it is filled with random data.
+        Ok(unsafe { obj.assume_init() })
     }
 
     /// Reads data from the descriptor chain buffer into a file descriptor.
     /// Returns the number of bytes read from the descriptor chain buffer.
     /// The number of bytes read can be less than `count` if there isn't
     /// enough data in the descriptor chain buffer.
-    pub fn read_to(&mut self, dst: &dyn AsRawFd, count: usize) -> Result<usize> {
-        let mem = self.mem;
-        self.buffer.consume(
-            |addr, count| {
-                mem.write_from_memory(addr, dst, count)
-                    .map_err(Error::GuestMemoryError)
-            },
-            count,
-        )
+    pub fn read_to<F: FileReadWriteVolatile>(
+        &mut self,
+        mut dst: F,
+        count: usize,
+    ) -> io::Result<usize> {
+        self.buffer
+            .consume(count, |bufs| dst.write_vectored_volatile(bufs))
     }
 
-    /// Reads data from the descriptor chain buffer into a FileReadWriteVolatile.
+    /// Reads data from the descriptor chain buffer into a File at offset `off`.
     /// Returns the number of bytes read from the descriptor chain buffer.
     /// The number of bytes read can be less than `count` if there isn't
     /// enough data in the descriptor chain buffer.
-    pub fn read_to_volatile<T: FileReadWriteVolatile + ?Sized>(
+    pub fn read_to_at<F: FileReadWriteAtVolatile>(
         &mut self,
-        dst: &mut T,
+        mut dst: F,
         count: usize,
-    ) -> Result<usize> {
-        let mem = self.mem;
-        self.buffer.consume(
-            |addr, count| {
-                let mem_volatile_slice = mem
-                    .get_slice(addr.offset(), count as u64)
-                    .map_err(Error::VolatileMemoryError)?;
-                dst.write_all_volatile(mem_volatile_slice)
-                    .map_err(Error::IoError)?;
-                Ok(())
-            },
-            count,
-        )
+        off: u64,
+    ) -> io::Result<usize> {
+        self.buffer
+            .consume(count, |bufs| dst.write_vectored_at_volatile(bufs, off))
+    }
+
+    pub fn read_exact_to<F: FileReadWriteVolatile>(
+        &mut self,
+        mut dst: F,
+        mut count: usize,
+    ) -> io::Result<()> {
+        while count > 0 {
+            match self.read_to(&mut dst, count) {
+                Ok(0) => {
+                    return Err(io::Error::new(
+                        io::ErrorKind::UnexpectedEof,
+                        "failed to fill whole buffer",
+                    ))
+                }
+                Ok(n) => count -= n,
+                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+
+        Ok(())
     }
 
-    /// Returns number of bytes available for reading.
-    pub fn available_bytes(&mut self) -> usize {
+    /// Returns number of bytes available for reading.  May return an error if the combined
+    /// lengths of all the buffers in the DescriptorChain would cause an integer overflow.
+    pub fn available_bytes(&self) -> Result<usize> {
         self.buffer.available_bytes()
     }
 
@@ -311,6 +284,29 @@ impl<'a> Reader<'a> {
     pub fn bytes_read(&self) -> usize {
         self.buffer.bytes_consumed()
     }
+
+    /// Splits this `Reader` into two at the given offset in the `DescriptorChain` buffer.
+    /// After the split, `self` will be able to read up to `offset` bytes while the returned
+    /// `Reader` can read up to `available_bytes() - offset` bytes.  Returns an error if
+    /// `offset > self.available_bytes()`.
+    pub fn split_at(&mut self, offset: usize) -> Result<Reader<'a>> {
+        self.buffer.split_at(offset).map(|buffer| Reader { buffer })
+    }
+}
+
+impl<'a> io::Read for Reader<'a> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.buffer.consume(buf.len(), |bufs| {
+            if let Some(vs) = bufs.first() {
+                // This is guaranteed by the implementation of `consume`.
+                debug_assert_eq!(vs.size(), cmp::min(buf.len() as u64, vs.size()));
+                vs.copy_to(buf);
+                Ok(vs.size() as usize)
+            } else {
+                Ok(0)
+            }
+        })
+    }
 }
 
 /// Provides high-level interface over the sequence of memory regions
@@ -320,65 +316,38 @@ impl<'a> Reader<'a> {
 /// descriptors after any device-readable descriptors (2.6.4.2 in Virtio Spec v1.1).
 /// Writer will start iterating the descriptors from the first writable one and will
 /// assume that all following descriptors are writable.
+#[derive(Clone)]
 pub struct Writer<'a> {
-    mem: &'a GuestMemory,
     buffer: DescriptorChainConsumer<'a>,
 }
 
 impl<'a> Writer<'a> {
     /// Construct a new Writer wrapper over `desc_chain`.
-    pub fn new(mem: &'a GuestMemory, desc_chain: DescriptorChain<'a>) -> Writer<'a> {
-        // Skip all readable descriptors and get first writable one.
-        let desc_chain = desc_chain.into_iter().writable().next();
-        Writer {
-            mem,
-            buffer: DescriptorChainConsumer::new(desc_chain, DescriptorFilter::OnlyWritable),
-        }
-    }
-
-    /// Writes a slice to the descriptor chain buffer.
-    /// Returns the number of bytes written. The number of bytes written
-    /// can be less than the length of the slice if there isn't enough
-    /// space in the descriptor chain buffer.
-    pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
-        let mem = self.mem;
-        let len = buf.len();
-        let mut write_count = 0;
-        self.buffer.consume(
-            move |addr, count| {
-                let result = mem.write_all_at_addr(&buf[write_count..write_count + count], addr);
-                if result.is_ok() {
-                    write_count += count;
-                }
-                result.map_err(Error::GuestMemoryError)
+    pub fn new(mem: &'a GuestMemory, desc_chain: DescriptorChain<'a>) -> Result<Writer<'a>> {
+        let buffers = desc_chain
+            .into_iter()
+            .writable()
+            .map(|desc| {
+                mem.get_slice(desc.addr.offset(), desc.len.into())
+                    .map_err(Error::VolatileMemoryError)
+            })
+            .collect::<Result<VecDeque<VolatileSlice<'a>>>>()?;
+        Ok(Writer {
+            buffer: DescriptorChainConsumer {
+                buffers,
+                bytes_consumed: 0,
             },
-            len,
-        )
-    }
-
-    /// Writes the entire contents of a slice to descriptor chain buffer.
-    /// Returns an error if there isn't enough room in the descriptor chain buffer
-    /// to complete the entire write. Part of the data may have been written
-    /// nevertheless.
-    pub fn write_all(&mut self, buf: &[u8]) -> Result<()> {
-        let count = self.write(buf)?;
-        if count == buf.len() {
-            Ok(())
-        } else {
-            Err(Error::GuestMemoryError(GuestMemoryError::ShortRead {
-                expected: buf.len(),
-                completed: count,
-            }))
-        }
+        })
     }
 
     /// Writes an object to the descriptor chain buffer.
-    pub fn write_obj<T: DataInit>(&mut self, val: T) -> Result<()> {
+    pub fn write_obj<T: DataInit>(&mut self, val: T) -> io::Result<()> {
         self.write_all(val.as_slice())
     }
 
-    /// Returns number of bytes available for writing.
-    pub fn available_bytes(&mut self) -> usize {
+    /// Returns number of bytes available for writing.  May return an error if the combined
+    /// lengths of all the buffers in the DescriptorChain would cause an overflow.
+    pub fn available_bytes(&self) -> Result<usize> {
         self.buffer.available_bytes()
     }
 
@@ -386,57 +355,77 @@ impl<'a> Writer<'a> {
     /// Returns the number of bytes written to the descriptor chain buffer.
     /// The number of bytes written can be less than `count` if
     /// there isn't enough data in the descriptor chain buffer.
-    pub fn write_from(&mut self, src: &dyn AsRawFd, count: usize) -> Result<usize> {
-        let mem = self.mem;
-        self.buffer.consume(
-            |addr, count| {
-                mem.read_to_memory(addr, src, count)
-                    .map_err(Error::GuestMemoryError)
-            },
-            count,
-        )
+    pub fn write_from<F: FileReadWriteVolatile>(
+        &mut self,
+        mut src: F,
+        count: usize,
+    ) -> io::Result<usize> {
+        self.buffer
+            .consume(count, |bufs| src.read_vectored_volatile(bufs))
     }
 
-    /// Writes data to the descriptor chain buffer from a FileReadWriteVolatile.
+    /// Writes data to the descriptor chain buffer from a File at offset `off`.
     /// Returns the number of bytes written to the descriptor chain buffer.
     /// The number of bytes written can be less than `count` if
     /// there isn't enough data in the descriptor chain buffer.
-    pub fn write_from_volatile<T: FileReadWriteVolatile + ?Sized>(
+    pub fn write_from_at<F: FileReadWriteAtVolatile>(
         &mut self,
-        src: &mut T,
+        mut src: F,
         count: usize,
-    ) -> Result<usize> {
-        let mem = self.mem;
-        self.buffer.consume(
-            |addr, count| {
-                let mem_volatile_slice = mem
-                    .get_slice(addr.offset(), count as u64)
-                    .map_err(Error::VolatileMemoryError)?;
-                src.read_exact_volatile(mem_volatile_slice)
-                    .map_err(Error::IoError)?;
-                Ok(())
-            },
-            count,
-        )
+        off: u64,
+    ) -> io::Result<usize> {
+        self.buffer
+            .consume(count, |bufs| src.read_vectored_at_volatile(bufs, off))
+    }
+
+    pub fn write_all_from<F: FileReadWriteVolatile>(
+        &mut self,
+        mut src: F,
+        mut count: usize,
+    ) -> io::Result<()> {
+        while count > 0 {
+            match self.write_from(&mut src, count) {
+                Ok(0) => {
+                    return Err(io::Error::new(
+                        io::ErrorKind::WriteZero,
+                        "failed to write whole buffer",
+                    ))
+                }
+                Ok(n) => count -= n,
+                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+
+        Ok(())
     }
 
     /// Returns number of bytes already written to the descriptor chain buffer.
     pub fn bytes_written(&self) -> usize {
         self.buffer.bytes_consumed()
     }
-}
 
-impl<'a> io::Read for Reader<'a> {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.read(buf)
-            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
+    /// Splits this `Writer` into two at the given offset in the `DescriptorChain` buffer.
+    /// After the split, `self` will be able to write up to `offset` bytes while the returned
+    /// `Writer` can write up to `available_bytes() - offset` bytes.  Returns an error if
+    /// `offset > self.available_bytes()`.
+    pub fn split_at(&mut self, offset: usize) -> Result<Writer<'a>> {
+        self.buffer.split_at(offset).map(|buffer| Writer { buffer })
     }
 }
 
 impl<'a> io::Write for Writer<'a> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.write(buf)
-            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
+        self.buffer.consume(buf.len(), |bufs| {
+            if let Some(vs) = bufs.first() {
+                // This is guaranteed by the implementation of `consume`.
+                debug_assert_eq!(vs.size(), cmp::min(buf.len() as u64, vs.size()));
+                vs.copy_from(buf);
+                Ok(vs.size() as usize)
+            } else {
+                Ok(0)
+            }
+        })
     }
 
     fn flush(&mut self) -> io::Result<()> {
@@ -445,18 +434,6 @@ impl<'a> io::Write for Writer<'a> {
     }
 }
 
-impl<'a> io::Seek for Reader<'a> {
-    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
-        self.buffer.seek(pos)
-    }
-}
-
-impl<'a> io::Seek for Writer<'a> {
-    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
-        self.buffer.seek(pos)
-    }
-}
-
 const VIRTQ_DESC_F_NEXT: u16 = 0x1;
 const VIRTQ_DESC_F_WRITE: u16 = 0x2;
 
@@ -524,7 +501,6 @@ pub fn create_descriptor_chain(
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::io::{Seek, SeekFrom};
     use sys_util::{MemfdSeals, SharedMemory};
 
     #[test]
@@ -547,8 +523,13 @@ mod tests {
             0,
         )
         .expect("create_descriptor_chain failed");
-        let mut reader = Reader::new(&memory, chain);
-        assert_eq!(reader.available_bytes(), 106);
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            106
+        );
         assert_eq!(reader.bytes_read(), 0);
 
         let mut buffer = [0 as u8; 64];
@@ -556,7 +537,12 @@ mod tests {
             panic!("read_exact should not fail here");
         }
 
-        assert_eq!(reader.available_bytes(), 42);
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            42
+        );
         assert_eq!(reader.bytes_read(), 64);
 
         match reader.read(&mut buffer) {
@@ -564,7 +550,12 @@ mod tests {
             Ok(length) => assert_eq!(length, 42),
         }
 
-        assert_eq!(reader.available_bytes(), 0);
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(reader.bytes_read(), 106);
     }
 
@@ -588,8 +579,13 @@ mod tests {
             0,
         )
         .expect("create_descriptor_chain failed");;
-        let mut writer = Writer::new(&memory, chain);
-        assert_eq!(writer.available_bytes(), 106);
+        let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            106
+        );
         assert_eq!(writer.bytes_written(), 0);
 
         let mut buffer = [0 as u8; 64];
@@ -597,7 +593,12 @@ mod tests {
             panic!("write_all should not fail here");
         }
 
-        assert_eq!(writer.available_bytes(), 42);
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            42
+        );
         assert_eq!(writer.bytes_written(), 64);
 
         match writer.write(&mut buffer) {
@@ -605,7 +606,12 @@ mod tests {
             Ok(length) => assert_eq!(length, 42),
         }
 
-        assert_eq!(writer.available_bytes(), 0);
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(writer.bytes_written(), 106);
     }
 
@@ -624,13 +630,23 @@ mod tests {
             0,
         )
         .expect("create_descriptor_chain failed");;
-        let mut reader = Reader::new(&memory, chain);
-        assert_eq!(reader.available_bytes(), 0);
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(reader.bytes_read(), 0);
 
         assert!(reader.read_obj::<u8>().is_err());
 
-        assert_eq!(reader.available_bytes(), 0);
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(reader.bytes_read(), 0);
     }
 
@@ -649,13 +665,23 @@ mod tests {
             0,
         )
         .expect("create_descriptor_chain failed");;
-        let mut writer = Writer::new(&memory, chain);
-        assert_eq!(writer.available_bytes(), 0);
+        let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(writer.bytes_written(), 0);
 
         assert!(writer.write_obj(0u8).is_err());
 
-        assert_eq!(writer.available_bytes(), 0);
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(writer.bytes_written(), 0);
     }
 
@@ -675,7 +701,7 @@ mod tests {
         )
         .expect("create_descriptor_chain failed");;
 
-        let mut reader = Reader::new(&memory, chain);
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
 
         // GuestMemory's write_from_memory requires raw file descriptor.
         let mut shm = SharedMemory::anon().unwrap();
@@ -686,14 +712,20 @@ mod tests {
         fd_seals.set_grow_seal();
         shm.add_seals(fd_seals).unwrap();
 
-        if let Ok(_) = reader.read_to(&shm, 512) {
-            panic!("read_to should fail here, got Ok(_) instead");
-        }
-
-        assert!(reader.available_bytes() < 512);
-        assert!(reader.available_bytes() > 0);
-        assert!(reader.bytes_read() < 512);
-        assert!(reader.bytes_read() > 0);
+        reader
+            .read_exact_to(&mut shm, 512)
+            .expect_err("successfully read more bytes than SharedMemory size");
+
+        // Linux doesn't do partial writes if you give a buffer larger than the remaining length of
+        // the shared memory. And since we passed an iovec with the full contents of the
+        // DescriptorChain we ended up not writing any bytes at all.
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            512
+        );
+        assert_eq!(reader.bytes_read(), 0);
     }
 
     #[test]
@@ -710,22 +742,25 @@ mod tests {
             vec![(Writable, 256), (Writable, 256)],
             0,
         )
-        .expect("create_descriptor_chain failed");;
+        .expect("create_descriptor_chain failed");
 
-        let mut writer = Writer::new(&memory, chain);
+        let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
 
         // GuestMemory's read_to_memory requires raw file descriptor.
         let mut shm = SharedMemory::anon().unwrap();
         shm.set_size(384).unwrap();
 
-        if let Ok(_) = writer.write_from(&shm, 512) {
-            panic!("write_from should fail here, got Ok(_) instead");
-        }
+        writer
+            .write_all_from(&mut shm, 512)
+            .expect_err("successfully wrote more bytes than in SharedMemory");
 
-        assert!(writer.available_bytes() < 512);
-        assert!(writer.available_bytes() > 0);
-        assert!(writer.bytes_written() < 512);
-        assert!(writer.bytes_written() > 0);
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            128
+        );
+        assert_eq!(writer.bytes_written(), 384);
     }
 
     #[test]
@@ -749,28 +784,40 @@ mod tests {
             ],
             0,
         )
-        .expect("create_descriptor_chain failed");;
-        let mut reader = Reader::new(&memory, chain.clone());
-        let mut writer = Writer::new(&memory, chain);
+        .expect("create_descriptor_chain failed");
+        let mut reader = Reader::new(&memory, chain.clone()).expect("failed to create Reader");
+        let mut writer = Writer::new(&memory, chain).expect("failed to create Writer");
 
         assert_eq!(reader.bytes_read(), 0);
         assert_eq!(writer.bytes_written(), 0);
 
-        let mut buffer = [0 as u8; 200];
+        let mut buffer = Vec::with_capacity(200);
 
-        match reader.read(&mut buffer) {
-            Err(_) => panic!("read should not fail here"),
-            Ok(length) => assert_eq!(length, 128),
-        }
+        assert_eq!(
+            reader
+                .read_to_end(&mut buffer)
+                .expect("read should not fail here"),
+            128
+        );
 
-        match writer.write(&mut buffer) {
-            Err(_) => panic!("write should not fail here"),
-            Ok(length) => assert_eq!(length, 68),
-        }
+        // The writable descriptors are only 68 bytes long.
+        writer
+            .write_all(&buffer[..68])
+            .expect("write should not fail here");
 
-        assert_eq!(reader.available_bytes(), 0);
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(reader.bytes_read(), 128);
-        assert_eq!(writer.available_bytes(), 0);
+        assert_eq!(
+            writer
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
         assert_eq!(writer.bytes_written(), 68);
     }
 
@@ -791,8 +838,8 @@ mod tests {
             vec![(Writable, 1), (Writable, 1), (Writable, 1), (Writable, 1)],
             123,
         )
-        .expect("create_descriptor_chain failed");;
-        let mut writer = Writer::new(&memory, chain_writer);
+        .expect("create_descriptor_chain failed");
+        let mut writer = Writer::new(&memory, chain_writer).expect("failed to create Writer");
         if let Err(_) = writer.write_obj(secret) {
             panic!("write_obj should not fail here");
         }
@@ -806,7 +853,7 @@ mod tests {
             123,
         )
         .expect("create_descriptor_chain failed");
-        let mut reader = Reader::new(&memory, chain_reader);
+        let mut reader = Reader::new(&memory, chain_reader).expect("failed to create Reader");
         match reader.read_obj::<Le32>() {
             Err(_) => panic!("read_obj should not fail here"),
             Ok(read_secret) => assert_eq!(read_secret, secret),
@@ -814,63 +861,217 @@ mod tests {
     }
 
     #[test]
-    fn reader_seek_simple_chain() {
+    fn reader_unexpected_eof() {
         use DescriptorType::*;
 
         let memory_start_addr = GuestAddress(0x0);
-        let memory = GuestMemory::new(&[(memory_start_addr, 0x10000)]).unwrap();
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
+
+        let chain = create_descriptor_chain(
+            &memory,
+            GuestAddress(0x0),
+            GuestAddress(0x100),
+            vec![(Readable, 256), (Readable, 256)],
+            0,
+        )
+        .expect("create_descriptor_chain failed");
+
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        let mut buf = Vec::with_capacity(1024);
+        buf.resize(1024, 0);
+
+        assert_eq!(
+            reader
+                .read_exact(&mut buf[..])
+                .expect_err("read more bytes than available")
+                .kind(),
+            io::ErrorKind::UnexpectedEof
+        );
+    }
+
+    #[test]
+    fn split_border() {
+        use DescriptorType::*;
+
+        let memory_start_addr = GuestAddress(0x0);
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
 
         let chain = create_descriptor_chain(
             &memory,
             GuestAddress(0x0),
             GuestAddress(0x100),
             vec![
-                (Readable, 8),
                 (Readable, 16),
-                (Readable, 18),
-                (Readable, 64),
+                (Readable, 16),
+                (Readable, 96),
+                (Writable, 64),
+                (Writable, 1),
+                (Writable, 3),
             ],
             0,
         )
         .expect("create_descriptor_chain failed");;
-        let mut reader = Reader::new(&memory, chain);
-        assert_eq!(reader.available_bytes(), 106);
-        assert_eq!(reader.bytes_read(), 0);
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        let other = reader.split_at(32).expect("failed to split Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            32
+        );
+        assert_eq!(
+            other
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            96
+        );
+    }
 
-        // Skip some bytes.  available_bytes() and bytes_read() should update accordingly.
-        reader
-            .seek(SeekFrom::Current(64))
-            .expect("seek should not fail here");
-        assert_eq!(reader.available_bytes(), 42);
-        assert_eq!(reader.bytes_read(), 64);
+    #[test]
+    fn split_middle() {
+        use DescriptorType::*;
 
-        // Seek past end of chain - position should point just past the last byte.
-        reader
-            .seek(SeekFrom::Current(64))
-            .expect("seek should not fail here");
-        assert_eq!(reader.available_bytes(), 0);
-        assert_eq!(reader.bytes_read(), 106);
+        let memory_start_addr = GuestAddress(0x0);
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
 
-        // Seek back to the beginning.
-        reader
-            .seek(SeekFrom::Start(0))
-            .expect("seek should not fail here");
-        assert_eq!(reader.available_bytes(), 106);
-        assert_eq!(reader.bytes_read(), 0);
+        let chain = create_descriptor_chain(
+            &memory,
+            GuestAddress(0x0),
+            GuestAddress(0x100),
+            vec![
+                (Readable, 16),
+                (Readable, 16),
+                (Readable, 96),
+                (Writable, 64),
+                (Writable, 1),
+                (Writable, 3),
+            ],
+            0,
+        )
+        .expect("create_descriptor_chain failed");
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        let other = reader.split_at(24).expect("failed to split Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            24
+        );
+        assert_eq!(
+            other
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            104
+        );
+    }
 
-        // Seek to one byte before the end.
-        reader
-            .seek(SeekFrom::End(-1))
-            .expect("seek should not fail here");
-        assert_eq!(reader.available_bytes(), 1);
-        assert_eq!(reader.bytes_read(), 105);
+    #[test]
+    fn split_end() {
+        use DescriptorType::*;
 
-        // Read the last byte.
-        let mut buffer = [0 as u8; 1];
-        reader
-            .read_exact(&mut buffer)
-            .expect("read_exact should not fail here");
-        assert_eq!(reader.available_bytes(), 0);
-        assert_eq!(reader.bytes_read(), 106);
+        let memory_start_addr = GuestAddress(0x0);
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
+
+        let chain = create_descriptor_chain(
+            &memory,
+            GuestAddress(0x0),
+            GuestAddress(0x100),
+            vec![
+                (Readable, 16),
+                (Readable, 16),
+                (Readable, 96),
+                (Writable, 64),
+                (Writable, 1),
+                (Writable, 3),
+            ],
+            0,
+        )
+        .expect("create_descriptor_chain failed");
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        let other = reader.split_at(128).expect("failed to split Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            128
+        );
+        assert_eq!(
+            other
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
+    }
+
+    #[test]
+    fn split_beginning() {
+        use DescriptorType::*;
+
+        let memory_start_addr = GuestAddress(0x0);
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
+
+        let chain = create_descriptor_chain(
+            &memory,
+            GuestAddress(0x0),
+            GuestAddress(0x100),
+            vec![
+                (Readable, 16),
+                (Readable, 16),
+                (Readable, 96),
+                (Writable, 64),
+                (Writable, 1),
+                (Writable, 3),
+            ],
+            0,
+        )
+        .expect("create_descriptor_chain failed");
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        let other = reader.split_at(0).expect("failed to split Reader");
+        assert_eq!(
+            reader
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            0
+        );
+        assert_eq!(
+            other
+                .available_bytes()
+                .expect("failed to get available bytes"),
+            128
+        );
+    }
+
+    #[test]
+    fn split_outofbounds() {
+        use DescriptorType::*;
+
+        let memory_start_addr = GuestAddress(0x0);
+        let memory = GuestMemory::new(&vec![(memory_start_addr, 0x10000)]).unwrap();
+
+        let chain = create_descriptor_chain(
+            &memory,
+            GuestAddress(0x0),
+            GuestAddress(0x100),
+            vec![
+                (Readable, 16),
+                (Readable, 16),
+                (Readable, 96),
+                (Writable, 64),
+                (Writable, 1),
+                (Writable, 3),
+            ],
+            0,
+        )
+        .expect("create_descriptor_chain failed");
+        let mut reader = Reader::new(&memory, chain).expect("failed to create Reader");
+
+        if let Ok(_) = reader.split_at(256) {
+            panic!("successfully split Reader with out of bounds offset");
+        }
     }
 }
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 29142ea..97c84d5 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -8,6 +8,7 @@ mod protocol;
 use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::i64;
+use std::io::Read;
 use std::mem::{self, size_of};
 use std::num::NonZeroU8;
 use std::os::unix::io::{AsRawFd, RawFd};
@@ -124,7 +125,14 @@ impl Frontend {
                 mem,
             ),
             GpuCommand::ResourceAttachBacking(info) => {
-                if reader.available_bytes() != 0 {
+                let available_bytes = match reader.available_bytes() {
+                    Ok(count) => count,
+                    Err(e) => {
+                        debug!("invalid descriptor: {}", e);
+                        0
+                    }
+                };
+                if available_bytes != 0 {
                     let entry_count = info.nr_entries.to_native() as usize;
                     let mut iovecs = Vec::with_capacity(entry_count);
                     for _ in 0..entry_count {
@@ -247,10 +255,17 @@ impl Frontend {
                 )
             }
             GpuCommand::CmdSubmit3d(info) => {
-                if reader.available_bytes() != 0 {
+                let available_bytes = match reader.available_bytes() {
+                    Ok(count) => count,
+                    Err(e) => {
+                        debug!("invalid descriptor: {}", e);
+                        0
+                    }
+                };
+                if available_bytes != 0 {
                     let cmd_size = info.size.to_native() as usize;
                     let mut cmd_buf = vec![0; cmd_size];
-                    if reader.read(&mut cmd_buf[..]).is_ok() {
+                    if reader.read_exact(&mut cmd_buf[..]).is_ok() {
                         self.backend
                             .submit_command(info.hdr.ctx_id.to_native(), &mut cmd_buf[..])
                     } else {
@@ -263,7 +278,14 @@ impl Frontend {
                 }
             }
             GpuCommand::AllocationMetadata(info) => {
-                if reader.available_bytes() != 0 {
+                let available_bytes = match reader.available_bytes() {
+                    Ok(count) => count,
+                    Err(e) => {
+                        debug!("invalid descriptor: {}", e);
+                        0
+                    }
+                };
+                if available_bytes != 0 {
                     let id = info.request_id.to_native();
                     let request_size = info.request_size.to_native();
                     let response_size = info.response_size.to_native();
@@ -275,7 +297,7 @@ impl Frontend {
 
                     let mut request_buf = vec![0; request_size as usize];
                     let response_buf = vec![0; response_size as usize];
-                    if reader.read(&mut request_buf[..]).is_ok() {
+                    if reader.read_exact(&mut request_buf[..]).is_ok() {
                         self.backend
                             .allocation_metadata(id, request_buf, response_buf)
                     } else {
@@ -286,7 +308,14 @@ impl Frontend {
                 }
             }
             GpuCommand::ResourceCreateV2(info) => {
-                if reader.available_bytes() != 0 {
+                let available_bytes = match reader.available_bytes() {
+                    Ok(count) => count,
+                    Err(e) => {
+                        debug!("invalid descriptor: {}", e);
+                        0
+                    }
+                };
+                if available_bytes != 0 {
                     let resource_id = info.resource_id.to_native();
                     let guest_memory_type = info.guest_memory_type.to_native();
                     let size = info.size.to_native();
@@ -314,7 +343,7 @@ impl Frontend {
                         }
                     }
 
-                    match reader.read(&mut args[..]) {
+                    match reader.read_exact(&mut args[..]) {
                         Ok(_) => self.backend.resource_create_v2(
                             resource_id,
                             guest_memory_type,
@@ -346,13 +375,23 @@ impl Frontend {
         let mut signal_used = false;
         while let Some(desc) = queue.pop(mem) {
             if Frontend::validate_desc(&desc) {
-                let mut reader = Reader::new(mem, desc.clone());
-                let mut writer = Writer::new(mem, desc.clone());
-                if let Some(ret_desc) =
-                    self.process_descriptor(mem, desc.index, &mut reader, &mut writer)
-                {
-                    queue.add_used(&mem, ret_desc.index, ret_desc.len);
-                    signal_used = true;
+                match (
+                    Reader::new(mem, desc.clone()),
+                    Writer::new(mem, desc.clone()),
+                ) {
+                    (Ok(mut reader), Ok(mut writer)) => {
+                        if let Some(ret_desc) =
+                            self.process_descriptor(mem, desc.index, &mut reader, &mut writer)
+                        {
+                            queue.add_used(&mem, ret_desc.index, ret_desc.len);
+                            signal_used = true;
+                        }
+                    }
+                    (_, Err(e)) | (Err(e), _) => {
+                        debug!("invalid descriptor: {}", e);
+                        queue.add_used(&mem, desc.index, 0);
+                        signal_used = true;
+                    }
                 }
             } else {
                 let likely_type = mem.read_obj_from_addr(desc.addr).unwrap_or(Le32::from(0));
@@ -391,7 +430,16 @@ impl Frontend {
         if resp.is_err() {
             debug!("{:?} -> {:?}", gpu_cmd, resp);
         }
-        if writer.available_bytes() != 0 {
+
+        let available_bytes = match writer.available_bytes() {
+            Ok(count) => count,
+            Err(e) => {
+                debug!("invalid descriptor: {}", e);
+                0
+            }
+        };
+
+        if available_bytes != 0 {
             let mut fence_id = 0;
             let mut ctx_id = 0;
             let mut flags = 0;
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
index c6773dd..dacc850 100644
--- a/devices/src/virtio/gpu/protocol.rs
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -7,6 +7,7 @@
 
 use std::cmp::min;
 use std::fmt::{self, Display};
+use std::io::{self, Write};
 use std::marker::PhantomData;
 use std::mem::{size_of, size_of_val};
 use std::str::from_utf8;
@@ -589,6 +590,8 @@ pub enum GpuCommandDecodeError {
     Memory(DescriptorError),
     /// The type of the command was invalid.
     InvalidType(u32),
+    /// An I/O error occurred.
+    IO(io::Error),
 }
 
 impl Display for GpuCommandDecodeError {
@@ -602,6 +605,7 @@ impl Display for GpuCommandDecodeError {
                 e,
             ),
             InvalidType(n) => write!(f, "invalid command type ({})", n),
+            IO(e) => write!(f, "an I/O error occurred: {}", e),
         }
     }
 }
@@ -612,6 +616,12 @@ impl From<DescriptorError> for GpuCommandDecodeError {
     }
 }
 
+impl From<io::Error> for GpuCommandDecodeError {
+    fn from(e: io::Error) -> GpuCommandDecodeError {
+        GpuCommandDecodeError::IO(e)
+    }
+}
+
 impl fmt::Debug for GpuCommand {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use self::GpuCommand::*;
@@ -754,6 +764,8 @@ pub enum GpuResponseEncodeError {
     TooManyDisplays(usize),
     /// More planes than are valid were in a `OkResourcePlaneInfo`.
     TooManyPlanes(usize),
+    /// An I/O error occurred.
+    IO(io::Error),
 }
 
 impl Display for GpuResponseEncodeError {
@@ -768,6 +780,7 @@ impl Display for GpuResponseEncodeError {
             ),
             TooManyDisplays(n) => write!(f, "{} is more displays than are valid", n),
             TooManyPlanes(n) => write!(f, "{} is more planes than are valid", n),
+            IO(e) => write!(f, "an I/O error occurred: {}", e),
         }
     }
 }
@@ -778,6 +791,12 @@ impl From<DescriptorError> for GpuResponseEncodeError {
     }
 }
 
+impl From<io::Error> for GpuResponseEncodeError {
+    fn from(e: io::Error) -> GpuResponseEncodeError {
+        GpuResponseEncodeError::IO(e)
+    }
+}
+
 impl GpuResponse {
     /// Encodes a this `GpuResponse` into `resp` and the given set of metadata.
     pub fn encode(
@@ -823,7 +842,7 @@ impl GpuResponse {
             }
             GpuResponse::OkCapset(ref data) => {
                 resp.write_obj(hdr)?;
-                resp.write(data)?;
+                resp.write_all(data)?;
                 size_of_val(&hdr) + data.len()
             }
             GpuResponse::OkResourcePlaneInfo {
@@ -847,7 +866,7 @@ impl GpuResponse {
                     strides,
                     offsets,
                 };
-                if resp.available_bytes() >= size_of_val(&plane_info) {
+                if resp.available_bytes()? >= size_of_val(&plane_info) {
                     resp.write_obj(plane_info)?;
                     size_of_val(&plane_info)
                 } else {
@@ -869,7 +888,7 @@ impl GpuResponse {
                 };
 
                 resp.write_obj(resp_info)?;
-                resp.write(&res_info.response)?;
+                resp.write_all(&res_info.response)?;
                 size_of_val(&resp_info) + res_info.response.len()
             }
             _ => {
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs
index ce99d52..2459d24 100644
--- a/devices/src/virtio/input/mod.rs
+++ b/devices/src/virtio/input/mod.rs
@@ -17,7 +17,8 @@ use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken};
 
 use self::event_source::{input_event, EvdevEventSource, EventSource, SocketEventSource};
 use super::{
-    copy_config, Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING, TYPE_INPUT,
+    copy_config, DescriptorChain, DescriptorError, Queue, Reader, VirtioDevice, Writer,
+    INTERRUPT_STATUS_USED_RING, TYPE_INPUT,
 };
 use std::collections::BTreeMap;
 use std::fmt::{self, Display};
@@ -54,7 +55,14 @@ pub enum InputError {
     EvdevGrabError(sys_util::Error),
     // Detected error on guest side
     GuestError(String),
+    // Virtio descriptor error
+    Descriptor(DescriptorError),
+    // Error while reading from virtqueue
+    ReadQueue(std::io::Error),
+    // Error while writing to virtqueue
+    WriteQueue(std::io::Error),
 }
+
 pub type Result<T> = std::result::Result<T, InputError>;
 
 impl Display for InputError {
@@ -76,6 +84,9 @@ impl Display for InputError {
             }
             EvdevGrabError(e) => write!(f, "failed to grab event device: {}", e),
             GuestError(s) => write!(f, "detected error on guest side: {}", s),
+            Descriptor(e) => write!(f, "virtio descriptor error: {}", e),
+            ReadQueue(e) => write!(f, "failed to read from virtqueue: {}", e),
+            WriteQueue(e) => write!(f, "failed to write to virtqueue: {}", e),
         }
     }
 }
@@ -365,6 +376,27 @@ impl<T: EventSource> Worker<T> {
         self.interrupt_evt.write(1).unwrap();
     }
 
+    // Fills a virtqueue with events from the source.  Returns the number of bytes written.
+    fn fill_event_virtqueue(
+        event_source: &mut T,
+        avail_desc: DescriptorChain,
+        mem: &GuestMemory,
+    ) -> Result<usize> {
+        let mut writer = Writer::new(mem, avail_desc).map_err(InputError::Descriptor)?;
+
+        while writer.available_bytes().map_err(InputError::Descriptor)?
+            >= virtio_input_event::EVENT_SIZE
+        {
+            if let Some(evt) = event_source.pop_available_event() {
+                writer.write_obj(evt).map_err(InputError::WriteQueue)?;
+            } else {
+                break;
+            }
+        }
+
+        Ok(writer.bytes_written())
+    }
+
     // Send events from the source to the guest
     fn send_events(&mut self) -> bool {
         let mut needs_interrupt = false;
@@ -377,26 +409,23 @@ impl<T: EventSource> Worker<T> {
                 }
                 Some(avail_desc) => {
                     let avail_desc_index = avail_desc.index;
-                    let mut writer = Writer::new(&self.guest_memory, avail_desc);
-                    while writer.available_bytes() >= virtio_input_event::EVENT_SIZE {
-                        match self.event_source.pop_available_event() {
-                            Some(evt) => {
-                                if let Err(e) = writer.write_obj(evt) {
-                                    // 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,
+
+                    let bytes_written = match Worker::fill_event_virtqueue(
+                        &mut self.event_source,
+                        avail_desc,
+                        &self.guest_memory,
+                    ) {
+                        Ok(count) => count,
+                        Err(e) => {
+                            error!("Input: failed to send events to guest: {}", e);
+                            break;
                         }
-                    }
+                    };
 
                     self.event_queue.add_used(
                         &self.guest_memory,
                         avail_desc_index,
-                        writer.bytes_written() as u32,
+                        bytes_written as u32,
                     );
                     needs_interrupt = true;
                 }
@@ -406,38 +435,42 @@ impl<T: EventSource> Worker<T> {
         needs_interrupt
     }
 
+    // Sends events from the guest to the source.  Returns the number of bytes read.
+    fn read_event_virtqueue(
+        avail_desc: DescriptorChain,
+        event_source: &mut T,
+        mem: &GuestMemory,
+    ) -> Result<usize> {
+        let mut reader = Reader::new(mem, avail_desc).map_err(InputError::Descriptor)?;
+        while reader.available_bytes().map_err(InputError::Descriptor)?
+            >= virtio_input_event::EVENT_SIZE
+        {
+            let evt: virtio_input_event = reader.read_obj().map_err(InputError::ReadQueue)?;
+            event_source.send_event(&evt)?;
+        }
+
+        Ok(reader.bytes_read())
+    }
+
     fn process_status_queue(&mut self) -> Result<bool> {
         let mut needs_interrupt = false;
         while let Some(avail_desc) = self.status_queue.pop(&self.guest_memory) {
             let avail_desc_index = avail_desc.index;
-            let mut reader = Reader::new(&self.guest_memory, avail_desc);
-            if reader.available_bytes() % virtio_input_event::EVENT_SIZE != 0 {
-                warn!(
-                    "Ignoring buffer of unexpected size on status queue: {:0}",
-                    reader.available_bytes(),
-                );
-            } else {
-                while reader.available_bytes() >= virtio_input_event::EVENT_SIZE {
-                    match reader.read_obj::<virtio_input_event>() {
-                        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;
-                        }
-                    }
-                }
-            }
 
-            self.status_queue.add_used(
+            let bytes_read = match Worker::read_event_virtqueue(
+                avail_desc,
+                &mut self.event_source,
                 &self.guest_memory,
-                avail_desc_index,
-                reader.bytes_read() as u32,
-            );
+            ) {
+                Ok(count) => count,
+                Err(e) => {
+                    error!("Input: failed to read events from virtqueue: {}", e);
+                    break;
+                }
+            };
+
+            self.status_queue
+                .add_used(&self.guest_memory, avail_desc_index, bytes_read as u32);
             needs_interrupt = true;
         }
 
diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs
index 7680b66..6bc2e25 100644
--- a/devices/src/virtio/net.rs
+++ b/devices/src/virtio/net.rs
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 use std::fmt::{self, Display};
+use std::io::{self, Read, Write};
 use std::mem;
 use std::net::Ipv4Addr;
 use std::os::unix::io::{AsRawFd, RawFd};
@@ -13,15 +14,12 @@ use std::thread;
 use libc::{EAGAIN, EEXIST};
 use net_sys;
 use net_util::{Error as TapError, MacAddress, TapT};
-use sys_util::guest_memory::Error as MemoryError;
 use sys_util::Error as SysError;
 use sys_util::{error, warn, EventFd, GuestMemory, PollContext, PollToken};
 use virtio_sys::virtio_net::virtio_net_hdr_v1;
 use virtio_sys::{vhost, virtio_net};
 
-use super::{
-    DescriptorError, Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING, TYPE_NET,
-};
+use super::{Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING, TYPE_NET};
 
 /// The maximum buffer size when segmentation offload is enabled. This
 /// includes the 12-byte virtio net header.
@@ -122,23 +120,30 @@ where
         };
 
         let index = desc_chain.index;
-        let mut writer = Writer::new(&self.mem, desc_chain);
-
-        match writer.write_all(&self.rx_buf[0..self.rx_count]) {
-            Ok(()) => (),
-            Err(DescriptorError::GuestMemoryError(MemoryError::ShortWrite { .. })) => {
-                warn!(
-                    "net: rx: buffer is too small to hold frame of size {}",
-                    self.rx_count
-                );
+        let bytes_written = match Writer::new(&self.mem, desc_chain) {
+            Ok(mut writer) => {
+                match writer.write_all(&self.rx_buf[0..self.rx_count]) {
+                    Ok(()) => (),
+                    Err(ref e) if e.kind() == io::ErrorKind::WriteZero => {
+                        warn!(
+                            "net: rx: buffer is too small to hold frame of size {}",
+                            self.rx_count
+                        );
+                    }
+                    Err(e) => {
+                        warn!("net: rx: failed to write slice: {}", e);
+                    }
+                };
+
+                writer.bytes_written() as u32
             }
             Err(e) => {
-                warn!("net: rx: failed to write slice: {}", e);
+                error!("net: failed to create Writer: {}", e);
+                0
             }
-        }
+        };
 
-        self.rx_queue
-            .add_used(&self.mem, index, writer.bytes_written() as u32);
+        self.rx_queue.add_used(&self.mem, index, bytes_written);
 
         // Interrupt the guest immediately for received frames to
         // reduce latency.
@@ -174,21 +179,38 @@ where
     fn process_tx(&mut self) {
         let mut frame = [0u8; MAX_BUFFER_SIZE];
 
+        // Reads up to `buf.len()` bytes or until there is no more data in `r`, whichever
+        // is smaller.
+        fn read_to_end(mut r: Reader, buf: &mut [u8]) -> io::Result<usize> {
+            let mut count = 0;
+            while count < buf.len() {
+                match r.read(&mut buf[count..]) {
+                    Ok(0) => break,
+                    Ok(n) => count += n,
+                    Err(e) => return Err(e),
+                }
+            }
+
+            Ok(count)
+        }
+
         while let Some(desc_chain) = self.tx_queue.pop(&self.mem) {
             let index = desc_chain.index;
-            let mut reader = Reader::new(&self.mem, desc_chain);
-
-            match reader.read(&mut frame) {
-                // We need to copy frame into continuous buffer before writing it to tap
-                // because tap requires frame to complete in a single write.
-                Ok(read_count) => {
-                    if let Err(err) = self.tap.write_all(&frame[..read_count]) {
-                        error!("net: tx: failed to write to tap: {}", err);
+
+            match Reader::new(&self.mem, desc_chain) {
+                Ok(reader) => {
+                    match read_to_end(reader, &mut frame[..]) {
+                        Ok(len) => {
+                            // We need to copy frame into continuous buffer before writing it to tap
+                            // because tap requires frame to complete in a single write.
+                            if let Err(err) = self.tap.write_all(&frame[..len]) {
+                                error!("net: tx: failed to write to tap: {}", err);
+                            }
+                        }
+                        Err(e) => error!("net: tx: failed to read frame into buffer: {}", e),
                     }
                 }
-                Err(err) => {
-                    error!("net: tx: failed to read frame into buffer: {}", err);
-                }
+                Err(e) => error!("net: failed to create Reader: {}", e),
             }
 
             self.tx_queue.add_used(&self.mem, index, 0);
diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs
index 6968733..6d89a45 100644
--- a/devices/src/virtio/p9.rs
+++ b/devices/src/virtio/p9.rs
@@ -17,7 +17,8 @@ use sys_util::{error, warn, Error as SysError, EventFd, GuestMemory, PollContext
 use virtio_sys::vhost::VIRTIO_F_VERSION_1;
 
 use super::{
-    copy_config, Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING, TYPE_9P,
+    copy_config, DescriptorError, Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING,
+    TYPE_9P,
 };
 
 const QUEUE_SIZE: u16 = 128;
@@ -45,6 +46,8 @@ pub enum P9Error {
     NoWritableDescriptors,
     /// Failed to signal the virio used queue.
     SignalUsedQueue(SysError),
+    /// A DescriptorChain contains invalid data.
+    InvalidDescriptorChain(DescriptorError),
     /// An internal I/O error occurred.
     Internal(io::Error),
 }
@@ -73,6 +76,9 @@ impl Display for P9Error {
             NoReadableDescriptors => write!(f, "request does not have any readable descriptors"),
             NoWritableDescriptors => write!(f, "request does not have any writable descriptors"),
             SignalUsedQueue(err) => write!(f, "failed to signal used queue: {}", err),
+            InvalidDescriptorChain(err) => {
+                write!(f, "DescriptorChain contains invalid data: {}", err)
+            }
             Internal(err) => write!(f, "P9 internal server error: {}", err),
         }
     }
@@ -98,8 +104,10 @@ impl Worker {
 
     fn process_queue(&mut self) -> P9Result<()> {
         while let Some(avail_desc) = self.queue.pop(&self.mem) {
-            let mut reader = Reader::new(&self.mem, avail_desc.clone());
-            let mut writer = Writer::new(&self.mem, avail_desc.clone());
+            let mut reader = Reader::new(&self.mem, avail_desc.clone())
+                .map_err(P9Error::InvalidDescriptorChain)?;
+            let mut writer = Writer::new(&self.mem, avail_desc.clone())
+                .map_err(P9Error::InvalidDescriptorChain)?;
 
             self.server
                 .handle_message(&mut reader, &mut writer)
diff --git a/devices/src/virtio/pmem.rs b/devices/src/virtio/pmem.rs
index 1553ff2..4df295b 100644
--- a/devices/src/virtio/pmem.rs
+++ b/devices/src/virtio/pmem.rs
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use std::fmt::{self, Display};
 use std::fs::File;
+use std::io;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
@@ -14,8 +16,8 @@ use sys_util::{error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken
 use data_model::{DataInit, Le32, Le64};
 
 use super::{
-    copy_config, Queue, Reader, VirtioDevice, Writer, INTERRUPT_STATUS_USED_RING, TYPE_PMEM,
-    VIRTIO_F_VERSION_1,
+    copy_config, DescriptorChain, DescriptorError, Queue, Reader, VirtioDevice, Writer,
+    INTERRUPT_STATUS_USED_RING, TYPE_PMEM, VIRTIO_F_VERSION_1,
 };
 
 const QUEUE_SIZE: u16 = 256;
@@ -53,6 +55,32 @@ struct virtio_pmem_req {
 // Safe because it only has data and has no implicit padding.
 unsafe impl DataInit for virtio_pmem_req {}
 
+#[derive(Debug)]
+enum Error {
+    /// Invalid virtio descriptor chain.
+    Descriptor(DescriptorError),
+    /// Failed to read from virtqueue.
+    ReadQueue(io::Error),
+    /// Failed to write to virtqueue.
+    WriteQueue(io::Error),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            Descriptor(e) => write!(f, "virtio descriptor error: {}", e),
+            ReadQueue(e) => write!(f, "failed to read from virtqueue: {}", e),
+            WriteQueue(e) => write!(f, "failed to write to virtqueue: {}", e),
+        }
+    }
+}
+
+impl ::std::error::Error for Error {}
+
+type Result<T> = ::std::result::Result<T, Error>;
+
 struct Worker {
     queue: Queue,
     memory: GuestMemory,
@@ -79,33 +107,39 @@ impl Worker {
         }
     }
 
+    fn handle_request(&self, avail_desc: DescriptorChain) -> Result<usize> {
+        let mut reader =
+            Reader::new(&self.memory, avail_desc.clone()).map_err(Error::Descriptor)?;
+        let mut writer = Writer::new(&self.memory, avail_desc).map_err(Error::Descriptor)?;
+
+        let status_code = reader
+            .read_obj()
+            .map(|request| self.execute_request(request))
+            .map_err(Error::ReadQueue)?;
+
+        let response = virtio_pmem_resp {
+            status_code: status_code.into(),
+        };
+
+        writer.write_obj(response).map_err(Error::WriteQueue)?;
+
+        Ok(writer.bytes_written())
+    }
+
     fn process_queue(&mut self) -> bool {
         let mut needs_interrupt = false;
         while let Some(avail_desc) = self.queue.pop(&self.memory) {
             let avail_desc_index = avail_desc.index;
-            let mut reader = Reader::new(&self.memory, avail_desc.clone());
-            let mut writer = Writer::new(&self.memory, avail_desc);
 
-            let status_code = match reader.read_obj::<virtio_pmem_req>() {
-                Ok(request) => self.execute_request(request),
+            let bytes_written = match self.handle_request(avail_desc) {
+                Ok(count) => count,
                 Err(e) => {
-                    error!("failed to read virtio_pmem_req: {}", e);
-                    VIRTIO_PMEM_RESP_TYPE_EIO
+                    error!("pmem: unable to handle request: {}", e);
+                    0
                 }
             };
-
-            let response = virtio_pmem_resp {
-                status_code: status_code.into(),
-            };
-            if let Err(e) = writer.write_obj(response) {
-                error!("failed to write virtio_pmem_resp: {}", e);
-            }
-
-            self.queue.add_used(
-                &self.memory,
-                avail_desc_index,
-                writer.bytes_written() as u32,
-            );
+            self.queue
+                .add_used(&self.memory, avail_desc_index, bytes_written as u32);
             needs_interrupt = true;
         }
 
diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs
index 4e6448f..17dca69 100644
--- a/devices/src/virtio/rng.rs
+++ b/devices/src/virtio/rng.rs
@@ -51,15 +51,18 @@ impl Worker {
         let mut needs_interrupt = false;
         while let Some(avail_desc) = queue.pop(&self.mem) {
             let index = avail_desc.index;
-            let mut writer = Writer::new(&self.mem, avail_desc);
-            // Fill the entire descriptor chain buffer with random bytes.
-            let written = match writer.write_from(&self.random_file, std::usize::MAX) {
+            let random_file = &mut self.random_file;
+            let written = match Writer::new(&self.mem, avail_desc)
+                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
+                .and_then(|mut writer| writer.write_from(random_file, std::usize::MAX))
+            {
                 Ok(n) => n,
                 Err(e) => {
                     warn!("Failed to write random data to the guest: {}", e);
                     0
                 }
             };
+
             queue.add_used(&self.mem, index, written as u32);
             needs_interrupt = true;
         }
diff --git a/devices/src/virtio/tpm.rs b/devices/src/virtio/tpm.rs
index a3cdf48..4e557dc 100644
--- a/devices/src/virtio/tpm.rs
+++ b/devices/src/virtio/tpm.rs
@@ -5,6 +5,7 @@
 use std::env;
 use std::fmt::{self, Display};
 use std::fs;
+use std::io::{self, Read, Write};
 use std::ops::BitOrAssign;
 use std::os::unix::io::RawFd;
 use std::path::PathBuf;
@@ -48,16 +49,17 @@ struct Device {
 
 impl Device {
     fn perform_work(&mut self, mem: &GuestMemory, desc: DescriptorChain) -> Result<u32> {
-        let mut reader = Reader::new(mem, desc.clone());
-        let mut writer = Writer::new(mem, desc);
+        let mut reader = Reader::new(mem, desc.clone()).map_err(Error::Descriptor)?;
+        let mut writer = Writer::new(mem, desc).map_err(Error::Descriptor)?;
 
-        if reader.available_bytes() > TPM_BUFSIZE {
+        let available_bytes = reader.available_bytes().map_err(Error::Descriptor)?;
+        if available_bytes > TPM_BUFSIZE {
             return Err(Error::CommandTooLong {
-                size: reader.available_bytes(),
+                size: available_bytes,
             });
         }
 
-        let mut command = vec![0u8; reader.available_bytes() as usize];
+        let mut command = vec![0u8; available_bytes];
         reader.read_exact(&mut command).map_err(Error::Read)?;
 
         let response = self.simulator.execute_command(&command);
@@ -68,9 +70,10 @@ impl Device {
             });
         }
 
-        if response.len() > writer.available_bytes() {
+        let writer_len = writer.available_bytes().map_err(Error::Descriptor)?;
+        if response.len() > writer_len {
             return Err(Error::BufferTooSmall {
-                size: writer.available_bytes(),
+                size: writer_len,
                 required: response.len(),
             });
         }
@@ -287,10 +290,11 @@ type Result<T> = std::result::Result<T, Error>;
 
 enum Error {
     CommandTooLong { size: usize },
-    Read(DescriptorError),
+    Descriptor(DescriptorError),
+    Read(io::Error),
     ResponseTooLong { size: usize },
     BufferTooSmall { size: usize, required: usize },
-    Write(DescriptorError),
+    Write(io::Error),
 }
 
 impl Display for Error {
@@ -303,6 +307,7 @@ impl Display for Error {
                 "vtpm command is too long: {} > {} bytes",
                 size, TPM_BUFSIZE
             ),
+            Descriptor(e) => write!(f, "virtio descriptor error: {}", e),
             Read(e) => write!(f, "vtpm failed to read from guest memory: {}", e),
             ResponseTooLong { size } => write!(
                 f,
diff --git a/sys_util/src/file_traits.rs b/sys_util/src/file_traits.rs
index d35bc4f..f296d9b 100644
--- a/sys_util/src/file_traits.rs
+++ b/sys_util/src/file_traits.rs
@@ -111,7 +111,7 @@ pub trait FileReadWriteVolatile {
     }
 }
 
-impl<'a, T: FileReadWriteVolatile> FileReadWriteVolatile for &'a mut T {
+impl<'a, T: FileReadWriteVolatile + ?Sized> FileReadWriteVolatile for &'a mut T {
     fn read_volatile(&mut self, slice: VolatileSlice) -> Result<usize> {
         (**self).read_volatile(slice)
     }
@@ -208,7 +208,7 @@ pub trait FileReadWriteAtVolatile {
     }
 }
 
-impl<'a, T: FileReadWriteAtVolatile> FileReadWriteAtVolatile for &'a mut T {
+impl<'a, T: FileReadWriteAtVolatile + ?Sized> FileReadWriteAtVolatile for &'a mut T {
     fn read_at_volatile(&mut self, slice: VolatileSlice, offset: u64) -> Result<usize> {
         (**self).read_at_volatile(slice, offset)
     }
diff --git a/sys_util/src/guest_memory.rs b/sys_util/src/guest_memory.rs
index ac7722e..1246a9c 100644
--- a/sys_util/src/guest_memory.rs
+++ b/sys_util/src/guest_memory.rs
@@ -18,6 +18,7 @@ use data_model::DataInit;
 
 #[derive(Debug)]
 pub enum Error {
+    DescriptorChainOverflow,
     InvalidGuestAddress(GuestAddress),
     MemoryAccess(GuestAddress, mmap::Error),
     MemoryMappingFailed(mmap::Error),
@@ -28,6 +29,8 @@ pub enum Error {
     MemoryAddSealsFailed(errno::Error),
     ShortWrite { expected: usize, completed: usize },
     ShortRead { expected: usize, completed: usize },
+    SplitOutOfBounds(usize),
+    VolatileMemoryAccess(VolatileMemoryError),
 }
 pub type Result<T> = result::Result<T, Error>;
 
@@ -38,6 +41,10 @@ impl Display for Error {
         use self::Error::*;
 
         match self {
+            DescriptorChainOverflow => write!(
+                f,
+                "the combined length of all the buffers in a DescriptorChain is too large"
+            ),
             InvalidGuestAddress(addr) => write!(f, "invalid guest address {}", addr),
             MemoryAccess(addr, e) => {
                 write!(f, "invalid guest memory access at addr={}: {}", addr, e)
@@ -64,6 +71,8 @@ impl Display for Error {
                 "incomplete read of {} instead of {} bytes",
                 completed, expected,
             ),
+            SplitOutOfBounds(off) => write!(f, "DescriptorChain split is out of bounds: {}", off),
+            VolatileMemoryAccess(e) => e.fmt(f),
         }
     }
 }