mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
misc: do not use u64 to represent host pointers
To ensure that struct sizes are the same on 32-bit and 64-bit, various kernel APIs use __u64 (Rust u64) to represent userspace pointers. Userspace is expected to cast pointers to __u64 before passing them to the kernel, and cast kernel-provided __u64 to a pointer before using them. However, various safe APIs in Cloud Hypervisor took caller-provided u64 values and passed them to syscalls that interpret them as userspace addresses. Therefore, passing bad u64 values would cause memory disclosure or corruption. Fix the bug by using usize and pointer types as appropriate. To make soundness of the code easier to reason about, the PCI code gains a new MmapRegion abstraction that ensures the validity of pointers. The rest of the code already has an MmapRegion abstraction it can use. To avoid having to reason about whether something is keeping the MmapRegion alive, reference counting is added. MmapRegion cannot hold references to other objects, so the reference counting cannot introduce cycles. Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
This commit is contained in:
committed by
Rob Bradford
parent
fdc19ad85e
commit
42522a88c0
+28
-34
@@ -97,8 +97,10 @@ use vm_device::interrupt::{
|
||||
InterruptIndex, InterruptManager, LegacyIrqGroupConfig, MsiIrqGroupConfig,
|
||||
};
|
||||
use vm_device::{Bus, BusDevice, BusDeviceSync, Resource, UserspaceMapping};
|
||||
#[cfg(feature = "ivshmem")]
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::guest_memory::FileOffset;
|
||||
use vm_memory::{Address, GuestAddress, GuestMemoryRegion, GuestUsize, MmapRegion};
|
||||
use vm_memory::{Address, GuestAddress, GuestMemoryRegion, GuestUsize, MmapRegion, VolatileMemory};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use vm_memory::{GuestAddressSpace, GuestMemory};
|
||||
use vm_migration::protocol::MemoryRangeTable;
|
||||
@@ -817,15 +819,15 @@ impl DeviceRelocation for AddressManager {
|
||||
if let Some(mut shm_regions) = virtio_dev.get_shm_regions()
|
||||
&& shm_regions.addr.raw_value() == old_base
|
||||
{
|
||||
// SAFETY: TODO what are the invariants here?
|
||||
// SAFETY: guaranteed by MmapRegion invariants
|
||||
unsafe {
|
||||
// Remove old mapping
|
||||
self.vm
|
||||
.remove_user_memory_region(
|
||||
shm_regions.mem_slot,
|
||||
old_base,
|
||||
shm_regions.len,
|
||||
shm_regions.host_addr,
|
||||
shm_regions.mapping.len(),
|
||||
shm_regions.mapping.as_ptr(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
@@ -834,17 +836,14 @@ impl DeviceRelocation for AddressManager {
|
||||
"failed to remove user memory region: {e:?}"
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
// SAFETY: TODO what are the invariants here?
|
||||
unsafe {
|
||||
// Create new mapping by inserting new region to KVM.
|
||||
self.vm
|
||||
.create_user_memory_region(
|
||||
shm_regions.mem_slot,
|
||||
new_base,
|
||||
shm_regions.len,
|
||||
shm_regions.host_addr,
|
||||
shm_regions.mapping.len(),
|
||||
shm_regions.mapping.as_ptr(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
@@ -3249,10 +3248,11 @@ impl DeviceManager {
|
||||
},
|
||||
)
|
||||
.map_err(DeviceManagerError::NewMmapRegion)?;
|
||||
let host_addr: u64 = mmap_region.as_ptr() as u64;
|
||||
let host_addr = mmap_region.as_ptr();
|
||||
|
||||
// SAFETY: host_addr points to region_size bytes of mmap-allocated memory.
|
||||
let mem_slot = unsafe {
|
||||
let region_size = region_size.try_into().unwrap();
|
||||
self.memory_manager
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -3261,10 +3261,9 @@ impl DeviceManager {
|
||||
}?;
|
||||
|
||||
let mapping = UserspaceMapping {
|
||||
host_addr,
|
||||
mem_slot,
|
||||
addr: GuestAddress(region_base),
|
||||
len: region_size,
|
||||
mapping: Arc::new(mmap_region),
|
||||
mergeable: false,
|
||||
};
|
||||
|
||||
@@ -3274,7 +3273,6 @@ impl DeviceManager {
|
||||
file,
|
||||
GuestAddress(region_base),
|
||||
mapping,
|
||||
mmap_region,
|
||||
self.force_iommu | pmem_cfg.iommu,
|
||||
self.seccomp_action.clone(),
|
||||
self.exit_evt
|
||||
@@ -4010,16 +4008,13 @@ impl DeviceManager {
|
||||
resources,
|
||||
)?;
|
||||
|
||||
// SAFETY: TODO
|
||||
// Note it is required to call 'add_pci_device()' in advance to have the list of
|
||||
// mmio regions provisioned correctly
|
||||
unsafe {
|
||||
vfio_user_pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map_mmio_regions()
|
||||
.map_err(DeviceManagerError::VfioUserMapRegion)
|
||||
}?;
|
||||
vfio_user_pci_device
|
||||
.lock()
|
||||
.unwrap()
|
||||
.map_mmio_regions()
|
||||
.map_err(DeviceManagerError::VfioUserMapRegion)?;
|
||||
|
||||
let mut node = device_node!(vfio_user_name, vfio_user_pci_device);
|
||||
|
||||
@@ -4747,8 +4742,8 @@ impl DeviceManager {
|
||||
.unwrap()
|
||||
.remove_userspace_mapping(
|
||||
mapping.addr.raw_value(),
|
||||
mapping.len,
|
||||
mapping.host_addr,
|
||||
mapping.mapping.size(),
|
||||
mapping.mapping.as_ptr() as _,
|
||||
mapping.mergeable,
|
||||
mapping.mem_slot,
|
||||
)
|
||||
@@ -5002,13 +4997,12 @@ impl IvshmemOps for IvshmemHandler {
|
||||
start_addr: u64,
|
||||
size: usize,
|
||||
backing_file: Option<PathBuf>,
|
||||
) -> Result<(Arc<GuestRegionMmap>, UserspaceMapping), IvshmemError> {
|
||||
) -> Result<(Arc<MmapRegion<AtomicBitmap>>, UserspaceMapping), IvshmemError> {
|
||||
info!("Creating ivshmem mem region at 0x{start_addr:x}");
|
||||
|
||||
let region: Arc<GuestRegionMmap> = MemoryManager::create_ram_region(
|
||||
let region = MemoryManager::create_ram_region_raw(
|
||||
&backing_file,
|
||||
0,
|
||||
GuestAddress(start_addr),
|
||||
size,
|
||||
false,
|
||||
true,
|
||||
@@ -5021,12 +5015,12 @@ impl IvshmemOps for IvshmemHandler {
|
||||
.map_err(|_| IvshmemError::CreateUserMemoryRegion)?;
|
||||
let mem_slot = {
|
||||
let mut manager = self.memory_manager.lock().unwrap();
|
||||
// SAFETY: guaranteed by GuestRegionMmap invariants
|
||||
// SAFETY: guaranteed by MmapRegion invariants
|
||||
unsafe {
|
||||
manager.create_userspace_mapping(
|
||||
region.start_addr().0,
|
||||
start_addr,
|
||||
region.len(),
|
||||
region.as_ptr() as u64,
|
||||
region.as_ptr(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -5034,11 +5028,11 @@ impl IvshmemOps for IvshmemHandler {
|
||||
}
|
||||
}
|
||||
.map_err(|_| IvshmemError::CreateUserspaceMapping)?;
|
||||
let region = Arc::new(region);
|
||||
let mapping = UserspaceMapping {
|
||||
host_addr: region.as_ptr() as u64,
|
||||
mapping: region.clone(),
|
||||
mem_slot,
|
||||
addr: GuestAddress(region.start_addr().0),
|
||||
len: region.len(),
|
||||
addr: GuestAddress(start_addr),
|
||||
mergeable: false,
|
||||
};
|
||||
Ok((region, mapping))
|
||||
@@ -5050,8 +5044,8 @@ impl IvshmemOps for IvshmemHandler {
|
||||
unsafe {
|
||||
manager.remove_userspace_mapping(
|
||||
mapping.addr.raw_value(),
|
||||
mapping.len,
|
||||
mapping.host_addr,
|
||||
mapping.mapping.len(),
|
||||
mapping.mapping.as_ptr(),
|
||||
mapping.mergeable,
|
||||
mapping.mem_slot,
|
||||
)
|
||||
|
||||
+63
-31
@@ -8,7 +8,7 @@ use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{self};
|
||||
use std::ops::{BitAnd, Deref, Not, Sub};
|
||||
use std::ops::{BitAnd, Not, Sub};
|
||||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
||||
use std::os::fd::AsFd;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
@@ -900,13 +900,12 @@ impl MemoryManager {
|
||||
|
||||
for (zone_id, regions) in list {
|
||||
for (region, virtio_mem) in regions {
|
||||
// SAFETY: regions only holds valid addresses.
|
||||
// TODO: encapsulate this unsafety in a small part of the file.
|
||||
// SAFETY: guaranteed by GuestRegionMmap invariants
|
||||
let slot = unsafe {
|
||||
self.create_userspace_mapping(
|
||||
region.start_addr().raw_value(),
|
||||
region.len(),
|
||||
region.as_ptr() as u64,
|
||||
region.len().try_into().unwrap(),
|
||||
region.as_ptr(),
|
||||
self.mergeable,
|
||||
false,
|
||||
self.log_dirty,
|
||||
@@ -962,13 +961,16 @@ impl MemoryManager {
|
||||
arch::layout::UEFI_START,
|
||||
)
|
||||
.unwrap();
|
||||
const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>());
|
||||
|
||||
// SAFETY: guaranteed by GuestRegionMmap
|
||||
unsafe {
|
||||
self.vm
|
||||
.create_user_memory_region(
|
||||
uefi_mem_slot,
|
||||
uefi_region.start_addr().raw_value(),
|
||||
uefi_region.len(),
|
||||
uefi_region.as_ptr() as u64,
|
||||
uefi_region.len() as usize,
|
||||
uefi_region.as_ptr(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
@@ -1364,10 +1366,9 @@ impl MemoryManager {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_ram_region(
|
||||
pub fn create_ram_region_raw(
|
||||
backing_file: &Option<PathBuf>,
|
||||
file_offset: u64,
|
||||
start_addr: GuestAddress,
|
||||
size: usize,
|
||||
prefault: bool,
|
||||
shared: bool,
|
||||
@@ -1376,7 +1377,7 @@ impl MemoryManager {
|
||||
host_numa_node: Option<u32>,
|
||||
existing_memory_file: Option<File>,
|
||||
thp: bool,
|
||||
) -> Result<Arc<GuestRegionMmap>, Error> {
|
||||
) -> Result<MmapRegion<AtomicBitmap>, Error> {
|
||||
let mut mmap_flags = libc::MAP_NORESERVE;
|
||||
|
||||
// The duplication of mmap_flags ORing here is unfortunate but it also makes
|
||||
@@ -1403,17 +1404,13 @@ impl MemoryManager {
|
||||
None
|
||||
};
|
||||
|
||||
let region = GuestRegionMmap::new(
|
||||
MmapRegion::build(fo, size, libc::PROT_READ | libc::PROT_WRITE, mmap_flags)
|
||||
.map_err(Error::GuestMemoryRegion)?,
|
||||
start_addr,
|
||||
)
|
||||
.map_err(Error::GuestMemory)?;
|
||||
let region = MmapRegion::build(fo, size, libc::PROT_READ | libc::PROT_WRITE, mmap_flags)
|
||||
.map_err(Error::GuestMemoryRegion)?;
|
||||
|
||||
// Apply NUMA policy if needed.
|
||||
if let Some(node) = host_numa_node {
|
||||
let addr = region.deref().as_ptr();
|
||||
let len = region.deref().size() as u64;
|
||||
let addr = region.as_ptr();
|
||||
let len = region.size() as u64;
|
||||
let mode = MPOL_BIND;
|
||||
let mut nodemask: Vec<u64> = Vec::new();
|
||||
let flags = MPOL_MF_STRICT | MPOL_MF_MOVE;
|
||||
@@ -1498,7 +1495,39 @@ impl MemoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Arc::new(region))
|
||||
Ok(region)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_ram_region(
|
||||
backing_file: &Option<PathBuf>,
|
||||
file_offset: u64,
|
||||
start_addr: GuestAddress,
|
||||
size: usize,
|
||||
prefault: bool,
|
||||
shared: bool,
|
||||
hugepages: bool,
|
||||
hugepage_size: Option<u64>,
|
||||
host_numa_node: Option<u32>,
|
||||
existing_memory_file: Option<File>,
|
||||
thp: bool,
|
||||
) -> Result<Arc<GuestRegionMmap>, Error> {
|
||||
let r = Self::create_ram_region_raw(
|
||||
backing_file,
|
||||
file_offset,
|
||||
size,
|
||||
prefault,
|
||||
shared,
|
||||
hugepages,
|
||||
hugepage_size,
|
||||
host_numa_node,
|
||||
existing_memory_file,
|
||||
thp,
|
||||
)?;
|
||||
|
||||
Ok(Arc::new(
|
||||
GuestRegionMmap::new(r, start_addr).map_err(Error::GuestMemory)?,
|
||||
))
|
||||
}
|
||||
|
||||
// Duplicate of `memory_zone_get_align_size` that does not require a `zone`
|
||||
@@ -1612,12 +1641,12 @@ impl MemoryManager {
|
||||
)?;
|
||||
|
||||
// Map it into the guest
|
||||
// SAFETY: create_ram_region only produces valid mappings.
|
||||
// SAFETY: guaranteed by GuestMmapRegion invariants
|
||||
let slot = unsafe {
|
||||
self.create_userspace_mapping(
|
||||
region.start_addr().0,
|
||||
region.len(),
|
||||
region.as_ptr() as u64,
|
||||
region.len().try_into().unwrap(),
|
||||
region.as_ptr(),
|
||||
self.mergeable,
|
||||
false,
|
||||
self.log_dirty,
|
||||
@@ -1722,8 +1751,8 @@ impl MemoryManager {
|
||||
pub unsafe fn create_userspace_mapping(
|
||||
&mut self,
|
||||
guest_phys_addr: u64,
|
||||
memory_size: u64,
|
||||
userspace_addr: u64,
|
||||
memory_size: usize,
|
||||
userspace_addr: *mut u8,
|
||||
mergeable: bool,
|
||||
readonly: bool,
|
||||
log_dirty: bool,
|
||||
@@ -1731,10 +1760,11 @@ impl MemoryManager {
|
||||
let slot = self.allocate_memory_slot();
|
||||
|
||||
info!(
|
||||
"Creating userspace mapping: {guest_phys_addr:x} -> {userspace_addr:x} {memory_size:x}, slot {slot}"
|
||||
"Creating userspace mapping: {guest_phys_addr:x} -> {userspace_addr_:x} {memory_size:x}, slot {slot}",
|
||||
userspace_addr_ = userspace_addr as u64
|
||||
);
|
||||
|
||||
// SAFETY: promised by caller
|
||||
// SAFETY: caller promises parameters are correct.
|
||||
unsafe {
|
||||
self.vm
|
||||
.create_user_memory_region(
|
||||
@@ -1788,7 +1818,8 @@ impl MemoryManager {
|
||||
}
|
||||
|
||||
info!(
|
||||
"Created userspace mapping: {guest_phys_addr:x} -> {userspace_addr:x} {memory_size:x}"
|
||||
"Created userspace mapping: {guest_phys_addr:x} -> {userspace_addr_:x} {memory_size:x}",
|
||||
userspace_addr_ = userspace_addr as u64
|
||||
);
|
||||
|
||||
Ok(slot)
|
||||
@@ -1806,12 +1837,12 @@ impl MemoryManager {
|
||||
pub unsafe fn remove_userspace_mapping(
|
||||
&mut self,
|
||||
guest_phys_addr: u64,
|
||||
memory_size: u64,
|
||||
userspace_addr: u64,
|
||||
memory_size: usize,
|
||||
userspace_addr: *mut u8,
|
||||
mergeable: bool,
|
||||
slot: u32,
|
||||
) -> Result<(), Error> {
|
||||
// SAFETY: The caller promises that the parameters are correct.
|
||||
// SAFETY: Caller promises parameters are correct.
|
||||
unsafe {
|
||||
self.vm
|
||||
.remove_user_memory_region(
|
||||
@@ -1852,7 +1883,8 @@ impl MemoryManager {
|
||||
}
|
||||
|
||||
info!(
|
||||
"Removed userspace mapping: {guest_phys_addr:x} -> {userspace_addr:x} {memory_size:x}"
|
||||
"Removed userspace mapping: {guest_phys_addr:x} -> {userspace_addr_:x} {memory_size:x}",
|
||||
userspace_addr_ = userspace_addr as u64
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
+4
-4
@@ -3469,8 +3469,8 @@ mod unit_tests {
|
||||
vm.create_user_memory_region(
|
||||
index as u32,
|
||||
region.start_addr().raw_value(),
|
||||
region.len(),
|
||||
region.as_ptr() as u64,
|
||||
region.len().try_into().unwrap(),
|
||||
region.as_ptr(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
@@ -3607,8 +3607,8 @@ pub fn test_vm() {
|
||||
vm.create_user_memory_region(
|
||||
index as u32,
|
||||
region.start_addr().raw_value(),
|
||||
region.len(),
|
||||
region.as_ptr() as u64,
|
||||
region.len().try_into().unwrap(),
|
||||
region.as_ptr() as _,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user