1 //! A Linux mechanism for handling page faults in user space. 2 //! 3 //! The main way to interact with this library is to create a `Uffd` object with a `UffdBuilder`, 4 //! then use the methods of `Uffd` from a worker thread. 5 //! 6 //! See [`userfaultfd(2)`](http://man7.org/linux/man-pages/man2/userfaultfd.2.html) and 7 //! [`ioctl_userfaultfd(2)`](http://man7.org/linux/man-pages/man2/ioctl_userfaultfd.2.html) for more 8 //! details. 9 10 mod builder; 11 mod error; 12 mod event; 13 mod raw; 14 15 pub use crate::builder::{FeatureFlags, UffdBuilder}; 16 pub use crate::error::{Error, Result}; 17 pub use crate::event::Event; 18 19 use bitflags::bitflags; 20 use libc::{self, c_void}; 21 use nix::errno::Errno; 22 use nix::unistd::read; 23 use std::mem; 24 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; 25 26 /// The userfaultfd object. 27 /// 28 /// The userspace representation of the object is a file descriptor, so this type implements 29 /// `AsRawFd`, `FromRawFd`, and `IntoRawFd`. These methods should be used with caution, but can be 30 /// essential for using functions like `poll` on a worker thread. 31 #[derive(Debug)] 32 pub struct Uffd { 33 fd: RawFd, 34 } 35 36 impl Drop for Uffd { drop(&mut self)37 fn drop(&mut self) { 38 unsafe { libc::close(self.fd) }; 39 } 40 } 41 42 impl AsRawFd for Uffd { as_raw_fd(&self) -> RawFd43 fn as_raw_fd(&self) -> RawFd { 44 self.fd 45 } 46 } 47 48 impl IntoRawFd for Uffd { into_raw_fd(self) -> RawFd49 fn into_raw_fd(self) -> RawFd { 50 self.fd 51 } 52 } 53 54 impl FromRawFd for Uffd { from_raw_fd(fd: RawFd) -> Self55 unsafe fn from_raw_fd(fd: RawFd) -> Self { 56 Uffd { fd } 57 } 58 } 59 60 impl Uffd { 61 /// Register a memory address range with the userfaultfd object, and returns the `IoctlFlags` 62 /// that are available for the selected range. 63 /// 64 /// While the underlying `ioctl` call accepts mode flags, only one mode 65 /// (`UFFDIO_REGISTER_MODE_MISSING`) is currently supported. register(&self, start: *mut c_void, len: usize) -> Result<IoctlFlags>66 pub fn register(&self, start: *mut c_void, len: usize) -> Result<IoctlFlags> { 67 let mut register = raw::uffdio_register { 68 range: raw::uffdio_range { 69 start: start as u64, 70 len: len as u64, 71 }, 72 // this is the only mode currently supported 73 mode: raw::UFFDIO_REGISTER_MODE_MISSING, 74 ioctls: 0, 75 }; 76 unsafe { 77 raw::register(self.as_raw_fd(), &mut register as *mut raw::uffdio_register)?; 78 } 79 IoctlFlags::from_bits(register.ioctls).ok_or(Error::UnrecognizedIoctls(register.ioctls)) 80 } 81 82 /// Unregister a memory address range from the userfaultfd object. unregister(&self, start: *mut c_void, len: usize) -> Result<()>83 pub fn unregister(&self, start: *mut c_void, len: usize) -> Result<()> { 84 let mut range = raw::uffdio_range { 85 start: start as u64, 86 len: len as u64, 87 }; 88 unsafe { 89 raw::unregister(self.as_raw_fd(), &mut range as *mut raw::uffdio_range)?; 90 } 91 Ok(()) 92 } 93 94 /// Atomically copy a continuous memory chunk into the userfaultfd-registed range, and return 95 /// the number of bytes that were successfully copied. 96 /// 97 /// If `wake` is `true`, wake up the thread waiting for pagefault resolution on the memory 98 /// range. copy( &self, src: *const c_void, dst: *mut c_void, len: usize, wake: bool, ) -> Result<usize>99 pub unsafe fn copy( 100 &self, 101 src: *const c_void, 102 dst: *mut c_void, 103 len: usize, 104 wake: bool, 105 ) -> Result<usize> { 106 let mut copy = raw::uffdio_copy { 107 src: src as u64, 108 dst: dst as u64, 109 len: len as u64, 110 mode: if wake { 111 0 112 } else { 113 raw::UFFDIO_COPY_MODE_DONTWAKE 114 }, 115 copy: 0, 116 }; 117 118 let res = raw::copy(self.as_raw_fd(), &mut copy as *mut raw::uffdio_copy); 119 if let Err(e) = res { 120 if let Some(errno) = e.as_errno() { 121 Err(Error::CopyFailed(errno)) 122 } else { 123 Err(e.into()) 124 } 125 } else { 126 if copy.copy < 0 { 127 // shouldn't ever get here, as errno should be caught above 128 Err(Error::CopyFailed(Errno::from_i32(-copy.copy as i32))) 129 } else { 130 Ok(copy.copy as usize) 131 } 132 } 133 } 134 135 /// Zero out a memory address range registered with userfaultfd, and return the number of bytes 136 /// that were successfully zeroed. 137 /// 138 /// If `wake` is `true`, wake up the thread waiting for pagefault resolution on the memory 139 /// address range. zeropage(&self, start: *mut c_void, len: usize, wake: bool) -> Result<usize>140 pub unsafe fn zeropage(&self, start: *mut c_void, len: usize, wake: bool) -> Result<usize> { 141 let mut zeropage = raw::uffdio_zeropage { 142 range: raw::uffdio_range { 143 start: start as u64, 144 len: len as u64, 145 }, 146 mode: if wake { 147 0 148 } else { 149 raw::UFFDIO_ZEROPAGE_MODE_DONTWAKE 150 }, 151 zeropage: 0, 152 }; 153 154 let res = raw::zeropage(self.as_raw_fd(), &mut zeropage as &mut raw::uffdio_zeropage); 155 if let Err(e) = res { 156 if let Some(errno) = e.as_errno() { 157 Err(Error::ZeropageFailed(errno)) 158 } else { 159 Err(e.into()) 160 } 161 } else { 162 if zeropage.zeropage < 0 { 163 // shouldn't ever get here, as errno should be caught above 164 Err(Error::ZeropageFailed(Errno::from_i32( 165 -zeropage.zeropage as i32, 166 ))) 167 } else { 168 Ok(zeropage.zeropage as usize) 169 } 170 } 171 } 172 173 /// Wake up the thread waiting for pagefault resolution on the specified memory address range. wake(&self, start: *mut c_void, len: usize) -> Result<()>174 pub fn wake(&self, start: *mut c_void, len: usize) -> Result<()> { 175 let mut range = raw::uffdio_range { 176 start: start as u64, 177 len: len as u64, 178 }; 179 unsafe { 180 raw::wake(self.as_raw_fd(), &mut range as *mut raw::uffdio_range)?; 181 } 182 Ok(()) 183 } 184 185 /// Read an `Event` from the userfaultfd object. 186 /// 187 /// If the `Uffd` object was created with `non_blocking` set to `false`, this will block until 188 /// an event is successfully read (returning `Some(event)`, or an error is returned. 189 /// 190 /// If `non_blocking` was `true`, this will immediately return `None` if no event is ready to 191 /// read. 192 /// 193 /// Note that while this method doesn't require a mutable reference to the `Uffd` object, it 194 /// does consume bytes (thread-safely) from the underlying file descriptor. read_event(&self) -> Result<Option<Event>>195 pub fn read_event(&self) -> Result<Option<Event>> { 196 const MSG_SIZE: usize = mem::size_of::<raw::uffd_msg>(); 197 let mut buf = [0x00u8; MSG_SIZE]; 198 199 // read one uffd_msg at a time; maybe implement an iterator for handling many at once? 200 let res = read(self.as_raw_fd(), &mut buf); 201 202 match res { 203 Err(e) if e.as_errno() == Some(Errno::EAGAIN) => return Ok(None), 204 Err(e) => Err(e)?, 205 Ok(nread) => { 206 if nread == libc::EOF as usize { 207 return Err(Error::ReadEof); 208 } 209 if nread != MSG_SIZE { 210 return Err(Error::IncompleteMsg { 211 read: nread, 212 expected: MSG_SIZE, 213 }); 214 } 215 let msg = unsafe { *(buf.as_mut_ptr() as *mut raw::uffd_msg) }; 216 let event = Event::from_uffd_msg(&msg)?; 217 return Ok(Some(event)); 218 } 219 } 220 } 221 } 222 223 bitflags! { 224 /// Used with `UffdBuilder` and `Uffd::register()` to determine which operations are available. 225 pub struct IoctlFlags: u64 { 226 const REGISTER = 1 << raw::_UFFDIO_REGISTER; 227 const UNREGISTER = 1 << raw::_UFFDIO_UNREGISTER; 228 const WAKE = 1 << raw::_UFFDIO_WAKE; 229 const COPY = 1 << raw::_UFFDIO_COPY; 230 const ZEROPAGE = 1 << raw::_UFFDIO_ZEROPAGE; 231 const API = 1 << raw::_UFFDIO_API; 232 } 233 } 234