mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vsock: vhost_user: vfio: Fix potential host memory overflow
The vsock packets that we're building are resolving guest addresses to host ones and use the latter as raw pointers. If the corresponding guest mapped buffer spans across several regions in the guest, they will do so in the host as well. Since we have no guarantees that host regions are contiguous, it may lead the VMM into trying to access memory outside of its memory space. For now we fix that by ensuring that the guest buffers do not span across several regions. If they do, we error out. Ideally, we should enhance the rust-vmm memory model to support safe acces across host regions. Fixes CVE-2019-18960 Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
committed by
Sebastien Boeuf
parent
1e97d1413e
commit
664431ff14
@@ -63,7 +63,7 @@ impl<'a> Iterator for DescIter<'a> {
|
||||
/// A virtio descriptor constraints with C representive.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
struct Descriptor {
|
||||
pub struct Descriptor {
|
||||
addr: u64,
|
||||
len: u32,
|
||||
flags: u16,
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
|
||||
use libc;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use std::convert::TryInto;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::queue::Descriptor;
|
||||
|
||||
use vm_device::get_host_address_range;
|
||||
use vm_memory::{Address, Error as MmapError, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
@@ -54,6 +58,8 @@ pub fn setup_vhost_user_vring(
|
||||
let mut vu_interrupt_list = Vec::new();
|
||||
|
||||
for (queue_index, queue) in queues.into_iter().enumerate() {
|
||||
let actual_size: usize = queue.actual_size().try_into().unwrap();
|
||||
|
||||
vu.set_vring_num(queue_index, queue.actual_size())
|
||||
.map_err(Error::VhostUserSetVringNum)?;
|
||||
|
||||
@@ -61,14 +67,19 @@ pub fn setup_vhost_user_vring(
|
||||
queue_max_size: queue.get_max_size(),
|
||||
queue_size: queue.actual_size(),
|
||||
flags: 0u32,
|
||||
desc_table_addr: mem
|
||||
.get_host_address(queue.desc_table)
|
||||
.ok_or_else(|| Error::DescriptorTableAddress)? as u64,
|
||||
used_ring_addr: mem
|
||||
.get_host_address(queue.used_ring)
|
||||
desc_table_addr: get_host_address_range(
|
||||
&mem,
|
||||
queue.desc_table,
|
||||
actual_size * std::mem::size_of::<Descriptor>(),
|
||||
)
|
||||
.ok_or_else(|| Error::DescriptorTableAddress)? as u64,
|
||||
// The used ring is {flags: u16; idx: u16; virtq_used_elem [{id: u16, len: u16}; actual_size]},
|
||||
// i.e. 4 + (4 + 4) * actual_size.
|
||||
used_ring_addr: get_host_address_range(&mem, queue.used_ring, 4 + actual_size * 8)
|
||||
.ok_or_else(|| Error::UsedAddress)? as u64,
|
||||
avail_ring_addr: mem
|
||||
.get_host_address(queue.avail_ring)
|
||||
// The used ring is {flags: u16; idx: u16; elem [u16; actual_size]},
|
||||
// i.e. 4 + (2) * actual_size.
|
||||
avail_ring_addr: get_host_address_range(&mem, queue.avail_ring, 4 + actual_size * 2)
|
||||
.ok_or_else(|| Error::AvailAddress)? as u64,
|
||||
log_addr: None,
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ use byteorder::{ByteOrder, LittleEndian};
|
||||
use super::super::DescriptorChain;
|
||||
use super::defs;
|
||||
use super::{Result, VsockError};
|
||||
use vm_device::get_host_address_range;
|
||||
|
||||
// The vsock packet header is defined by the C struct:
|
||||
//
|
||||
@@ -116,9 +117,7 @@ impl VsockPacket {
|
||||
}
|
||||
|
||||
let mut pkt = Self {
|
||||
hdr: head
|
||||
.mem
|
||||
.get_host_address(head.addr)
|
||||
hdr: get_host_address_range(&head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||||
buf: None,
|
||||
buf_size: 0,
|
||||
@@ -151,9 +150,7 @@ impl VsockPacket {
|
||||
|
||||
pkt.buf_size = buf_desc.len as usize;
|
||||
pkt.buf = Some(
|
||||
buf_desc
|
||||
.mem
|
||||
.get_host_address(buf_desc.addr)
|
||||
get_host_address_range(&buf_desc.mem, buf_desc.addr, pkt.buf_size)
|
||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||||
);
|
||||
|
||||
@@ -182,19 +179,16 @@ impl VsockPacket {
|
||||
return Err(VsockError::BufDescMissing);
|
||||
}
|
||||
let buf_desc = head.next_descriptor().ok_or(VsockError::BufDescMissing)?;
|
||||
let buf_size = buf_desc.len as usize;
|
||||
|
||||
Ok(Self {
|
||||
hdr: head
|
||||
.mem
|
||||
.get_host_address(head.addr)
|
||||
hdr: get_host_address_range(&head.mem, head.addr, VSOCK_PKT_HDR_SIZE)
|
||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||||
buf: Some(
|
||||
buf_desc
|
||||
.mem
|
||||
.get_host_address(buf_desc.addr)
|
||||
get_host_address_range(&buf_desc.mem, buf_desc.addr, buf_size)
|
||||
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||||
),
|
||||
buf_size: buf_desc.len as usize,
|
||||
buf_size,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -383,7 +377,8 @@ mod tests {
|
||||
|
||||
fn set_pkt_len(len: u32, guest_desc: &GuestQDesc, mem: &GuestMemoryMmap) {
|
||||
let hdr_gpa = guest_desc.addr.get();
|
||||
let hdr_ptr = mem.get_host_address(GuestAddress(hdr_gpa)).unwrap() as *mut u8;
|
||||
let hdr_ptr = get_host_address_range(&mem, GuestAddress(hdr_gpa), VSOCK_PKT_HDR_SIZE)
|
||||
.unwrap() as *mut u8;
|
||||
let len_ptr = unsafe { hdr_ptr.add(HDROFF_LEN) };
|
||||
|
||||
LittleEndian::write_u32(unsafe { std::slice::from_raw_parts_mut(len_ptr, 4) }, len);
|
||||
|
||||
Reference in New Issue
Block a user