blob: 8ea75f212a7c9bdf8771619c38f5a72f6ee29cf3 [file] [log] [blame]
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)),
}
}
}