Skip to content

Commit 7b16aeb

Browse files
roypatandreeaflorescu
authored andcommitted
Remove the AsSlice trait
Taking rust-style references to guest memory of a running virtual machine might violate the aliasing rules, as the guest can modify its memory at will. Additionally, since the `read_from` and `write_to` functions "leak" references to guest memory to code outside of the vm-memory crate, it is possible to trigger undefined behavior in single-threaded, safe code, for example by creating a `Read` implementation the reads from a volatile slice: struct VolatileSliceReader<'a> { src: VolatileSlice<'a, ()> } impl<'a> Read for VolatileSliceReader<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { Ok(self.src.copy_to(buf)) } } \#[test] // test fails under miri fn undefined_behavior() { let mut data = vec![0u8; 20]; let vslice = unsafe {VolatileSlice::new(data.as_mut_ptr(), 20)}; let mut reader = VolatileSliceReader {src: vslice}; vslice.read_from(0, &mut reader, 10); } Since this trait was marked `pub(crate)`, this is not a breaking change. As a consequence, the `read_from` and `write_to` functions of `Bytes` implementations now always first copy to a buffer allocated within the function, before transferign data from/to the Read/Write object provided. There is no safe way to keep the existing abstraction, as Rust does not give us a low-level interface to Read/Write that operates with pointers. Signed-off-by: Patrick Roy <[email protected]>
1 parent aefa2ed commit 7b16aeb

File tree

4 files changed

+30
-117
lines changed

4 files changed

+30
-117
lines changed

src/guest_memory.rs

