2017-07-07 00:02:26 +00:00
|
|
|
// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{Seek, SeekFrom};
|
|
|
|
use std::os::unix::io::{AsRawFd, IntoRawFd, FromRawFd, RawFd};
|
|
|
|
|
2018-01-03 23:21:04 +00:00
|
|
|
use libc::{self, off64_t, c_long, c_int, c_uint, c_char, close, syscall, ftruncate64, fcntl,
|
|
|
|
F_ADD_SEALS, F_GET_SEALS, F_SEAL_GROW, F_SEAL_SHRINK, F_SEAL_WRITE, F_SEAL_SEAL,
|
|
|
|
MFD_ALLOW_SEALING};
|
2017-07-07 00:02:26 +00:00
|
|
|
|
2017-11-14 00:47:25 +00:00
|
|
|
use errno;
|
2017-07-07 00:02:26 +00:00
|
|
|
use syscall_defines::linux::LinuxSyscall::SYS_memfd_create;
|
|
|
|
|
|
|
|
use {Result, errno_result};
|
|
|
|
|
|
|
|
/// A shared memory file descriptor and its size.
|
|
|
|
pub struct SharedMemory {
|
|
|
|
fd: File,
|
|
|
|
size: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
// from <sys/memfd.h>
|
|
|
|
const MFD_CLOEXEC: c_uint = 0x0001;
|
|
|
|
|
|
|
|
unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
|
2017-08-31 00:07:01 +00:00
|
|
|
syscall(SYS_memfd_create as c_long, name as i64, flags as i64) as c_int
|
2017-07-07 00:02:26 +00:00
|
|
|
}
|
|
|
|
|
2018-01-03 23:21:04 +00:00
|
|
|
/// A set of memfd seals.
|
|
|
|
///
|
|
|
|
/// An enumeration of each bit can be found at `fcntl(2)`.
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct MemfdSeals(i32);
|
|
|
|
|
|
|
|
impl MemfdSeals {
|
|
|
|
/// Returns an empty set of memfd seals.
|
|
|
|
#[inline]
|
|
|
|
pub fn new() -> MemfdSeals {
|
|
|
|
MemfdSeals(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the raw bitmask of seals enumerated in `fcntl(2)`.
|
|
|
|
#[inline]
|
|
|
|
pub fn bitmask(self) -> i32 {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// True of the grow seal bit is present.
|
|
|
|
#[inline]
|
|
|
|
pub fn grow_seal(self) -> bool {
|
|
|
|
self.0 & F_SEAL_GROW != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the grow seal bit.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_grow_seal(&mut self) {
|
|
|
|
self.0 |= F_SEAL_GROW;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// True of the shrink seal bit is present.
|
|
|
|
#[inline]
|
|
|
|
pub fn shrink_seal(self) -> bool {
|
|
|
|
self.0 & F_SEAL_SHRINK != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the shrink seal bit.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_shrink_seal(&mut self) {
|
|
|
|
self.0 |= F_SEAL_SHRINK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// True of the write seal bit is present.
|
|
|
|
#[inline]
|
|
|
|
pub fn write_seal(self) -> bool {
|
|
|
|
self.0 & F_SEAL_WRITE != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the write seal bit.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_write_seal(&mut self) {
|
|
|
|
self.0 |= F_SEAL_WRITE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// True of the seal seal bit is present.
|
|
|
|
#[inline]
|
|
|
|
pub fn seal_seal(self) -> bool {
|
|
|
|
self.0 & F_SEAL_SEAL != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the seal seal bit.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_seal_seal(&mut self) {
|
|
|
|
self.0 |= F_SEAL_SEAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 00:02:26 +00:00
|
|
|
impl SharedMemory {
|
|
|
|
/// Creates a new shared memory file descriptor with zero size.
|
|
|
|
///
|
|
|
|
/// If a name is given, it will appear in `/proc/self/fd/<shm fd>` for the purposes of
|
|
|
|
/// debugging. The name does not need to be unique.
|
|
|
|
///
|
2018-01-03 23:21:04 +00:00
|
|
|
/// The file descriptor is opened with the close on exec flag and allows memfd sealing.
|
2017-07-07 00:02:26 +00:00
|
|
|
pub fn new(name: Option<&CStr>) -> Result<SharedMemory> {
|
|
|
|
let shm_name = name.map(|n| n.as_ptr())
|
2017-08-31 00:07:01 +00:00
|
|
|
.unwrap_or(b"/crosvm_shm\0".as_ptr() as *const c_char);
|
2017-07-07 00:02:26 +00:00
|
|
|
// The following are safe because we give a valid C string and check the
|
|
|
|
// results of the memfd_create call.
|
2018-01-03 23:21:04 +00:00
|
|
|
let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC | MFD_ALLOW_SEALING) };
|
2017-07-07 00:02:26 +00:00
|
|
|
if fd < 0 {
|
|
|
|
return errno_result();
|
|
|
|
}
|
|
|
|
|
|
|
|
let file = unsafe { File::from_raw_fd(fd) };
|
|
|
|
|
|
|
|
Ok(SharedMemory { fd: file, size: 0 })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructs a `SharedMemory` instance from a file descriptor that represents shared memory.
|
|
|
|
///
|
|
|
|
/// The size of the resulting shared memory will be determined using `File::seek`. If the given
|
|
|
|
/// file's size can not be determined this way, this will return an error.
|
|
|
|
pub fn from_raw_fd<T: IntoRawFd>(fd: T) -> Result<SharedMemory> {
|
|
|
|
// Safe because the IntoRawFd trait indicates fd has unique ownership.
|
|
|
|
let mut file = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
|
|
|
|
let file_size = file.seek(SeekFrom::End(0))?;
|
|
|
|
Ok(SharedMemory {
|
|
|
|
fd: file,
|
|
|
|
size: file_size as u64,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-01-03 23:21:04 +00:00
|
|
|
/// Gets the memfd seals that have already been added to this.
|
|
|
|
///
|
|
|
|
/// This may fail if this instance was not constructed from a memfd.
|
|
|
|
pub fn get_seals(&self) -> Result<MemfdSeals> {
|
|
|
|
let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_GET_SEALS) };
|
|
|
|
if ret < 0 {
|
|
|
|
return errno_result();
|
|
|
|
}
|
|
|
|
Ok(MemfdSeals(ret))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds the given set of memfd seals.
|
|
|
|
///
|
|
|
|
/// This may fail if this instance was not constructed from a memfd with sealing allowed or if
|
|
|
|
/// the seal seal (`F_SEAL_SEAL`) bit was already added.
|
|
|
|
pub fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
|
|
|
|
let ret = unsafe { fcntl(self.fd.as_raw_fd(), F_ADD_SEALS, seals) };
|
|
|
|
if ret < 0 {
|
|
|
|
return errno_result();
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2017-07-07 00:02:26 +00:00
|
|
|
/// Gets the size in bytes of the shared memory.
|
|
|
|
///
|
|
|
|
/// The size returned here does not reflect changes by other interfaces or users of the shared
|
|
|
|
/// memory file descriptor..
|
|
|
|
pub fn size(&self) -> u64 {
|
|
|
|
self.size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the size in bytes of the shared memory.
|
|
|
|
///
|
|
|
|
/// Note that if some process has already mapped this shared memory and the new size is smaller,
|
|
|
|
/// that process may get signaled with SIGBUS if they access any page past the new size.
|
|
|
|
pub fn set_size(&mut self, size: u64) -> Result<()> {
|
|
|
|
let ret = unsafe { ftruncate64(self.fd.as_raw_fd(), size as off64_t) };
|
|
|
|
if ret < 0 {
|
|
|
|
return errno_result();
|
|
|
|
}
|
|
|
|
self.size = size;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRawFd for SharedMemory {
|
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
|
|
self.fd.as_raw_fd()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-14 00:47:25 +00:00
|
|
|
/// Checks if the kernel we are running on has memfd_create. It was introduced in 3.17.
|
|
|
|
/// Only to be used from tests to prevent running on ancient kernels that won't
|
|
|
|
/// support the functionality anyways.
|
|
|
|
pub fn kernel_has_memfd() -> bool {
|
|
|
|
unsafe {
|
|
|
|
let fd = memfd_create(b"/test_memfd_create\0".as_ptr() as *const c_char, 0);
|
|
|
|
if fd < 0 {
|
|
|
|
if errno::Error::last().errno() == libc::ENOSYS {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2017-07-07 00:02:26 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
use std::ffi::CString;
|
|
|
|
use std::fs::read_link;
|
|
|
|
use std::io::repeat;
|
|
|
|
|
|
|
|
use data_model::VolatileMemory;
|
|
|
|
|
|
|
|
use MemoryMapping;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
assert_eq!(shm.size(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_sized() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
shm.set_size(1024)
|
|
|
|
.expect("failed to set shared memory size");
|
|
|
|
assert_eq!(shm.size(), 1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_huge() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
shm.set_size(0x7fff_ffff_ffff_ffff)
|
|
|
|
.expect("failed to set shared memory size");
|
|
|
|
assert_eq!(shm.size(), 0x7fff_ffff_ffff_ffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_too_huge() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
shm.set_size(0x8000_0000_0000_0000).unwrap_err();
|
|
|
|
assert_eq!(shm.size(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_named() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let name = "very unique name";
|
|
|
|
let cname = CString::new(name).unwrap();
|
|
|
|
let shm = SharedMemory::new(Some(&cname)).expect("failed to create shared memory");
|
|
|
|
let fd_path = format!("/proc/self/fd/{}", shm.as_raw_fd());
|
|
|
|
let link_name =
|
|
|
|
read_link(fd_path).expect("failed to read link of shared memory /proc/self/fd entry");
|
|
|
|
assert!(link_name.to_str().unwrap().contains(name));
|
|
|
|
}
|
|
|
|
|
2018-01-03 23:21:04 +00:00
|
|
|
#[test]
|
|
|
|
fn new_sealed() {
|
|
|
|
if !kernel_has_memfd() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
let mut seals = shm.get_seals().expect("failed to get seals");
|
|
|
|
assert_eq!(seals.bitmask(), 0);
|
|
|
|
seals.set_seal_seal();
|
|
|
|
shm.add_seals(seals).expect("failed to add seals");
|
|
|
|
seals = shm.get_seals().expect("failed to get seals");
|
|
|
|
assert!(seals.seal_seal());
|
|
|
|
// Adding more seals should be rejected by the kernel.
|
|
|
|
shm.add_seals(seals).unwrap_err();
|
|
|
|
}
|
|
|
|
|
2017-07-07 00:02:26 +00:00
|
|
|
#[test]
|
|
|
|
fn mmap_page() {
|
2017-11-14 00:47:25 +00:00
|
|
|
if !kernel_has_memfd() { return; }
|
2017-07-07 00:02:26 +00:00
|
|
|
let mut shm = SharedMemory::new(None).expect("failed to create shared memory");
|
|
|
|
shm.set_size(4096)
|
|
|
|
.expect("failed to set shared memory size");
|
|
|
|
|
|
|
|
let mmap1 =
|
|
|
|
MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
|
|
|
|
let mmap2 =
|
|
|
|
MemoryMapping::from_fd(&shm, shm.size() as usize).expect("failed to map shared memory");
|
|
|
|
|
|
|
|
assert_ne!(mmap1.get_slice(0, 1).unwrap().as_ptr(),
|
|
|
|
mmap2.get_slice(0, 1).unwrap().as_ptr());
|
|
|
|
|
|
|
|
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(), 0x45u8);
|
|
|
|
}
|
|
|
|
}
|
2018-01-03 22:33:55 +00:00
|
|
|
|
|
|
|
#[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);
|
|
|
|
}
|
|
|
|
}
|
2017-07-07 00:02:26 +00:00
|
|
|
}
|