sys_util: add mmap with offset support

This is needed to support the plugin process API, which may register
guest memory mapped at an offset from the beginning of a file.

TEST=cargo test
BUG=None

Change-Id: Idf1e9f0287df5510728ab2bcf4dd090f9e81a5bf
Reviewed-on: https://chromium-review.googlesource.com/849495
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Zach Reizner 2018-01-03 14:33:55 -08:00 committed by chrome-bot
parent 89f81a761b
commit a13839564c
2 changed files with 58 additions and 2 deletions

View file

@ -8,7 +8,7 @@
use std;
use std::io::{Read, Write};
use std::ptr::null_mut;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::io::AsRawFd;
use libc;
@ -21,6 +21,8 @@ use data_model::DataInit;
pub enum Error {
/// Requested memory out of range.
InvalidAddress,
/// Requested offset is out of range of `libc::off_t`.
InvalidOffset,
/// Requested memory range spans past the end of the region.
InvalidRange(usize, usize),
/// Couldn't read from the given source.
@ -79,6 +81,19 @@ impl MemoryMapping {
/// * `fd` - File descriptor to mmap from.
/// * `size` - Size of memory region in bytes.
pub fn from_fd(fd: &AsRawFd, size: usize) -> Result<MemoryMapping> {
MemoryMapping::from_fd_offset(fd, size, 0)
}
/// Maps the `size` bytes starting at `offset` bytes of the given `fd`.
///
/// # Arguments
/// * `fd` - File descriptor to mmap from.
/// * `size` - Size of memory region in bytes.
/// * `offset` - Offset in bytes from the beginning of `fd` to start the mmap.
pub fn from_fd_offset(fd: &AsRawFd, size: usize, offset: usize) -> Result<MemoryMapping> {
if offset > libc::off_t::max_value() as usize {
return Err(Error::InvalidOffset);
}
// This is safe because we are creating a mapping in a place not already used by any other
// area in this process.
let addr = unsafe {
@ -87,7 +102,7 @@ impl MemoryMapping {
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd.as_raw_fd(),
0)
offset as libc::off_t)
};
if addr == libc::MAP_FAILED {
return Err(Error::SystemCallFailed(errno::Error::last()));
@ -363,6 +378,7 @@ impl Drop for MemoryMapping {
mod tests {
use super::*;
use data_model::{VolatileMemory, VolatileMemoryError};
use std::os::unix::io::FromRawFd;
#[test]
fn basic_map() {
@ -439,4 +455,15 @@ mod tests {
let res = m.get_slice(3, 3).unwrap_err();
assert_eq!(res, VolatileMemoryError::OutOfBounds { addr: 6 });
}
#[test]
fn from_fd_offset_invalid() {
let fd = unsafe { std::fs::File::from_raw_fd(-1) };
let res = MemoryMapping::from_fd_offset(&fd, 4096, (libc::off_t::max_value() as usize) + 1)
.unwrap_err();
match res {
Error::InvalidOffset => {}
e => panic!("unexpected error: {:?}", e),
}
}
}

View file

@ -190,4 +190,33 @@ mod tests {
assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
}
}
#[test]
fn mmap_page_offset() {
if !kernel_has_memfd() {
return;
}
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
shm.set_size(8092)
.expect("failed to set shared memory size");
let mmap1 =
MemoryMapping::from_fd_offset(&shm, shm.size() as usize, 4096)
.expect("failed to map shared memory");
let mmap2 =
MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
mmap1
.get_slice(0, 4096)
.expect("failed to get mmap slice")
.read_from(&mut repeat(0x45))
.expect("failed to fill mmap slice");
for i in 0..4096 {
assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0);
}
for i in 4096..8092 {
assert_eq!(mmap2.get_ref::<u8>(i).unwrap().load(), 0x45u8);
}
}
}