mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: Avoid raw iovecs in VHDX sync I/O
The VHDX synchronous async I/O backend still converted owned AsyncIoOperation targets back into raw iovec slices before calling the VHDX read and write helpers. That kept pointer dereferences in the owned path and allowed a backend mistake to violate the safety boundary. Handle owned VHDX reads and writes through AsyncIoOperation copy helpers instead. The VHDX file operations still run synchronously, but data is copied through operation-owned buffers or guest-memory targets without reconstructing Rust slices from raw iovec pointers. Assisted-by: Codex:GPT-5 Signed-off-by: Dylan Reid <dgreid@fb.com>
This commit is contained in:
+18
-63
@@ -6,11 +6,10 @@
|
|||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
@@ -18,7 +17,6 @@ use crate::async_io::{
|
|||||||
DiskFileError,
|
DiskFileError,
|
||||||
};
|
};
|
||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||||
use crate::request::DEFAULT_DESCRIPTOR_VEC_SIZE;
|
|
||||||
use crate::vhdx::{Vhdx, VhdxError};
|
use crate::vhdx::{Vhdx, VhdxError};
|
||||||
use crate::{BlockBackend, Error, disk_file};
|
use crate::{BlockBackend, Error, disk_file};
|
||||||
|
|
||||||
@@ -124,64 +122,30 @@ impl VhdxSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: each iovec must describe writable memory that remains valid for
|
fn read_operation(&mut self, op: &mut AsyncIoOperation) -> AsyncIoResult<usize> {
|
||||||
// this synchronous call. The caller must also ensure that creating a
|
let offset = op.offset();
|
||||||
// mutable slice from each iovec does not violate Rust aliasing rules.
|
let mut buf = vec![0u8; op.total_len()];
|
||||||
unsafe fn read_iovecs(
|
|
||||||
&mut self,
|
|
||||||
offset: libc::off_t,
|
|
||||||
iovecs: &[libc::iovec],
|
|
||||||
) -> AsyncIoResult<usize> {
|
|
||||||
let mut slices: SmallVec<[IoSliceMut; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
|
||||||
SmallVec::with_capacity(iovecs.len());
|
|
||||||
for iovec in iovecs.iter() {
|
|
||||||
if iovec.iov_len == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// SAFETY: Guaranteed by read_iovecs' caller.
|
|
||||||
let slice = unsafe {
|
|
||||||
std::slice::from_raw_parts_mut(iovec.iov_base.cast::<u8>(), iovec.iov_len)
|
|
||||||
};
|
|
||||||
slices.push(IoSliceMut::new(slice));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vhdx = self.vhdx_file.lock().unwrap();
|
let mut vhdx = self.vhdx_file.lock().unwrap();
|
||||||
vhdx.seek(SeekFrom::Start(offset as u64))
|
vhdx.seek(SeekFrom::Start(offset as u64))
|
||||||
.map_err(AsyncIoError::ReadVectored)?;
|
.map_err(AsyncIoError::ReadVectored)?;
|
||||||
let mut result = 0usize;
|
let result = vhdx.read(&mut buf).map_err(AsyncIoError::ReadVectored)?;
|
||||||
for slice in slices.iter_mut() {
|
drop(vhdx);
|
||||||
result += vhdx.read(slice).map_err(AsyncIoError::ReadVectored)?;
|
|
||||||
}
|
op.write_bytes_at(0, &buf[..result])
|
||||||
|
.map_err(AsyncIoError::ReadVectored)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: each iovec must describe readable memory that remains valid for
|
fn write_operation(&mut self, op: &AsyncIoOperation) -> AsyncIoResult<usize> {
|
||||||
// this synchronous call. The caller must also ensure that creating a shared
|
let offset = op.offset();
|
||||||
// slice from each iovec does not violate Rust aliasing rules.
|
let mut buf = vec![0u8; op.total_len()];
|
||||||
unsafe fn write_iovecs(
|
op.read_bytes_at(0, &mut buf)
|
||||||
&mut self,
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
offset: libc::off_t,
|
|
||||||
iovecs: &[libc::iovec],
|
|
||||||
) -> AsyncIoResult<usize> {
|
|
||||||
let mut slices: SmallVec<[IoSlice; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
|
||||||
SmallVec::with_capacity(iovecs.len());
|
|
||||||
for iovec in iovecs.iter() {
|
|
||||||
if iovec.iov_len == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// SAFETY: Guaranteed by write_iovecs' caller.
|
|
||||||
let slice =
|
|
||||||
unsafe { std::slice::from_raw_parts(iovec.iov_base.cast::<u8>(), iovec.iov_len) };
|
|
||||||
slices.push(IoSlice::new(slice));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vhdx = self.vhdx_file.lock().unwrap();
|
let mut vhdx = self.vhdx_file.lock().unwrap();
|
||||||
vhdx.seek(SeekFrom::Start(offset as u64))
|
vhdx.seek(SeekFrom::Start(offset as u64))
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
let mut result = 0usize;
|
let result = vhdx.write(&buf).map_err(AsyncIoError::WriteVectored)?;
|
||||||
for slice in slices.iter() {
|
|
||||||
result += vhdx.write(slice).map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
}
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,21 +156,12 @@ impl AsyncIo for VhdxSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submit_data_operation(&mut self, op: AsyncIoOperation) -> AsyncIoResult<()> {
|
fn submit_data_operation(&mut self, op: AsyncIoOperation) -> AsyncIoResult<()> {
|
||||||
let offset = op.offset();
|
|
||||||
let is_read = op.is_read();
|
let is_read = op.is_read();
|
||||||
let iovecs = op.iovecs();
|
let mut op = op;
|
||||||
let result = if is_read {
|
let result = if is_read {
|
||||||
// SAFETY: AsyncIoOperation keeps the iovec target alive for this
|
self.read_operation(&mut op)?
|
||||||
// synchronous call. Host-memory operations also satisfy the
|
|
||||||
// aliasing requirement above; guest-memory-backed iovecs remain a
|
|
||||||
// temporary unsound VHDX case deferred to a later fix.
|
|
||||||
unsafe { self.read_iovecs(offset, iovecs)? }
|
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: AsyncIoOperation keeps the iovec target alive for this
|
self.write_operation(&op)?
|
||||||
// synchronous call. Host-memory operations also satisfy the
|
|
||||||
// aliasing requirement above; guest-memory-backed iovecs remain a
|
|
||||||
// temporary unsound VHDX case deferred to a later fix.
|
|
||||||
unsafe { self.write_iovecs(offset, iovecs)? }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.completion_list
|
self.completion_list
|
||||||
|
|||||||
Reference in New Issue
Block a user