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