process_consistency 0.5.0

Hash your executable memory to ensure it stays the same
Documentation
use std::{
    fs::File,
    io::{BufRead, BufReader},
    path::Path,
};

use crate::{error::Error, Region};

pub fn get_executable_regions(
    skip_libs: bool,
    include_writable_code: bool,
) -> Result<Vec<Region>, Error> {
    let mut regions = vec![];
    let path = std::path::Path::new("/proc/self/maps");
    let file = File::open(path).map_err(|e| Error::ProcFsUnavailableError {
        source: e,
        path: path.to_owned(),
    })?;

    let filter = if skip_libs {
        std::env::current_exe().ok()
    } else {
        None
    };

    for line in BufReader::new(file).lines() {
        let line = line.map_err(|e| Error::ProcFsUnavailableError {
            source: e,
            path: path.to_owned(),
        })?;
        let segments: Vec<_> = line.split_whitespace().collect();
        if !segments[1].starts_with("r-x")
            && !(include_writable_code && segments[1].starts_with("rwx"))
        {
            continue;
        }
        let (start, end) = segments[0]
            .split_once('-')
            .ok_or(Error::ProcFsFormatError {
                path: path.to_owned(),
            })?;
        let start = usize::from_str_radix(start, 16).map_err(|_| Error::ProcFsFormatError {
            path: path.to_owned(),
        })? as *const u8;
        let end = usize::from_str_radix(end, 16).map_err(|_| Error::ProcFsFormatError {
            path: path.to_owned(),
        })? as *const u8;

        let source = segments
            .get(5)
            .copied()
            .ok_or(Error::ProcFsFormatError {
                path: path.to_owned(),
            })?
            .to_owned();

        if let Some(filter_path) = &filter {
            if Path::new(&source) != filter_path {
                continue;
            }
        }

        regions.push(Region { start, end, source })
    }
    Ok(regions)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_all_combinations() {
        println!("{:#?}", get_executable_regions(false, false));
        println!("----");
        println!("{:#?}", get_executable_regions(true, false));
        assert!(get_executable_regions(false, false).unwrap().len() > 2);
        assert!(get_executable_regions(false, true).unwrap().len() > 2);
        assert!(get_executable_regions(true, false).unwrap().len() <= 2);
        assert!(get_executable_regions(true, true).unwrap().len() <= 2);
    }
}