| use crate::error::{Error, Result}; |
| use crate::raw; |
| use crate::Uffd; |
| use libc::c_void; |
| #[cfg(feature = "linux4_14")] |
| use nix::unistd::Pid; |
| use std::os::unix::io::{FromRawFd, RawFd}; |
| |
| /// Whether a page fault event was for a read or write. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum ReadWrite { |
| Read, |
| Write, |
| } |
| |
| /// The kind of fault for a page fault event. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum FaultKind { |
| /// The fault was a read or write on a missing page. |
| Missing, |
| /// The fault was a write on a write-protected page. |
| #[cfg(feature = "linux5_7")] |
| WriteProtected, |
| } |
| |
| /// Events from the userfaultfd object that are read by `Uffd::read_event()`. |
| #[derive(Debug)] |
| pub enum Event { |
| /// A pagefault event. |
| Pagefault { |
| /// The kind of fault. |
| kind: FaultKind, |
| /// Whether the fault is on a read or a write. |
| rw: ReadWrite, |
| /// The address that triggered the fault. |
| addr: *mut c_void, |
| /// The thread that triggered the fault, if [`FeatureFlags::THREAD_ID`] is enabled. |
| /// |
| /// If the thread ID feature is not enabled, the value of this field is undefined. It would |
| /// not be undefined behavior to use it, strictly speaking, but the [`Pid`] will not |
| /// necessarily point to a real thread. |
| /// |
| /// This requires this crate to be compiled with the `linux4_14` feature. |
| #[cfg(feature = "linux4_14")] |
| thread_id: Pid, |
| }, |
| /// Generated when the faulting process invokes `fork(2)` (or `clone(2)` without the `CLONE_VM` |
| /// flag). |
| Fork { |
| /// The `Uffd` object created for the child by `fork(2)` |
| uffd: Uffd, |
| }, |
| /// Generated when the faulting process invokes `mremap(2)`. |
| Remap { |
| /// The original address of the memory range that was remapped. |
| from: *mut c_void, |
| /// The new address of the memory range that was remapped. |
| to: *mut c_void, |
| /// The original length of the memory range that was remapped. |
| len: usize, |
| }, |
| /// Generated when the faulting process invokes `madvise(2)` with `MADV_DONTNEED` or |
| /// `MADV_REMOVE` advice. |
| Remove { |
| /// The start address of the memory range that was freed. |
| start: *mut c_void, |
| /// The end address of the memory range that was freed. |
| end: *mut c_void, |
| }, |
| /// Generated when the faulting process unmaps a meomry range, either explicitly using |
| /// `munmap(2)` or implicitly during `mmap(2)` or `mremap(2)`. |
| Unmap { |
| /// The start address of the memory range that was unmapped. |
| start: *mut c_void, |
| /// The end address of the memory range that was unmapped. |
| end: *mut c_void, |
| }, |
| } |
| |
| impl Event { |
| pub(crate) fn from_uffd_msg(msg: &raw::uffd_msg) -> Result<Event> { |
| match msg.event { |
| raw::UFFD_EVENT_PAGEFAULT => { |
| let pagefault = unsafe { msg.arg.pagefault }; |
| cfg_if::cfg_if!( |
| if #[cfg(feature = "linux5_7")] { |
| let kind = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 { |
| FaultKind::WriteProtected |
| } else { |
| FaultKind::Missing |
| }; |
| } else { |
| let kind = FaultKind::Missing; |
| } |
| ); |
| |
| let rw = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WRITE == 0 { |
| ReadWrite::Read |
| } else { |
| ReadWrite::Write |
| }; |
| // Converting the ptid to i32 is safe because the maximum pid in |
| // Linux is 2^22, which is about 4 million. |
| // |
| // Reference: |
| // https://github.com/torvalds/linux/blob/2d338201d5311bcd79d42f66df4cecbcbc5f4f2c/include/linux/threads.h |
| #[cfg(feature = "linux4_14")] |
| let thread_id = Pid::from_raw(unsafe { pagefault.feat.ptid } as i32); |
| Ok(Event::Pagefault { |
| kind, |
| rw, |
| addr: pagefault.address as *mut c_void, |
| #[cfg(feature = "linux4_14")] |
| thread_id, |
| }) |
| } |
| raw::UFFD_EVENT_FORK => { |
| let fork = unsafe { msg.arg.fork }; |
| Ok(Event::Fork { |
| uffd: unsafe { Uffd::from_raw_fd(fork.ufd as RawFd) }, |
| }) |
| } |
| raw::UFFD_EVENT_REMAP => { |
| let remap = unsafe { msg.arg.remap }; |
| Ok(Event::Remap { |
| from: remap.from as *mut c_void, |
| to: remap.to as *mut c_void, |
| len: remap.len as usize, |
| }) |
| } |
| raw::UFFD_EVENT_REMOVE => { |
| let remove = unsafe { msg.arg.remove }; |
| Ok(Event::Remove { |
| start: remove.start as *mut c_void, |
| end: remove.end as *mut c_void, |
| }) |
| } |
| raw::UFFD_EVENT_UNMAP => { |
| let remove = unsafe { msg.arg.remove }; |
| Ok(Event::Unmap { |
| start: remove.start as *mut c_void, |
| end: remove.end as *mut c_void, |
| }) |
| } |
| _ => Err(Error::UnrecognizedEvent(msg.event)), |
| } |
| } |
| } |