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///
15/// # Errors
16///
17/// Returns a message when the host is not x86_64 or `program` cannot be
18/// started, including when the filter cannot be installed.
19#[cfg(target_os = "linux")]
20pub fn run(target: &Target, program: &str, args: &[String]) -> Result<i32, String> {
21    use crate::filter::{install, program as filter_program};
22    use std::os::unix::process::CommandExt;
23    use std::process::Command;
24
25    if !cfg!(target_arch = "x86_64") {
26        return Err("the local runner simulates x86_64 kernels only".to_string());
27    }
28    let insns = filter_program(target);
29    let mut cmd = Command::new(program);
30    cmd.args(args);
31    // SAFETY: the closure only calls prctl (async-signal-safe) on data built
32    // before the fork; it does not allocate.
33    unsafe {
34        cmd.pre_exec(move || install::apply(&install::Prog::new(&insns)));
35    }
36    let status = cmd
37        .status()
38        .map_err(|e| format!("cannot start {program} under the seccomp filter: {e}"))?;
39    Ok(status.code().unwrap_or(128))
40}
41
42/// The local runner needs Linux; elsewhere it reports why it cannot run.
43///
44/// # Errors
45///
46/// Always returns a message saying a Linux host is needed.
47#[cfg(not(target_os = "linux"))]
48pub fn run(_target: &Target, _program: &str, _args: &[String]) -> Result<i32, String> {
49    Err("the local runner needs a Linux host; use the container runner".to_string())
50}
51
52#[cfg(all(test, target_os = "linux", target_arch = "x86_64"))]
53mod tests {
54    use super::*;
55    use crate::KernelVersion;
56
57    #[test]
58    fn runs_a_program_under_the_filter() {
59        let target = Target::kernel(KernelVersion::parse("3.10").unwrap());
60        let ok = run(&target, "/bin/sh", &["-c".into(), "exit 7".into()]);
61        assert_eq!(ok, Ok(7));
62        assert!(run(&target, "/no/such/program", &[]).is_err());
63    }
64}