ureeves_userfaultfd/
builder.rs

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