From d42e4931436c67cfbf5750c5bc27f029f4fe64ac Mon Sep 17 00:00:00 2001 From: Zach Reizner Date: Wed, 3 Jan 2018 15:21:04 -0800 Subject: [PATCH] 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 Tested-by: Zach Reizner Reviewed-by: Dylan Reid --- Cargo.lock | 26 +++++----- Cargo.toml | 2 +- sys_util/src/shm.rs | 115 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a3ff71173..d09fc4c198 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ "io_jail 0.1.0", "kernel_loader 0.1.0", "kvm 0.1.0", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", "vm_control 0.1.0", "x86_64 0.1.0", @@ -30,7 +30,7 @@ dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "data_model 0.1.0", "io_jail 0.1.0", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "net_sys 0.1.0", "net_util 0.1.0", "sys_util 0.1.0", @@ -48,14 +48,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "io_jail" version = "0.1.0" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "kernel_loader" version = "0.1.0" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -64,7 +64,7 @@ name = "kvm" version = "0.1.0" dependencies = [ "kvm_sys 0.1.0", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -72,13 +72,13 @@ dependencies = [ name = "kvm_sys" version = "0.1.0" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] [[package]] name = "libc" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -92,7 +92,7 @@ dependencies = [ name = "net_util" version = "0.1.0" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "net_sys 0.1.0", "sys_util 0.1.0", ] @@ -103,7 +103,7 @@ version = "0.1.0" dependencies = [ "data_model 0.1.0", "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "syscall_defines 0.1.0", ] @@ -115,7 +115,7 @@ version = "0.1.0" name = "vhost" version = "0.1.0" dependencies = [ - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "net_util 0.1.0", "sys_util 0.1.0", "virtio_sys 0.1.0", @@ -135,7 +135,7 @@ dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "data_model 0.1.0", "kvm 0.1.0", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -147,11 +147,11 @@ dependencies = [ "data_model 0.1.0", "kvm 0.1.0", "kvm_sys 0.1.0", - "libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] [metadata] "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "56cce3130fd040c28df6f495c8492e5ec5808fb4c9093c310df02b0c8f030148" +"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0" diff --git a/Cargo.toml b/Cargo.toml index 74e272b1ba..98c824c64a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ io_jail = { path = "io_jail" } kvm = { path = "kvm" } sys_util = { path = "sys_util" } kernel_loader = { path = "kernel_loader" } -libc = "=0.2.32" +libc = "=0.2.34" byteorder = "=1.1.0" vm_control = { path = "vm_control" } data_model = { path = "data_model" } diff --git a/sys_util/src/shm.rs b/sys_util/src/shm.rs index a2103c7901..b15ba560d2 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/` 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 { 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 { + 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 @@ -165,6 +258,22 @@ mod tests { assert!(link_name.to_str().unwrap().contains(name)); } + #[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; }