mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
The address that is passed from the guest should be treated as
untrusted. Currently an invalid address will panic the VMM. This only
allows the guest to hurt itself, but we shouldn't have the VMM crashing.
Instead let's return an error if possible or invalidate the queue if it
happen during setup.
The data flow from guest to translate_gva/translate_gpa is:
1. Guest writes a raw u64 address into a virtio descriptor in the
shared descriptor table (guest memory).
2. The virtio-queue crate reads this descriptor via read_obj() and
returns the addr field as-is in a GuestAddress — no validation.
3. Device code calls .translate_gva(access_platform, len) on the
GuestAddress.
4. With IOMMU (access_platform is Some): the address is an IOVA that
must be translated to a GPA via the IOMMU mapping table. If the
guest provides an unmapped IOVA, translation returns Err.
Previously, .unwrap() here panicked the VMM.
5. Without IOMMU (access_platform is None): translate_gva is a no-op
(returns self). The raw address flows to GuestMemory::read_obj()
which validates it — out-of-range addresses return
Err(InvalidGuestAddress), so no host memory corruption is possible.
Signed-off-by: Dylan Reid <dgreid@fb.com>
180 lines
5.4 KiB
Rust
180 lines
5.4 KiB
Rust
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
//
|
|
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE-BSD-3-Clause file.
|
|
//
|
|
// Copyright © 2019 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
//! Implements virtio queues
|
|
|
|
use std::fmt::{self, Debug};
|
|
|
|
use virtio_queue::{Queue, QueueT};
|
|
use vm_memory::GuestAddress;
|
|
|
|
pub mod queue;
|
|
pub use queue::*;
|
|
|
|
pub const VIRTIO_MSI_NO_VECTOR: u16 = 0xffff;
|
|
|
|
// Types taken from linux/virtio_ids.h
|
|
#[derive(Copy, Clone, Debug)]
|
|
#[allow(dead_code)]
|
|
#[allow(non_camel_case_types)]
|
|
#[repr(C)]
|
|
pub enum VirtioDeviceType {
|
|
Net = 1,
|
|
Block = 2,
|
|
Console = 3,
|
|
Rng = 4,
|
|
Balloon = 5,
|
|
Fs9P = 9,
|
|
Gpu = 16,
|
|
Input = 18,
|
|
Vsock = 19,
|
|
Iommu = 23,
|
|
Mem = 24,
|
|
Fs = 26,
|
|
Pmem = 27,
|
|
Watchdog = 35,
|
|
Unknown = 0xFF,
|
|
}
|
|
|
|
impl From<u32> for VirtioDeviceType {
|
|
fn from(t: u32) -> Self {
|
|
match t {
|
|
1 => VirtioDeviceType::Net,
|
|
2 => VirtioDeviceType::Block,
|
|
3 => VirtioDeviceType::Console,
|
|
4 => VirtioDeviceType::Rng,
|
|
5 => VirtioDeviceType::Balloon,
|
|
9 => VirtioDeviceType::Fs9P,
|
|
16 => VirtioDeviceType::Gpu,
|
|
18 => VirtioDeviceType::Input,
|
|
19 => VirtioDeviceType::Vsock,
|
|
23 => VirtioDeviceType::Iommu,
|
|
24 => VirtioDeviceType::Mem,
|
|
26 => VirtioDeviceType::Fs,
|
|
27 => VirtioDeviceType::Pmem,
|
|
35 => VirtioDeviceType::Watchdog,
|
|
_ => VirtioDeviceType::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
// In order to use the `{}` marker, the trait `fmt::Display` must be implemented
|
|
// manually for the type VirtioDeviceType.
|
|
impl fmt::Display for VirtioDeviceType {
|
|
// This trait requires `fmt` with this exact signature.
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let output = match *self {
|
|
VirtioDeviceType::Net => "net",
|
|
VirtioDeviceType::Block => "block",
|
|
VirtioDeviceType::Console => "console",
|
|
VirtioDeviceType::Rng => "rng",
|
|
VirtioDeviceType::Balloon => "balloon",
|
|
VirtioDeviceType::Gpu => "gpu",
|
|
VirtioDeviceType::Fs9P => "9p",
|
|
VirtioDeviceType::Input => "input",
|
|
VirtioDeviceType::Vsock => "vsock",
|
|
VirtioDeviceType::Iommu => "iommu",
|
|
VirtioDeviceType::Mem => "mem",
|
|
VirtioDeviceType::Fs => "fs",
|
|
VirtioDeviceType::Pmem => "pmem",
|
|
VirtioDeviceType::Watchdog => "watchdog",
|
|
VirtioDeviceType::Unknown => "UNKNOWN",
|
|
};
|
|
write!(f, "{output}")
|
|
}
|
|
}
|
|
|
|
/// Trait for devices with access to data in memory being limited and/or
|
|
/// translated.
|
|
pub trait AccessPlatform: Send + Sync + Debug {
|
|
/// Provide a way to translate GVA address ranges into GPAs.
|
|
fn translate_gva(&self, base: u64, size: u64) -> std::result::Result<u64, std::io::Error>;
|
|
/// Provide a way to translate GPA address ranges into GVAs.
|
|
fn translate_gpa(&self, base: u64, size: u64) -> std::result::Result<u64, std::io::Error>;
|
|
}
|
|
|
|
pub trait Translatable {
|
|
fn translate_gva(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error>
|
|
where
|
|
Self: Sized;
|
|
fn translate_gpa(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error>
|
|
where
|
|
Self: Sized;
|
|
}
|
|
|
|
impl Translatable for GuestAddress {
|
|
fn translate_gva(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error> {
|
|
Ok(GuestAddress(self.0.translate_gva(access_platform, len)?))
|
|
}
|
|
fn translate_gpa(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error> {
|
|
Ok(GuestAddress(self.0.translate_gpa(access_platform, len)?))
|
|
}
|
|
}
|
|
|
|
impl Translatable for u64 {
|
|
fn translate_gva(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error> {
|
|
if let Some(access_platform) = access_platform {
|
|
access_platform.translate_gva(*self, len as u64)
|
|
} else {
|
|
Ok(*self)
|
|
}
|
|
}
|
|
fn translate_gpa(
|
|
&self,
|
|
access_platform: Option<&dyn AccessPlatform>,
|
|
len: usize,
|
|
) -> std::result::Result<Self, std::io::Error> {
|
|
if let Some(access_platform) = access_platform {
|
|
access_platform.translate_gpa(*self, len as u64)
|
|
} else {
|
|
Ok(*self)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Helper for cloning a Queue since QueueState doesn't derive Clone
|
|
pub fn clone_queue(queue: &Queue) -> Queue {
|
|
let mut q = Queue::new(queue.max_size()).unwrap();
|
|
|
|
q.set_next_avail(queue.next_avail());
|
|
q.set_next_used(queue.next_used());
|
|
q.set_event_idx(queue.event_idx_enabled());
|
|
q.set_size(queue.size());
|
|
q.set_ready(queue.ready());
|
|
q.try_set_desc_table_address(GuestAddress(queue.desc_table()))
|
|
.unwrap();
|
|
q.try_set_avail_ring_address(GuestAddress(queue.avail_ring()))
|
|
.unwrap();
|
|
q.try_set_used_ring_address(GuestAddress(queue.used_ring()))
|
|
.unwrap();
|
|
|
|
q
|
|
}
|