Lines changed: 28 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
250250
/// # Safety
251251
///
252252
/// Unsafe because of possible aliasing.
253+
#[deprecated = "It is impossible to use this function for accessing memory of a running virtual \
254+
machine without violating aliasing rules "]
253255
unsafe fn as_slice(&self) -> Option<&[u8]> {
254256
None
255257
}
@@ -263,6 +265,8 @@ pub trait GuestMemoryRegion: Bytes<MemoryRegionAddress, E = Error> {
263265
/// Unsafe because of possible aliasing. Mutable accesses performed through the
264266
/// returned slice are not visible to the dirty bitmap tracking functionality of
265267
/// the region, and must be manually recorded using the associated bitmap object.
268+
#[deprecated = "It is impossible to use this function for accessing memory of a running virtual \
269+
machine without violating aliasing rules "]
266270
unsafe fn as_mut_slice(&self) -> Option<&mut [u8]> {
267271
None
268272
}
@@ -848,38 +852,22 @@ impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T {
848852
self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
849853
// Check if something bad happened before doing unsafe things.
850854
assert!(offset <= count);
851-
// SAFETY: Safe because we are checking the offset.
852-
if let Some(dst) = unsafe { region.as_mut_slice() } {
853-
// This is safe cause `start` and `len` are within the `region`, and we manually
854-
// record the dirty status of the written range below.
855-
let start = caddr.raw_value() as usize;
856-
let end = start + len;
857-
let bytes_read = loop {
858-
match src.read(&mut dst[start..end]) {
859-
Ok(n) => break n,
860-
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
861-
Err(e) => return Err(Error::IOError(e)),
862-
}
863-
};
864-
865-
region.bitmap().mark_dirty(start, bytes_read);
866-
Ok(bytes_read)
867-
} else {
868-
let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
869-
let mut buf = vec![0u8; len].into_boxed_slice();
870-
loop {
871-
match src.read(&mut buf[..]) {
872-
Ok(bytes_read) => {
873-
// We don't need to update the dirty bitmap manually here because it's
874-
// expected to be handled by the logic within the `Bytes`
875-
// implementation for the region object.
876-
let bytes_written = region.write(&buf[0..bytes_read], caddr)?;
877-
assert_eq!(bytes_written, bytes_read);
878-
break Ok(bytes_read);
879-
}
880-
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
881-
Err(e) => break Err(Error::IOError(e)),
855+
856+
let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
857+
let mut buf = vec![0u8; len].into_boxed_slice();
858+
859+
loop {
860+
match src.read(&mut buf[..]) {
861+
Ok(bytes_read) => {
862+
// We don't need to update the dirty bitmap manually here because it's
863+
// expected to be handled by the logic within the `Bytes`
864+
// implementation for the region object.
865+
let bytes_written = region.write(&buf[0..bytes_read], caddr)?;
866+
assert_eq!(bytes_written, bytes_read);
867+
break Ok(bytes_read);
882868
}
869+
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
870+
Err(e) => break Err(Error::IOError(e)),
883871
}
884872
}
885873
})
@@ -936,32 +924,15 @@ impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T {
936924
self.try_access(count, addr, |offset, len, caddr, region| -> Result<usize> {
937925
// Check if something bad happened before doing unsafe things.
938926
assert!(offset <= count);
939-
// SAFETY: Safe because we are checking the offset is valid.
940-
if let Some(src) = unsafe { region.as_slice() } {
941-
// This is safe cause `start` and `len` are within the `region`.
942-
let start = caddr.raw_value() as usize;
943-
let end = start + len;
944-
loop {
945-
// It is safe to read from volatile memory. Accessing the guest
946-
// memory as a slice should be OK as long as nothing assumes another
947-
// thread won't change what is loaded; however, we may want to introduce
948-
// VolatileRead and VolatileWrite traits in the future.
949-
match dst.write(&src[start..end]) {
950-
Ok(n) => break Ok(n),
951-
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
952-
Err(e) => break Err(Error::IOError(e)),
953-
}
954-
}
955-
} else {
956-
let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
957-
let mut buf = vec![0u8; len].into_boxed_slice();
958-
let bytes_read = region.read(&mut buf, caddr)?;
959-
assert_eq!(bytes_read, len);
960-
// For a non-RAM region, reading could have side effects, so we
961-
// must use write_all().
962-
dst.write_all(&buf).map_err(Error::IOError)?;
963-
Ok(len)
964-
}
927+
928+
let len = std::cmp::min(len, MAX_ACCESS_CHUNK);
929+
let mut buf = vec![0u8; len].into_boxed_slice();
930+
let bytes_read = region.read(&mut buf, caddr)?;
931+
assert_eq!(bytes_read, len);
932+
// For a non-RAM region, reading could have side effects, so we
933+
// must use write_all().
934+
dst.write_all(&buf).map_err(Error::IOError)?;
935+
Ok(len)
965936
})
966937
}
967938

src/mmap.rs

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,6 @@ impl NewBitmap for () {
5050
fn with_len(_len: usize) -> Self {}
5151
}
5252

53-
/// Trait implemented by the underlying `MmapRegion`.
54-
pub(crate) trait AsSlice {
55-
/// Returns a slice corresponding to the data in the underlying `MmapRegion`.
56-
///
57-
/// # Safety
58-
///
59-
/// This is unsafe because of possible aliasing.
60-
unsafe fn as_slice(&self) -> &[u8];
61-
62-
/// Returns a mutable slice corresponding to the data in the underlying `MmapRegion`.
63-
///
64-
/// # Safety
65-
///
66-
/// This is unsafe because of possible aliasing. Accesses done through the resulting slice
67-
/// are not visible to dirty bitmap tracking functionality (when present), and have to be
68-
/// explicitly accounted for.
69-
#[allow(clippy::mut_from_ref)]
70-
unsafe fn as_mut_slice(&self) -> &mut [u8];
71-
}
72-
7353
/// Errors that can occur when creating a memory map.
7454
#[derive(Debug)]
7555
pub enum Error {
@@ -486,14 +466,6 @@ impl<B: Bitmap> GuestMemoryRegion for GuestRegionMmap<B> {
486466
self.mapping.file_offset()
487467
}
488468

489-
unsafe fn as_slice(&self) -> Option<&[u8]> {
490-
Some(self.mapping.as_slice())
491-
}
492-
493-
unsafe fn as_mut_slice(&self) -> Option<&mut [u8]> {
494-
Some(self.mapping.as_mut_slice())
495-
}
496-
497469
fn get_slice(
498470
&self,
499471
offset: MemoryRegionAddress,

src/mmap_unix.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::result;
1919

2020
use crate::bitmap::{Bitmap, BS};
2121
use crate::guest_memory::FileOffset;
22-
use crate::mmap::{check_file_offset, AsSlice, NewBitmap};
22+
use crate::mmap::{check_file_offset, NewBitmap};
2323
use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice};
2424

2525
/// Error conditions that may arise when creating a new `MmapRegion` object.
@@ -401,21 +401,6 @@ impl<B: Bitmap> MmapRegion<B> {
401401
}
402402
}
403403

404-
impl<B> AsSlice for MmapRegion<B> {
405-
// SAFETY: This is safe because we mapped the area at addr ourselves, so this slice will not
406-
// overflow. However, it is possible to alias.
407-
unsafe fn as_slice(&self) -> &[u8] {
408-
std::slice::from_raw_parts(self.addr, self.size)
409-
}
410-
411-
// SAFETY: This is safe because we mapped the area at addr ourselves, so this slice will not
412-
// overflow. However, it is possible to alias.
413-
#[allow(clippy::mut_from_ref)]
414-
unsafe fn as_mut_slice(&self) -> &mut [u8] {
415-
std::slice::from_raw_parts_mut(self.addr, self.size)
416-
}
417-
}
418-
419404
impl<B: Bitmap> VolatileMemory for MmapRegion<B> {
420405
type B = B;
421406

src/mmap_windows.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use winapi::um::errhandlingapi::GetLastError;
1414

1515
use crate::bitmap::{Bitmap, BS};
1616
use crate::guest_memory::FileOffset;
17-
use crate::mmap::{AsSlice, NewBitmap};
17+
use crate::mmap::NewBitmap;
1818
use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice};
1919

2020
#[allow(non_snake_case)]
@@ -190,21 +190,6 @@ impl<B: Bitmap> MmapRegion<B> {
190190
}
191191
}
192192

193-
impl<B> AsSlice for MmapRegion<B> {
194-
unsafe fn as_slice(&self) -> &[u8] {
195-
// This is safe because we mapped the area at addr ourselves, so this slice will not
196-
// overflow. However, it is possible to alias.
197-
std::slice::from_raw_parts(self.addr, self.size)
198-
}
199-
200-
#[allow(clippy::mut_from_ref)]
201-
unsafe fn as_mut_slice(&self) -> &mut [u8] {
202-
// This is safe because we mapped the area at addr ourselves, so this slice will not
203-
// overflow. However, it is possible to alias.
204-
std::slice::from_raw_parts_mut(self.addr, self.size)
205-
}
206-
}
207-
208193
impl<B: Bitmap> VolatileMemory for MmapRegion<B> {
209194
type B = B;
210195

0 commit comments

Comments
 (0)