hermit-detcore 0.4.0

Detcore: the deterministic scheduler and syscall determinization core of the Hermit execution engine.
Documentation
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

pub use procfs::process::MMapPath;
pub use procfs::process::MemoryMap;
use reverie::Guest;
use reverie::Pid;
use reverie::Tool;
use reverie::syscalls::Addr;
use reverie::syscalls::MemoryAccess;

use crate::Digest;

fn display_pathname(p: &MMapPath) -> String {
    match p {
        MMapPath::Vdso => String::from("[vsdo]"),
        MMapPath::Stack => String::from("[stack]"),
        MMapPath::TStack(tid) => format!("[tstack:{}]", tid),
        MMapPath::Vvar => String::from("[vvar]"),
        MMapPath::Vsyscall => String::from("[syscalls]"),
        MMapPath::Heap => String::from("[heap]"),
        MMapPath::Other(s) => format!("[other: {}]", s),
        MMapPath::Anonymous => String::from("[annonymous]"),
        MMapPath::Path(s) => s.display().to_string(),
        MMapPath::Rollup => String::from("[rollup]"),
        MMapPath::Vsys(vsys) => vsys.to_string(),
    }
}

pub fn display(map: &MemoryMap) -> String {
    display_as(map, &display_pathname(&map.pathname))
}

/// Render a mapping with an explicit pathname column.
///
/// Used to report a region that *is* the guest heap but which the kernel did
/// not label `[heap]`, so the record is textually comparable with the labelled
/// one another backend produces. Every other column is the mapping's real
/// procfs data.
pub fn display_as(map: &MemoryMap, pathname: &str) -> String {
    display_range_as(map, map.address.0, map.address.1, pathname)
}

/// Render a mapping's real procfs columns against an EXPLICIT address range.
///
/// The two address columns come from `start`/`end`; every other column is the
/// mapping's real procfs data. Used where the region being reported is a SUBSET
/// of the mapping that contains it -- the observed program break inside its
/// enclosing anonymous mapping -- so the record's extent is the region actually
/// hashed rather than the arena that happens to hold it.
pub fn display_range_as(map: &MemoryMap, start: u64, end: u64, pathname: &str) -> String {
    format!(
        "{:#x}-{:#x} {:?} {:x} {:x}:{:x} {} {}",
        start, end, map.perms, map.offset, map.dev.0, map.dev.1, map.inode, pathname
    )
}

fn map_error(err: procfs::ProcError) -> reverie::Error {
    match err {
        procfs::ProcError::Io(err, _) => reverie::Error::Io(err),
        err => reverie::Error::Tool(anyhow::anyhow!(err)),
    }
}

pub fn from_pid<F>(pid: Pid, filter: F) -> Result<Vec<MemoryMap>, reverie::Error>
where
    F: Fn(&MemoryMap) -> bool,
{
    match procfs::process::Process::new(pid.as_raw()) {
        Ok(process) => match process.maps() {
            Ok(mut maps) => {
                maps.0.retain(filter);
                Ok(maps.0)
            }
            Err(err) => Err(map_error(err)),
        },
        Err(err) => Err(map_error(err)),
    }
}

pub fn compute_hash<G, T: Tool>(guest: &mut G, map: &MemoryMap) -> Result<Digest, reverie::Error>
where
    G: Guest<T>,
{
    compute_hash_range(guest, map.address.0, map.address.1)
}

/// Hash the guest bytes in the half-open guest-virtual range `[start, end)`,
/// read through `guest.memory()`. Used for backend-reported memory regions
/// ([`reverie::Guest::detlog_memory_regions`]) where the range is a guest
/// address rather than an entry parsed from a host `/proc/<pid>/maps`.
pub fn compute_hash_range<G, T: Tool>(
    guest: &mut G,
    start: u64,
    end: u64,
) -> Result<Digest, reverie::Error>
where
    G: Guest<T>,
{
    let size = end.saturating_sub(start) as usize;
    let memory = guest.memory();
    let mut buf = vec![0; size];
    let start = Addr::<u8>::from_raw(start as usize).unwrap();
    memory.read_values(start, buf.as_mut_slice())?;
    Ok(Digest::new(buf.as_slice()))
}