1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
use crate::error::{Error, Result};
use crate::raw;
use crate::{IoctlFlags, Uffd};
use bitflags::bitflags;
use nix::errno::Errno;
use std::fs::{File, OpenOptions};
use std::io::ErrorKind;
use std::os::fd::AsRawFd;
const UFFD_DEVICE_PATH: &str = "/dev/userfaultfd";
cfg_if::cfg_if! {
if #[cfg(any(feature = "linux5_7", feature = "linux4_14"))] {
bitflags! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
const SIGBUS = raw::UFFD_FEATURE_SIGBUS;
const THREAD_ID = raw::UFFD_FEATURE_THREAD_ID;
}
}
} else {
bitflags! {
/// Used with `UffdBuilder` to determine which features are available in the current kernel.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FeatureFlags: u64 {
const PAGEFAULT_FLAG_WP = raw::UFFD_FEATURE_PAGEFAULT_FLAG_WP;
const EVENT_FORK = raw::UFFD_FEATURE_EVENT_FORK;
const EVENT_REMAP = raw::UFFD_FEATURE_EVENT_REMAP;
const EVENT_REMOVE = raw::UFFD_FEATURE_EVENT_REMOVE;
const MISSING_HUGETLBFS = raw::UFFD_FEATURE_MISSING_HUGETLBFS;
const MISSING_SHMEM = raw::UFFD_FEATURE_MISSING_SHMEM;
const EVENT_UNMAP = raw::UFFD_FEATURE_EVENT_UNMAP;
}
}
}
}
/// A builder for initializing `Uffd` objects.
///
/// ```
/// use userfaultfd::UffdBuilder;
///
/// let uffd = UffdBuilder::new()
/// .close_on_exec(true)
/// .non_blocking(true)
/// .user_mode_only(true)
/// .create();
/// assert!(uffd.is_ok());
/// ```
pub struct UffdBuilder {
close_on_exec: bool,
non_blocking: bool,
user_mode_only: bool,
req_features: FeatureFlags,
req_ioctls: IoctlFlags,
}
impl UffdBuilder {
/// Create a new builder with no required features or ioctls, `close_on_exec` and
/// `non_blocking` both set to `false`, and `user_mode_only` set to `true`.
pub fn new() -> UffdBuilder {
UffdBuilder {
close_on_exec: false,
non_blocking: false,
user_mode_only: true,
req_features: FeatureFlags::empty(),
req_ioctls: IoctlFlags::empty(),
}
}
/// Enable the close-on-exec flag for the new userfaultfd object (see the description of
/// `O_CLOEXEC` in [`open(2)`](http://man7.org/linux/man-pages/man2/open.2.html)).
pub fn close_on_exec(&mut self, close_on_exec: bool) -> &mut Self {
self.close_on_exec = close_on_exec;
self
}
/// Enable non-blocking operation for the userfaultfd object.
///
/// If this is set to `false`, `Uffd::read_event()` will block until an event is available to
/// read. Otherwise, it will immediately return `None` if no event is available.
pub fn non_blocking(&mut self, non_blocking: bool) -> &mut Self {
self.non_blocking = non_blocking;
self
}
/// Enable user-mode only flag for the userfaultfd object.
///
/// If set to `false`, the process must have the `CAP_SYS_PTRACE` capability starting with Linux 5.11
/// or object creation will fail with EPERM. When set to `true`, userfaultfd can't be used
/// to handle kernel-mode page faults such as when kernel tries copying data to userspace.
///
/// When used with kernels older than 5.11, this has no effect; the process doesn't need
/// `CAP_SYS_PTRACE` and can handle kernel-mode page faults.
pub fn user_mode_only(&mut self, user_mode_only: bool) -> &mut Self {
self.user_mode_only = user_mode_only;
self
}
/// Add a requirement that a particular feature or set of features is available.
///
/// If a required feature is unavailable, `UffdBuilder.create()` will return an error.
pub fn require_features(&mut self, feature: FeatureFlags) -> &mut Self {
self.req_features |= feature;
self
}
/// Add a requirement that a particular ioctl or set of ioctls is available.
///
/// If a required ioctl is unavailable, `UffdBuilder.create()` will return an error.
pub fn require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self {
self.req_ioctls |= ioctls;
self
}
fn uffd_from_dev(&self, file: &mut File, flags: i32) -> Result<Uffd> {
match unsafe { raw::new_uffd(file.as_raw_fd(), flags) } {
Err(err) => Err(err.into()),
Ok(fd) => Ok(Uffd { fd }),
}
}
fn uffd_from_syscall(&self, flags: i32) -> Result<Uffd> {
let fd = match Errno::result(unsafe { raw::userfaultfd(flags) }) {
Ok(fd) => fd,
// setting the USER_MODE_ONLY flag on kernel pre-5.11 causes it to return EINVAL.
// If the user asks for the flag, we first try with it set, and if kernel gives
// EINVAL we try again without the flag set.
Err(Errno::EINVAL) if self.user_mode_only => Errno::result(unsafe {
raw::userfaultfd(flags & !raw::UFFD_USER_MODE_ONLY as i32)
})?,
Err(e) => return Err(e.into()),
};
// Wrap the fd up so that a failure in this function body closes it with the drop.
Ok(Uffd { fd })
}
// Try to get a UFFD file descriptor using `/dev/userfaultfd`. If that fails
// fall back to calling the system call.
fn open_file_descriptor(&self, flags: i32) -> Result<Uffd> {
// If `/dev/userfaultfd` exists we'll try to get the file descriptor from it. If the file
// doesn't exist we will fall back to calling the system call. This means, that if the
// device exists but the calling process does not have access rights to it, this will fail,
// i.e. we will not fall back to calling the system call.
match OpenOptions::new()
.read(true)
.write(true)
.open(UFFD_DEVICE_PATH)
{
Ok(mut file) => self.uffd_from_dev(&mut file, flags),
Err(err) if err.kind() == ErrorKind::NotFound => self.uffd_from_syscall(flags),
Err(err) => Err(Error::OpenDevUserfaultfd(err)),
}
}
/// Create a `Uffd` object with the current settings of this builder.
pub fn create(&self) -> Result<Uffd> {
// first do the syscall to get the file descriptor
let mut flags = 0;
if self.close_on_exec {
flags |= libc::O_CLOEXEC;
}
if self.non_blocking {
flags |= libc::O_NONBLOCK;
}
if self.user_mode_only {
flags |= raw::UFFD_USER_MODE_ONLY as i32;
}
let uffd = self.open_file_descriptor(flags)?;
// then do the UFFDIO_API ioctl to set up and ensure features and other ioctls are available
let mut api = raw::uffdio_api {
api: raw::UFFD_API,
features: self.req_features.bits(),
ioctls: 0,
};
unsafe {
raw::api(uffd.fd, &mut api as *mut raw::uffdio_api)?;
}
let supported = IoctlFlags::from_bits_retain(api.ioctls);
if !supported.contains(self.req_ioctls) {
Err(Error::UnsupportedIoctls(supported))
} else {
Ok(uffd)
}
}
}