Skip to content

Commit

Permalink
Merge pull request #12
Browse files Browse the repository at this point in the history
Implement IntoRawFd and modernize the codebase slightly
  • Loading branch information
nagisa committed Jul 4, 2020
2 parents 7f6fa4e + a4aa039 commit edc4196
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 82 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ exclude = [
libc = "0.2"
# Public dependencies, exposed through library API.
either = "1.5"
thiserror = "1"

[package.metadata.release]
sign-commit = true
Expand All @@ -27,3 +26,6 @@ disable-push = true
pre-release-commit-message = "cargo: memfd release {{version}}"
post-release-commit-message = "cargo: development version bump"
tag-message = "memfd {{version}}"

[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
41 changes: 31 additions & 10 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
//! Error handling.
use std::fmt;

/// Enumeration of errors possible in this library
#[derive(thiserror::Error, Debug)]
#[derive(Debug)]
pub enum Error {
/// Cannot convert the `name` argument to a C String!
#[error("cannot convert `name` to a C string")]
NameCStringConversion(#[source] std::ffi::NulError),
NameCStringConversion(std::ffi::NulError),
/// Cannot create the memfd
#[error("cannot create memfd")]
Create(#[source] std::io::Error),
Create(std::io::Error),
/// Cannot add new seals to the memfd
#[error("cannot add seals to the memfd")]
AddSeals(#[source] std::io::Error),
AddSeals(std::io::Error),
/// Cannot read the seals of a memfd
#[error("cannot read seals for a memfd")]
GetSeals(#[source] std::io::Error),
GetSeals(std::io::Error),
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match self {
NameCStringConversion(ref e) => Some(e),
Create(ref e) => Some(e),
AddSeals(ref e) => Some(e),
GetSeals(ref e) => Some(e),
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
f.write_str(match self {
NameCStringConversion(_) => "cannot convert `name` to a C string",
Create(_) => "cannot create a memfd",
AddSeals(_) => "cannot add seals to the memfd",
GetSeals(_) => "cannot read seals for a memfd",
})
}
}

#[cfg(test)]
#[test]
fn error_send_sync() {
fn assert_error<E: std::error::Error + Send + Sync + 'static>() {}
fn assert_error<E: std::error::Error + Send + Sync + fmt::Display + fmt::Debug + 'static>() {}
assert_error::<Error>();
}
26 changes: 17 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,26 @@
//! Ok(mfd)
//! }
//! ```

#![deny(missing_docs)]

extern crate either;
extern crate libc;
extern crate thiserror;
#![deny(
missing_docs,
intra_doc_link_resolution_failure,
clippy::all,
unreachable_pub,
unused,
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, doc(cfg(target_os="linux")))]
// No-op crate on platforms that do not support memfd_create, instead of failing to link, or at
// runtime.
#![cfg(target_os="linux")]

mod errors;
mod memfd;
mod nr;
mod sealing;

pub use crate::errors::Error;
pub use crate::memfd::{HugetlbSize, Memfd, MemfdOptions};
pub use crate::sealing::{FileSeal, SealsHashSet};
pub use crate::{
errors::Error,
memfd::{HugetlbSize, Memfd, MemfdOptions},
sealing::{FileSeal, SealsHashSet},
};
115 changes: 69 additions & 46 deletions src/memfd.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::nr;
use crate::sealing;
use either;
use libc;
use std::ffi;
use std::fs;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::{ffi, fs, os::raw, os::unix::io::{AsRawFd, FromRawFd, RawFd}};
use crate::{nr, sealing};

#[cfg(target_os="linux")]
unsafe fn memfd_create(name: *const raw::c_char, flags: raw::c_uint) -> raw::c_int {
libc::syscall(libc::SYS_memfd_create, name, flags) as raw::c_int
}

/// A `Memfd` builder, providing advanced options and flags for specifying its behavior.
#[derive(Clone, Debug)]
Expand All @@ -18,32 +18,34 @@ impl MemfdOptions {
/// Default set of options for `Memfd` creation.
///
/// The default options are:
/// * sealing: `F_SEAL_SEAL` (i.e. no further sealing)
/// * close-on-exec: false
/// * hugetlb: false
/// * [`FileSeal::SealSeal`] (i.e. no further sealing);
/// * close-on-exec is disabled;
/// * hugetlb is disabled.
///
/// [`FileSeal::SealSeal`]: sealing::FileSeal::SealSeal
pub fn new() -> Self {
Self::default()
}

/// Whether to allow sealing on the final memfd.
/// Whether to allow adding seals to the created `Memfd`.
pub fn allow_sealing(mut self, value: bool) -> Self {
self.allow_sealing = value;
self
}

/// Whether to set the `FD_CLOEXEC` flag on the final memfd.
/// Whether to set the `FD_CLOEXEC` flag on the created `Memfd`.
pub fn close_on_exec(mut self, value: bool) -> Self {
self.cloexec = value;
self
}

/// Optional hugetlb support and page size for the final memfd.
/// Optional hugetlb support and page size for the created `Memfd`.
pub fn hugetlb(mut self, size: Option<HugetlbSize>) -> Self {
self.hugetlb = size;
self
}

/// Translates the current options into a bitflags value for `memfd_create`.
/// Translate the current options into a bitflags value for `memfd_create`.
fn bitflags(&self) -> u32 {
let mut bits = 0;
if self.allow_sealing {
Expand All @@ -59,22 +61,28 @@ impl MemfdOptions {
bits
}

/// Create a memfd according to configuration.
/// Create a [`Memfd`] according to configuration.
///
/// [`Memfd`]: Memfd
pub fn create<T: AsRef<str>>(&self, name: T) -> Result<Memfd, crate::Error> {
let cname =
ffi::CString::new(name.as_ref()).map_err(crate::Error::NameCStringConversion)?;
let name_ptr = cname.as_ptr();
let flags = self.bitflags();

// UNSAFE(lucab): name_ptr points to memory owned by cname.
let r = unsafe { libc::syscall(libc::SYS_memfd_create, name_ptr, flags) };
if r < 0 {
return Err(crate::Error::Create(std::io::Error::last_os_error()));
};

// UNSAFE(lucab): returned from kernel, checked for non-negative value.
let mfd = unsafe { Memfd::from_raw_fd(r as RawFd) };
Ok(mfd)
// SAFETY: A syscall is being invoked. It has soundness implications – in particular
// `name_ptr` must be pointing to a valid null-terminated string. We construct a `CString`
// in this `unsafe` block, ensuring both all invariants for the `name_ptr`.
//
// Furthermore `from_raw_fd` requires a valid file descriptor representing a `memfd`. This
// is true by definition as we obtain said file descriptor from `memfd_create` syscall and
// check the result for errors.
unsafe {
let cname =
ffi::CString::new(name.as_ref()).map_err(crate::Error::NameCStringConversion)?;
let name_ptr = cname.as_ptr();
let fd = memfd_create(name_ptr, flags);
if fd < 0 {
return Err(crate::Error::Create(std::io::Error::last_os_error()));
}
Ok(Memfd::from_raw_fd(fd))
}
}
}

Expand Down Expand Up @@ -137,13 +145,13 @@ pub struct Memfd {
}

impl Memfd {
/// Try to convert a `File` object into a `Memfd`.
/// Try to convert a [`File`] object into a `Memfd`.
///
/// This function consumes the ownership of the specified `File`. If the underlying
/// file-descriptor is compatible with memfd/sealing, a `Memfd` object is returned.
/// Otherwise the supplied `File` is returned for further usage.
///
/// This requires transferring ownership of the `File`.
/// If the underlying file-descriptor is compatible with
/// memfd/sealing, it returns a proper `Memfd` object,
/// otherwise it transfers back ownership of the original
/// `File` for further usage.
/// [`File`]: fs::File
pub fn try_from_file(fp: fs::File) -> either::Either<Self, fs::File> {
// Check if the fd supports F_GET_SEALS;
// if so, it is safely compatible with `Memfd`.
Expand All @@ -153,23 +161,27 @@ impl Memfd {
}
}

/// Return a `File` object for this memfd.
/// Return a reference to the backing [`File`].
///
/// [`File`]: fs::File
pub fn as_file(&self) -> &fs::File {
&self.file
}

/// Consume this `Memfd`, returning the underlying `File`.
/// Convert `Memfd` to the backing [`File`].
///
/// [`File`]: fs::File
pub fn into_file(self) -> fs::File {
self.file
}

/// Return the current set of seals.
/// Obtain the current set of seals for the `Memfd`.
pub fn seals(&self) -> Result<sealing::SealsHashSet, crate::Error> {
let flags = Self::file_get_seals(&self.file)?;
Ok(sealing::bitflags_to_seals(flags))
}

/// Add a single seal to the existing set of seals.
/// Add a seal to the existing set of seals.
pub fn add_seal(&self, seal: sealing::FileSeal) -> Result<(), crate::Error> {
use std::iter::FromIterator;

Expand All @@ -192,21 +204,32 @@ impl Memfd {
/// Return the current sealing bitflags.
fn file_get_seals(fp: &fs::File) -> Result<u64, crate::Error> {
let fd = fp.as_raw_fd();
// UNSAFE(lucab): required syscall.
let r = unsafe { libc::syscall(libc::SYS_fcntl, fd, libc::F_GET_SEALS) };
// SAFETY: The syscall called has no soundness implications (i.e. does not mess with
// process memory in weird ways, checks its arguments for correctness, etc.). Furthermore
// due to invariants of `Memfd` this syscall is provided a valid file descriptor.
let r = unsafe {
libc::syscall(libc::SYS_fcntl, fd, libc::F_GET_SEALS)
};
if r < 0 {
return Err(crate::Error::GetSeals(std::io::Error::last_os_error()));
};

Ok(r as u64)
}
}

/// Assemble a `File` object from a raw file-descriptor.
impl FromRawFd for Memfd {
/// Convert a raw file-descriptor to a [`Memfd`].
///
/// This function consumes ownership of the specified file descriptor. `Memfd` will take
/// responsibility for closing it when the object goes out of scope.
///
/// # Safety
///
/// `fd` must be a valid file descriptor representing a memfd file.
///
/// Safety: `fd` must be a valid file-descriptor for the calling
/// process at the time of invocation.
unsafe fn from_raw_fd(fd: RawFd) -> Self {
/// [`Memfd`]: Memfd
unsafe fn from_raw_fd(fd: RawFd) -> Memfd {
let file = fs::File::from_raw_fd(fd);
Self { file }
Memfd { file }
}
}
28 changes: 14 additions & 14 deletions src/nr.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/* from <sys/memfd.h> */

pub const MFD_CLOEXEC: u32 = 1;
pub const MFD_ALLOW_SEALING: u32 = 2;
pub const MFD_HUGETLB: u32 = 4;
pub(super) const MFD_CLOEXEC: u32 = 1;
pub(super) const MFD_ALLOW_SEALING: u32 = 2;
pub(super) const MFD_HUGETLB: u32 = 4;

/* from <asm-generic/hugetlb_encode.h> */

pub const MFD_HUGE_SHIFT: u32 = 26;
pub(super) const MFD_HUGE_SHIFT: u32 = 26;

pub const MFD_HUGE_64KB: u32 = 16 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_512KB: u32 = 19 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_1MB: u32 = 20 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_2MB: u32 = 21 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_8MB: u32 = 23 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_16MB: u32 = 24 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_256MB: u32 = 28 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_1GB: u32 = 30 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_2GB: u32 = 31 << MFD_HUGE_SHIFT;
pub const MFD_HUGE_16GB: u32 = 34 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_64KB: u32 = 16 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_512KB: u32 = 19 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_1MB: u32 = 20 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_2MB: u32 = 21 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_8MB: u32 = 23 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_16MB: u32 = 24 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_256MB: u32 = 28 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_1GB: u32 = 30 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_2GB: u32 = 31 << MFD_HUGE_SHIFT;
pub(super) const MFD_HUGE_16GB: u32 = 34 << MFD_HUGE_SHIFT;
13 changes: 11 additions & 2 deletions src/sealing.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
use libc;
use std::collections::HashSet;

/// An `HashSet` specialized on `FileSeal`.
pub type SealsHashSet = HashSet<FileSeal>;

/// Seal that can be applied to a `Memfd`.
/// Seal that can be applied to a [`Memfd`].
///
/// [`Memfd`]: crate::Memfd
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FileSeal {
/// File cannot be reduced in size.
///
/// Corresponds to `F_SEAL_SHRINK`.
SealShrink,
/// File cannot be grown in size.
///
/// Corresponds to `F_SEAL_GROW`.
SealGrow,
/// File cannot be written.
///
/// Corresponds to `F_SEAL_WRITE`.
SealWrite,
/// File sealing cannot be further manipulated.
///
/// Corresponds to `F_SEAL_SEAL`.
SealSeal,
}

Expand Down

0 comments on commit edc4196

Please sign in to comment.