Skip to main content

kernel_abi_tools/
local.rs

1//! Runs a program on the host under a seccomp filter that simulates a kernel.
2//!
3//! No container runtime and no root are needed: the child sets
4//! `no_new_privs`, installs the filter from [`crate::filter`], and executes
5//! the program. Only the kernel is simulated; the program still uses the
6//! host's libc and userland. Inside the filtered process tree, setuid
7//! programs (such as `sudo`) cannot gain privileges.
8
9use crate::Target;
10
11/// Runs `program` with `args` under the filter for `target` and returns its
12/// exit status (128 if it was killed by a signal). The current directory and
13/// environment are inherited. Suitable for a Cargo runner.
14#[cfg(target_os = "linux")]
15pub fn run(target: &Target, program: &str, args: &[String]) -> Result<i32, String> {
16    use crate::filter::{install, program as filter_program};
17    use std::os::unix::process::CommandExt;
18    use std::process::Command;
19
20    if !cfg!(target_arch = "x86_64") {
21        return Err("the local runner simulates x86_64 kernels only".to_string());
22    }
23    let insns = filter_program(target);
24    let mut cmd = Command::new(program);
25    cmd.args(args);
26    // SAFETY: the closure only calls prctl (async-signal-safe) on data built
27    // before the fork; it does not allocate.
28    unsafe {
29        cmd.pre_exec(move || install::apply(&install::Prog::new(&insns)));
30    }
31    let status = cmd
32        .status()
33        .map_err(|e| format!("cannot start {program} under the seccomp filter: {e}"))?;
34    Ok(status.code().unwrap_or(128))
35}
36
37/// The local runner needs Linux; elsewhere it reports why it cannot run.
38#[cfg(not(target_os = "linux"))]
39pub fn run(_target: &Target, _program: &str, _args: &[String]) -> Result<i32, String> {
40    Err("the local runner needs a Linux host; use the container runner".to_string())
41}
42
43#[cfg(all(test, target_os = "linux", target_arch = "x86_64"))]
44mod tests {
45    use super::*;
46    use crate::KernelVersion;
47
48    #[test]
49    fn runs_a_program_under_the_filter() {
50        let target = Target::kernel(KernelVersion::parse("3.10").unwrap());
51        let ok = run(&target, "/bin/sh", &["-c".into(), "exit 7".into()]);
52        assert_eq!(ok, Ok(7));
53        assert!(run(&target, "/no/such/program", &[]).is_err());
54    }
55}