summary refs log tree commit diff
path: root/sys_util/src/shm.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2017-07-06 17:02:26 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-07-10 11:22:37 -0700
commitfd3425db4731e8db64d1dab632892544de6b12d4 (patch)
tree37cc9c9d668835220ab986523bed31f6e5149277 /sys_util/src/shm.rs
parentf98d8f3ba6d0d158accb65f4a2ff726da1c65486 (diff)
downloadcrosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar.gz
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar.bz2
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar.lz
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar.xz
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.tar.zst
crosvm-fd3425db4731e8db64d1dab632892544de6b12d4.zip
sys_util: add shared memory module
This CL adds the SharedMemory structure for managing memory attached to
a file descriptor, which can be sent to external processes.

TEST=cargo test
BUG=chromium:738638

Change-Id: I2b6a10e0e7275367ddb578831a73de6956d231bb
Reviewed-on: https://chromium-review.googlesource.com/562509
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'sys_util/src/shm.rs')
-rw-r--r--sys_util/src/shm.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/sys_util/src/shm.rs b/sys_util/src/shm.rs
new file mode 100644
index 0000000..5fee8df
--- /dev/null
+++ b/sys_util/src/shm.rs
@@ -0,0 +1,169 @@
+// 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};
+
+use libc::{off64_t, c_int, c_uint, c_char, syscall, ftruncate64};
+
+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 {
+    syscall(SYS_memfd_create as i64, name as i64, flags as i64) as c_int
+}
+
+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.
+    ///
+    /// The file descriptor is opened with the close on exec flag.
+    pub fn new(name: Option<&CStr>) -> Result<SharedMemory> {
+        let shm_name = name.map(|n| n.as_ptr())
+            .unwrap_or(b"/crosvm_shm\0".as_ptr() as *const i8);
+        // The following are safe because we give a valid C string and check the
+        // results of the memfd_create call.
+        let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC) };
+        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,
+           })
+    }
+
+    /// 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()
+    }
+}
+
+#[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() {
+        let shm = SharedMemory::new(None).expect("failed to create shared memory");
+        assert_eq!(shm.size(), 0);
+    }
+
+    #[test]
+    fn new_sized() {
+        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() {
+        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() {
+        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() {
+        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));
+    }
+
+    #[test]
+    fn mmap_page() {
+        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);
+        }
+    }
+}