diff options
Diffstat (limited to 'src/event.rs')
-rw-r--r-- | src/event.rs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..8ea75f2 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,148 @@ +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)), + } + } +} |