Skip to main content

userfaultfd/
event.rs

1use crate::error::{Error, Result};
2use crate::raw;
3use crate::Uffd;
4use libc::c_void;
5#[cfg(feature = "linux4_14")]
6use nix::unistd::Pid;
7use std::os::unix::io::{FromRawFd, RawFd};
8
9/// Whether a page fault event was for a read or write.
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub enum ReadWrite {
12    Read,
13    Write,
14}
15
16/// The kind of fault for a page fault event.
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum FaultKind {
19    /// The fault was a read or write on a missing page.
20    Missing,
21    /// The fault was a write on a write-protected page.
22    #[cfg(feature = "linux5_7")]
23    WriteProtected,
24    // The fault was a minor page fault, meaning the page was present in the page cache,
25    // but the userspace page table entry was missing.
26    #[cfg(feature = "linux5_13")]
27    Minor,
28}
29
30/// Events from the userfaultfd object that are read by `Uffd::read_event()`.
31#[derive(Debug)]
32pub enum Event {
33    /// A pagefault event.
34    Pagefault {
35        /// The kind of fault.
36        kind: FaultKind,
37        /// Whether the fault is on a read or a write.
38        rw: ReadWrite,
39        /// The address that triggered the fault.
40        addr: *mut c_void,
41        /// The thread that triggered the fault, if [`FeatureFlags::THREAD_ID`] is enabled.
42        ///
43        /// If the thread ID feature is not enabled, the value of this field is undefined. It would
44        /// not be undefined behavior to use it, strictly speaking, but the [`Pid`] will not
45        /// necessarily point to a real thread.
46        ///
47        /// This requires this crate to be compiled with the `linux4_14` feature.
48        #[cfg(feature = "linux4_14")]
49        thread_id: Pid,
50    },
51    /// Generated when the faulting process invokes `fork(2)` (or `clone(2)` without the `CLONE_VM`
52    /// flag).
53    Fork {
54        /// The `Uffd` object created for the child by `fork(2)`
55        uffd: Uffd,
56    },
57    /// Generated when the faulting process invokes `mremap(2)`.
58    Remap {
59        /// The original address of the memory range that was remapped.
60        from: *mut c_void,
61        /// The new address of the memory range that was remapped.
62        to: *mut c_void,
63        /// The original length of the memory range that was remapped.
64        len: usize,
65    },
66    /// Generated when the faulting process invokes `madvise(2)` with `MADV_DONTNEED` or
67    /// `MADV_REMOVE` advice.
68    Remove {
69        /// The start address of the memory range that was freed.
70        start: *mut c_void,
71        /// The end address of the memory range that was freed.
72        end: *mut c_void,
73    },
74    /// Generated when the faulting process unmaps a meomry range, either explicitly using
75    /// `munmap(2)` or implicitly during `mmap(2)` or `mremap(2)`.
76    Unmap {
77        /// The start address of the memory range that was unmapped.
78        start: *mut c_void,
79        /// The end address of the memory range that was unmapped.
80        end: *mut c_void,
81    },
82}
83
84impl Event {
85    pub(crate) fn from_uffd_msg(msg: &raw::uffd_msg) -> Result<Event> {
86        match msg.event {
87            raw::UFFD_EVENT_PAGEFAULT => {
88                let pagefault = unsafe { msg.arg.pagefault };
89
90                #[allow(unused_mut)]
91                let mut kind = FaultKind::Missing;
92
93                // The below two flags are mutually exclusive (it does not make sense
94                // to have a minor fault that is a write-protect fault at the same time.
95                #[cfg(feature = "linux5_7")]
96                if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WP != 0 {
97                    kind = FaultKind::WriteProtected;
98                }
99
100                #[cfg(feature = "linux5_13")]
101                if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_MINOR != 0 {
102                    kind = FaultKind::Minor
103                }
104
105                let rw = if pagefault.flags & raw::UFFD_PAGEFAULT_FLAG_WRITE == 0 {
106                    ReadWrite::Read
107                } else {
108                    ReadWrite::Write
109                };
110                // Converting the ptid to i32 is safe because the maximum pid in
111                // Linux is 2^22, which is about 4 million.
112                //
113                // Reference:
114                //   https://github.com/torvalds/linux/blob/2d338201d5311bcd79d42f66df4cecbcbc5f4f2c/include/linux/threads.h
115                #[cfg(feature = "linux4_14")]
116                let thread_id = Pid::from_raw(unsafe { pagefault.feat.ptid } as i32);
117                Ok(Event::Pagefault {
118                    kind,
119                    rw,
120                    addr: pagefault.address as *mut c_void,
121                    #[cfg(feature = "linux4_14")]
122                    thread_id,
123                })
124            }
125            raw::UFFD_EVENT_FORK => {
126                let fork = unsafe { msg.arg.fork };
127                Ok(Event::Fork {
128                    uffd: unsafe { Uffd::from_raw_fd(fork.ufd as RawFd) },
129                })
130            }
131            raw::UFFD_EVENT_REMAP => {
132                let remap = unsafe { msg.arg.remap };
133                Ok(Event::Remap {
134                    from: remap.from as *mut c_void,
135                    to: remap.to as *mut c_void,
136                    len: remap.len as usize,
137                })
138            }
139            raw::UFFD_EVENT_REMOVE => {
140                let remove = unsafe { msg.arg.remove };
141                Ok(Event::Remove {
142                    start: remove.start as *mut c_void,
143                    end: remove.end as *mut c_void,
144                })
145            }
146            raw::UFFD_EVENT_UNMAP => {
147                let remove = unsafe { msg.arg.remove };
148                Ok(Event::Unmap {
149                    start: remove.start as *mut c_void,
150                    end: remove.end as *mut c_void,
151                })
152            }
153            _ => Err(Error::UnrecognizedEvent(msg.event)),
154        }
155    }
156}