summary refs log tree commit diff
path: root/sys_util/src/shm.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2018-01-03 15:21:04 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-01-05 14:28:44 -0800
commitd42e4931436c67cfbf5750c5bc27f029f4fe64ac (patch)
tree9b94f42d12d213c1b3d26497b0088caca62cb9ce /sys_util/src/shm.rs
parenta13839564cde44efdb9b39c8c6c6235171326e77 (diff)
downloadcrosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar.gz
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar.bz2
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar.lz
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar.xz
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.tar.zst
crosvm-d42e4931436c67cfbf5750c5bc27f029f4fe64ac.zip
sys_util: add memfd seal support to SharedMemory
Getting and settings seals is useful to ensure the size of files
underlying memory mappings doesn't shrink, which can trigger a SIGBUS on
access to the truncated pages.

This also bumps the libc version to get MFD_ALLOW_SEALING.

TEST=cargo test
BUG=None
CQ-DEPEND=CL:850535

Change-Id: Ifbe1ec2c47d3d5c51b63472f545acc10d3c8eed2
Reviewed-on: https://chromium-review.googlesource.com/849488
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.rs115
1 files changed, 112 insertions, 3 deletions
diff --git a/sys_util/src/shm.rs b/sys_util/src/shm.rs
index a2103c7..b15ba56 100644
--- a/sys_util/src/shm.rs
+++ b/sys_util/src/shm.rs
@@ -7,7 +7,9 @@ use std::fs::File;
 use std::io::{Seek, SeekFrom};
 use std::os::unix::io::{AsRawFd, IntoRawFd, FromRawFd, RawFd};
 
-use libc::{self, off64_t, c_long, c_int, c_uint, c_char, close, syscall, ftruncate64};
+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};
 
 use errno;
 use syscall_defines::linux::LinuxSyscall::SYS_memfd_create;
@@ -27,19 +29,87 @@ unsafe fn memfd_create(name: *const c_char, flags: c_uint) -> c_int {
     syscall(SYS_memfd_create as c_long, name as i64, flags as i64) as c_int
 }
 
+/// 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;
+    }
+}
+
 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.
+    /// The file descriptor is opened with the close on exec flag and allows memfd sealing.
     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 c_char);
         // 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) };
+        let fd = unsafe { memfd_create(shm_name, MFD_CLOEXEC | MFD_ALLOW_SEALING) };
         if fd < 0 {
             return errno_result();
         }
@@ -63,6 +133,29 @@ impl SharedMemory {
            })
     }
 
+    /// 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(())
+    }
+
     /// 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
@@ -166,6 +259,22 @@ mod tests {
     }
 
     #[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();
+    }
+
+    #[test]
     fn mmap_page() {
         if !kernel_has_memfd() { return; }
         let mut shm = SharedMemory::new(None).expect("failed to create shared memory");