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