aboutsummaryrefslogtreecommitdiff
path: root/src/event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/event.rs')
-rw-r--r--src/event.rs148
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)),
+ }
+ }
+}