diff options
Diffstat (limited to 'sys_util/src/mmap.rs')
-rw-r--r-- | sys_util/src/mmap.rs | 256 |
1 files changed, 215 insertions, 41 deletions
diff --git a/sys_util/src/mmap.rs b/sys_util/src/mmap.rs index 22c6753..71b8d56 100644 --- a/sys_util/src/mmap.rs +++ b/sys_util/src/mmap.rs @@ -6,26 +6,45 @@ //! mmap object leaves scope. use std; +use std::io::{Read, Write}; use std::ptr::null_mut; use std::os::unix::io::AsRawFd; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; use libc; -use {Result, errno_result}; +use errno; + +#[derive(Debug)] +pub enum Error { + /// Requested memory out of range. + InvalidAddress, + /// Couldn't read from the given source. + ReadFromSource, + /// `mmap` returned the given error. + SystemCallFailed(errno::Error), + /// Wrting to memory failed + WriteToMemory(std::io::Error), +} +pub type Result<T> = std::result::Result<T, Error>; /// Wraps an anonymous shared memory mapping in the current process. pub struct MemoryMapping { addr: *mut u8, size: usize, - ref_count: Arc<AtomicUsize>, } +// Send and Sync aren't automatically inherited for the raw address pointer. +// Accessing that pointer is only done through the stateless interface which +// allows the object to be shared by multiple threads without a decrease in +// safety. unsafe impl Send for MemoryMapping {} +unsafe impl Sync for MemoryMapping {} impl MemoryMapping { /// Creates an anonymous shared mapping of `size` bytes. + /// + /// # Arguments + /// * `size` - Size of memory region in bytes. pub fn new(size: usize) -> Result<MemoryMapping> { // This is safe because we are creating an anonymous mapping in a place not already used by // any other area in this process. @@ -37,17 +56,20 @@ impl MemoryMapping { -1, 0) }; - if addr == null_mut() { - return errno_result(); + if addr.is_null() { + return Err(Error::SystemCallFailed(errno::Error::last())); } Ok(MemoryMapping { addr: addr as *mut u8, size: size, - ref_count: Arc::new(AtomicUsize::new(1)), }) } /// Maps the first `size` bytes of the given `fd`. + /// + /// # Arguments + /// * `fd` - File descriptor to mmap from. + /// * `size` - Size of memory region in bytes. pub fn from_fd(fd: &AsRawFd, size: usize) -> Result<MemoryMapping> { // This is safe because we are creating a mapping in a place not already used by any other // area in this process. @@ -59,63 +81,217 @@ impl MemoryMapping { fd.as_raw_fd(), 0) }; - if addr == null_mut() { - return errno_result(); + if addr.is_null() { + return Err(Error::SystemCallFailed(errno::Error::last())); } Ok(MemoryMapping { addr: addr as *mut u8, size: size, - ref_count: Arc::new(AtomicUsize::new(1)), }) } + /// Returns a pointer to the begining of the memory region. Should only be + /// used for passing this region to ioctls for setting guest memory. pub fn as_ptr(&self) -> *mut u8 { self.addr } + /// Returns the size of the memory region in bytes. pub fn size(&self) -> usize { self.size } - #[deprecated(note="use volatile_read with the ptr instead")] - pub fn as_slice(&self) -> &[u8] { - // This is safe because we mapped the area at addr ourselves, so this slice will not - // overflow. However, it is possible to alias, hence the deprecation. - unsafe { std::slice::from_raw_parts(self.addr, self.size) } + /// Writes a slice to the memory region at the specified offset. + /// Returns Ok(<number of bytes written>). The number of bytes written can + /// be less than the length of the slice if there isn't enough room in the + /// memory region. + /// + /// # Examples + /// * Write a slice at offset 256. + /// + /// ``` + /// # use sys_util::MemoryMapping; + /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); + /// let res = mem_map.write_slice(&[1,2,3,4,5], 0); + /// assert!(res.is_ok()); + /// assert_eq!(res.unwrap(), 5); + /// ``` + pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> { + if offset >= self.size { + return Err(Error::InvalidAddress); + } + unsafe { + // Guest memory can't strictly be modeled as a slice because it is + // volatile. Writing to it with what compiles down to a memcpy + // won't hurt anything as long as we get the bounds checks right. + let mut slice: &mut [u8] = &mut self.as_mut_slice()[offset..]; + Ok(slice.write(buf).map_err(Error::WriteToMemory)?) + } } - #[deprecated(note="use volatile_write with the ptr instead")] - pub fn as_mut_slice(&self) -> &mut [u8] { - // This is safe because we mapped the area at addr ourselves, so this slice will not - // overflow. However, it is possible to alias, hence the deprecation. - unsafe { std::slice::from_raw_parts_mut(self.addr, self.size) } + /// Writes an object to the memory region at the specified offset. + /// Returns Ok(()) if the object fits, or Err if it extends past the end. + /// + /// # Examples + /// * Write a u64 at offset 16. + /// + /// ``` + /// # use sys_util::MemoryMapping; + /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); + /// let res = mem_map.write_obj(55u64, 16); + /// assert!(res.is_ok()); + /// ``` + pub fn write_obj<T>(&self, val: T, offset: usize) -> Result<()> { + unsafe { + // Guest memory can't strictly be modeled as a slice because it is + // volatile. Writing to it with what compiles down to a memcpy + // won't hurt anything as long as we get the bounds checks right. + if offset + std::mem::size_of::<T>() > self.size { + return Err(Error::InvalidAddress); + } + std::ptr::write_volatile(&mut self.as_mut_slice()[offset..] as *mut _ as *mut T, val); + Ok(()) + } + } + + /// Reads on object from the memory region at the given offset. + /// Reading from a volatile area isn't strictly safe as it could change + /// mid-read. However, as long as the type T is plain old data and can + /// handle random initialization, everything will be OK. + /// + /// # Examples + /// * Read a u64 written to offset 32. + /// + /// ``` + /// # use sys_util::MemoryMapping; + /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); + /// let res = mem_map.write_obj(55u64, 32); + /// assert!(res.is_ok()); + /// let num: u64 = mem_map.read_obj(32).unwrap(); + /// assert_eq!(55, num); + /// ``` + pub fn read_obj<T: Copy>(&self, offset: usize) -> Result<T> { + if offset + std::mem::size_of::<T>() > self.size { + return Err(Error::InvalidAddress); + } + unsafe { + // This is safe because by definition Copy types can have their bits + // set arbitrarily and still be valid. + Ok(std::ptr::read_volatile(&self.as_slice()[offset..] as *const _ as *const T)) + } + } + + /// Reads data from a readable object like a File and writes it to guest memory. + /// + /// # Arguments + /// * `mem_offset` - Begin writing memory at this offset. + /// * `src` - Read from `src` to memory. + /// * `count` - Read `count` bytes from `src` to memory. + /// + /// # Examples + /// + /// * Read bytes from /dev/urandom + /// + /// ``` + /// # use sys_util::MemoryMapping; + /// # use std::fs::File; + /// # use std::path::Path; + /// # fn test_read_random() -> Result<u32, ()> { + /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); + /// let mut file = File::open(Path::new("/dev/urandom")).map_err(|_| ())?; + /// mem_map.read_to_memory(32, &mut file, 128).map_err(|_| ())?; + /// let rand_val: u32 = mem_map.read_obj(40).map_err(|_| ())?; + /// # Ok(rand_val) + /// # } + /// ``` + pub fn read_to_memory<F>(&self, mem_offset: usize, src: &mut F, count: usize) -> Result<()> + where F: Read + { + let mem_end = mem_offset + count; + if mem_end > self.size() { + return Err(Error::InvalidAddress); + } + unsafe { + // It is safe to overwrite the volatile memory. Acessing the guest + // memory as a mutable slice is OK because nothing assumes another + // thread won't change what is loaded. + let mut dst = &mut self.as_mut_slice()[mem_offset..mem_end]; + if src.read_exact(dst).is_err() { + return Err(Error::ReadFromSource); + } + } + Ok(()) } - // TODO(zachr): remove when we no longer need it, clone is sketchy - pub fn clone(&self) -> MemoryMapping { - self.ref_count.fetch_add(1, Ordering::SeqCst); - MemoryMapping { - addr: self.addr, - size: self.size, - ref_count: self.ref_count.clone(), + /// Writes data from memory to a writable object. + /// + /// # Arguments + /// * `mem_offset` - Begin reading memory from this offset. + /// * `dst` - Write from memory to `dst`. + /// * `count` - Read `count` bytes from memory to `src`. + /// + /// # Examples + /// + /// * Write 128 bytes to /dev/null + /// + /// ``` + /// # use sys_util::MemoryMapping; + /// # use std::fs::File; + /// # use std::path::Path; + /// # fn test_write_null() -> Result<(), ()> { + /// # let mut mem_map = MemoryMapping::new(1024).unwrap(); + /// let mut file = File::open(Path::new("/dev/null")).map_err(|_| ())?; + /// mem_map.write_from_memory(32, &mut file, 128).map_err(|_| ())?; + /// # Ok(()) + /// # } + /// ``` + pub fn write_from_memory<F>(&self, mem_offset: usize, dst: &mut F, count: usize) -> Result<()> + where F: Write + { + let mem_end = match mem_offset.checked_add(count) { + None => return Err(Error::InvalidAddress), + Some(m) => m, + }; + if mem_end > self.size() { + return Err(Error::InvalidAddress); + } + unsafe { + // It is safe to read from volatile memory. Acessing the guest + // memory as a slice is OK because nothing assumes another thread + // won't change what is loaded. + let src = &self.as_mut_slice()[mem_offset..mem_end]; + if dst.write_all(src).is_err() { + return Err(Error::ReadFromSource); + } } + Ok(()) + } + + unsafe fn as_slice(&self) -> &[u8] { + // This is safe because we mapped the area at addr ourselves, so this slice will not + // overflow. However, it is possible to alias. + std::slice::from_raw_parts(self.addr, self.size) + } + + unsafe fn as_mut_slice(&self) -> &mut [u8] { + // This is safe because we mapped the area at addr ourselves, so this slice will not + // overflow. However, it is possible to alias. + std::slice::from_raw_parts_mut(self.addr, self.size) } } impl Drop for MemoryMapping { fn drop(&mut self) { - if self.ref_count.fetch_sub(1, Ordering::SeqCst) == 1 { - // This is safe because we mmap the area at addr ourselves, and the ref_count ensures - // nobody else is holding a reference to it. - unsafe { - libc::munmap(self.addr as *mut libc::c_void, self.size); - } + // This is safe because we mmap the area at addr ourselves, and nobody + // else is holding a reference to it. + unsafe { + libc::munmap(self.addr as *mut libc::c_void, self.size); } } } #[cfg(test)] -mod test { +mod tests { use super::*; #[test] @@ -125,12 +301,10 @@ mod test { } #[test] - fn mutate_slices() { - let m = MemoryMapping::new(1024).unwrap(); - assert_eq!(1024, m.size()); - { - m.as_mut_slice()[128] = 55; - } - assert_eq!(m.as_slice()[128], 55); + fn test_write_past_end() { + let m = MemoryMapping::new(5).unwrap(); + let res = m.write_slice(&[1, 2, 3, 4, 5, 6], 0); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), 5); } } |