diff options
author | Zach Reizner <zachr@google.com> | 2018-01-03 15:21:04 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-01-05 14:28:44 -0800 |
commit | d42e4931436c67cfbf5750c5bc27f029f4fe64ac (patch) | |
tree | 9b94f42d12d213c1b3d26497b0088caca62cb9ce /sys_util/src/shm.rs | |
parent | a13839564cde44efdb9b39c8c6c6235171326e77 (diff) | |
download | crosvm-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.rs | 115 |
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"); |