proc-maps 0.4.0

Helper crate for getting virtual memory maps from processes
Documentation
#[allow(warnings)]
mod bindings;
mod protection;
mod ptrace;

use libc::{c_int, pid_t};
use std::convert::From;
use std::iter::Iterator;
use std::path::{Path, PathBuf};

use MapRangeImpl;

pub type Pid = pid_t;

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct MapRange {
    range_start: usize,
    range_end: usize,
    protection: c_int,
    offset: usize,
    vnode: usize,
    pathname: Option<PathBuf>,
}

impl MapRangeImpl for MapRange {
    fn size(&self) -> usize {
        self.range_end - self.range_start
    }
    fn start(&self) -> usize {
        self.range_start
    }
    fn filename(&self) -> Option<&Path> {
        self.pathname.as_deref()
    }
    fn is_read(&self) -> bool {
        self.protection & protection::VM_PROT_READ != 0
    }
    fn is_write(&self) -> bool {
        self.protection & protection::VM_PROT_WRITE != 0
    }
    fn is_exec(&self) -> bool {
        self.protection & protection::VM_PROT_EXECUTE != 0
    }
}

impl From<ptrace::VmEntry> for MapRange {
    fn from(vm_entry: ptrace::VmEntry) -> Self {
        Self {
            range_start: vm_entry.pve_start as usize,
            range_end: vm_entry.pve_end as usize,
            protection: vm_entry.pve_prot as _,
            offset: vm_entry.pve_offset as usize,
            vnode: vm_entry.pve_fileid as usize,
            pathname: vm_entry.pve_path,
        }
    }
}

pub fn get_process_maps(pid: Pid) -> std::io::Result<Vec<MapRange>> {
    let iter = ptrace::VmEntryIterator::new(pid)?;

    Ok(iter.map(MapRange::from).collect())
}

#[test]
fn test_write_xor_execute_policy() -> () {
    use std::process::Command;
    let mut child = Command::new("/bin/cat")
        .spawn()
        .expect("failed to execute /bin/cat");

    let maps = get_process_maps(child.id() as Pid).unwrap();

    child.kill().expect("failed to kill test process");

    assert!(maps.len() > 0, "No process maps were found");

    let write_and_exec_regions = maps.iter().any(|x| x.is_write() && x.is_exec());

    assert!(!write_and_exec_regions, "W^X violation!");
}