use crate::{Error, Protection, Region, Result};
use core::mem::{MaybeUninit, size_of};
use core::ptr;
use libc::{
CTL_KERN, KERN_PROC_VMMAP, KVE_ET_COPYONWRITE, KVE_PROT_EXEC, KVE_PROT_READ, KVE_PROT_WRITE,
c_int, c_uint, getpid, kinfo_vmentry, sysctl,
};
pub struct QueryIter {
mib: [c_int; 3],
vmentry: kinfo_vmentry,
previous_boundary: usize,
upper_bound: usize,
}
impl QueryIter {
pub fn new(origin: *const (), size: usize) -> Result<QueryIter> {
Ok(QueryIter {
mib: [CTL_KERN, KERN_PROC_VMMAP, unsafe { getpid() }],
vmentry: unsafe { MaybeUninit::zeroed().assume_init() },
upper_bound: (origin as usize).saturating_add(size),
previous_boundary: 0,
})
}
pub fn upper_bound(&self) -> usize {
self.upper_bound
}
}
impl Iterator for QueryIter {
type Item = Result<Region>;
fn next(&mut self) -> Option<Self::Item> {
let mut len = size_of::<kinfo_vmentry>();
let result = unsafe {
sysctl(
self.mib.as_ptr(),
self.mib.len() as c_uint,
&mut self.vmentry as *mut _ as *mut _,
&mut len,
ptr::null_mut(),
0,
)
};
if result == -1 {
return Some(Err(Error::last_os_error()));
}
if len == 0 || self.vmentry.kve_end as usize == self.previous_boundary {
return None;
}
let region = Region {
base: ptr::with_exposed_provenance(self.vmentry.kve_start as usize),
protection: Protection::from_native(self.vmentry.kve_protection),
max_protection: Protection::from_native(self.vmentry.kve_max_protection),
shared: (self.vmentry.kve_etype & KVE_ET_COPYONWRITE) == 0,
size: (self.vmentry.kve_end - self.vmentry.kve_start) as _,
..Default::default()
};
self.vmentry.kve_start += 1;
self.previous_boundary = self.vmentry.kve_end as usize;
Some(Ok(region))
}
}
impl Protection {
fn from_native(protection: c_int) -> Self {
const MAPPINGS: &[(c_int, Protection)] = &[
(KVE_PROT_READ, Protection::READ),
(KVE_PROT_WRITE, Protection::WRITE),
(KVE_PROT_EXEC, Protection::EXECUTE),
];
MAPPINGS
.iter()
.filter(|(flag, _)| protection & *flag == *flag)
.fold(Protection::NONE, |acc, (_, prot)| acc | *prot)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn protection_flags_are_mapped_from_native() {
let rw = KVE_PROT_READ | KVE_PROT_WRITE;
let rwx = rw | KVE_PROT_EXEC;
assert_eq!(Protection::from_native(0), Protection::NONE);
assert_eq!(Protection::from_native(KVE_PROT_READ), Protection::READ);
assert_eq!(Protection::from_native(rw), Protection::READ_WRITE);
assert_eq!(Protection::from_native(rwx), Protection::READ_WRITE_EXECUTE);
}
}