mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-12-01 04:28:48 +00:00
virtqueue: DescriptorChain::checked_new returns anyhow::Error
checked_new fails when guest gives invalid descriptor entries. The error makes debugging easier. BUG=None TEST=cargo test Change-Id: I6c6f5b04e7f39a685d95ca738b9cb01be643fa21 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3418620 Reviewed-by: David Stevens <stevensd@chromium.org> Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Woody Chow <woodychow@google.com>
This commit is contained in:
parent
d43ae3c13c
commit
8b2c7b995b
2 changed files with 34 additions and 17 deletions
|
@ -12,6 +12,7 @@ use std::ptr::copy_nonoverlapping;
|
|||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use base::{FileReadWriteAtVolatile, FileReadWriteVolatile};
|
||||
use cros_async::MemRegion;
|
||||
use data_model::{DataInit, Le16, Le32, Le64, VolatileMemoryError, VolatileSlice};
|
||||
|
@ -30,8 +31,8 @@ pub enum Error {
|
|||
DescriptorChainOverflow,
|
||||
#[error("descriptor guest memory error: {0}")]
|
||||
GuestMemoryError(vm_memory::GuestMemoryError),
|
||||
#[error("invalid descriptor chain")]
|
||||
InvalidChain,
|
||||
#[error("invalid descriptor chain: {0:#}")]
|
||||
InvalidChain(anyhow::Error),
|
||||
#[error("descriptor I/O error: {0}")]
|
||||
IoError(io::Error),
|
||||
#[error("`DescriptorChain` split is out of bounds: {0}")]
|
||||
|
@ -802,18 +803,20 @@ pub fn create_descriptor_chain(
|
|||
let offset = size + spaces_between_regions;
|
||||
buffers_start_addr = buffers_start_addr
|
||||
.checked_add(offset as u64)
|
||||
.ok_or(Error::InvalidChain)?;
|
||||
.context("Invalid buffers_start_addr)")
|
||||
.map_err(Error::InvalidChain)?;
|
||||
|
||||
let _ = memory.write_obj_at_addr(
|
||||
desc,
|
||||
descriptor_array_addr
|
||||
.checked_add(index as u64 * std::mem::size_of::<virtq_desc>() as u64)
|
||||
.ok_or(Error::InvalidChain)?,
|
||||
.context("Invalid descriptor_array_addr")
|
||||
.map_err(Error::InvalidChain)?,
|
||||
);
|
||||
}
|
||||
|
||||
DescriptorChain::checked_new(memory, descriptor_array_addr, 0x100, 0, 0)
|
||||
.ok_or(Error::InvalidChain)
|
||||
.map_err(Error::InvalidChain)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::cmp::min;
|
|||
use std::num::Wrapping;
|
||||
use std::sync::atomic::{fence, Ordering};
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use base::error;
|
||||
use cros_async::{AsyncError, EventAsync};
|
||||
use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||
|
@ -85,15 +86,18 @@ impl DescriptorChain {
|
|||
queue_size: u16,
|
||||
index: u16,
|
||||
required_flags: u16,
|
||||
) -> Option<DescriptorChain> {
|
||||
) -> anyhow::Result<DescriptorChain> {
|
||||
if index >= queue_size {
|
||||
return None;
|
||||
bail!("index ({}) >= queue_size ({})", index, queue_size);
|
||||
}
|
||||
|
||||
let desc_head = mem.checked_offset(desc_table, (index as u64) * 16)?;
|
||||
let desc_head = mem
|
||||
.checked_offset(desc_table, (index as u64) * 16)
|
||||
.context("desc_table checked_offset failed")?;
|
||||
// These reads can't fail unless Guest memory is hopelessly broken.
|
||||
let addr = GuestAddress(mem.read_obj_from_addr::<u64>(desc_head).unwrap() as u64);
|
||||
mem.checked_offset(desc_head, 16)?;
|
||||
mem.checked_offset(desc_head, 16)
|
||||
.context("desc_head checked_offset failed")?;
|
||||
let len: u32 = mem.read_obj_from_addr(desc_head.unchecked_add(8)).unwrap();
|
||||
let flags: u16 = mem.read_obj_from_addr(desc_head.unchecked_add(12)).unwrap();
|
||||
let next: u16 = mem.read_obj_from_addr(desc_head.unchecked_add(14)).unwrap();
|
||||
|
@ -110,9 +114,9 @@ impl DescriptorChain {
|
|||
};
|
||||
|
||||
if chain.is_valid() && chain.flags & required_flags == required_flags {
|
||||
Some(chain)
|
||||
Ok(chain)
|
||||
} else {
|
||||
None
|
||||
bail!("chain is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,17 +165,22 @@ impl DescriptorChain {
|
|||
if self.has_next() {
|
||||
// Once we see a write-only descriptor, all subsequent descriptors must be write-only.
|
||||
let required_flags = self.flags & VIRTQ_DESC_F_WRITE;
|
||||
DescriptorChain::checked_new(
|
||||
match DescriptorChain::checked_new(
|
||||
&self.mem,
|
||||
self.desc_table,
|
||||
self.queue_size,
|
||||
self.next,
|
||||
required_flags,
|
||||
)
|
||||
.map(|mut c| {
|
||||
c.ttl = self.ttl - 1;
|
||||
c
|
||||
})
|
||||
) {
|
||||
Ok(mut c) => {
|
||||
c.ttl = self.ttl - 1;
|
||||
Some(c)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{:#}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -431,6 +440,11 @@ impl Queue {
|
|||
let descriptor_index: u16 = mem.read_obj_from_addr(desc_idx_addr).unwrap();
|
||||
|
||||
DescriptorChain::checked_new(mem, self.desc_table, queue_size, descriptor_index, 0)
|
||||
.map_err(|e| {
|
||||
error!("{:#}", e);
|
||||
e
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Remove the first available descriptor chain from the queue.
|
||||
|
|
Loading…
Reference in a new issue