1 use crate::error::{Error, Result};
2 use crate::raw;
3 use crate::{IoctlFlags, Uffd};
4 use bitflags::bitflags;
5 use nix::errno::Errno;
6 use std::fs::{File, OpenOptions};
7 use std::io::ErrorKind;
8 use std::os::fd::AsRawFd;
9 
10 const UFFD_DEVICE_PATH: &str = "/dev/userfaultfd";
11 
12 cfg_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 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 /// ```
57 pub 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 
65 impl 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`.
new() -> UffdBuilder68     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)).
close_on_exec(&mut self, close_on_exec: bool) -> &mut Self80     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.
non_blocking(&mut self, non_blocking: bool) -> &mut Self89     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.
user_mode_only(&mut self, user_mode_only: bool) -> &mut Self102     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.
require_features(&mut self, feature: FeatureFlags) -> &mut Self110     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.
require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self118     pub fn require_ioctls(&mut self, ioctls: IoctlFlags) -> &mut Self {
119         self.req_ioctls |= ioctls;
120         self
121     }
122 
uffd_from_dev(&self, file: &mut File, flags: i32) -> Result<Uffd>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 
uffd_from_syscall(&self, flags: i32) -> Result<Uffd>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.
open_file_descriptor(&self, flags: i32) -> Result<Uffd>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.
create(&self) -> Result<Uffd>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 =
191             IoctlFlags::from_bits(api.ioctls).ok_or(Error::UnrecognizedIoctls(api.ioctls))?;
192         if !supported.contains(self.req_ioctls) {
193             Err(Error::UnsupportedIoctls(supported))
194         } else {
195             Ok(uffd)
196         }
197     }
198 }
199