ureeves-userfaultfd 0.8.1

Rust bindings for the Linux userfaultfd functionality
Documentation
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)),
        }
    }
}