profiles/tests/enforce.rs
// SPDX-License-Identifier: AGPL-3.0-only
//! Enforce each checked-in profile (`Mode::Enforce`, unknown call =
//! KILL_PROCESS) on its real release binary over its whole probe corpus:
//! every run must end with its `expect_exit`, never SIGSYS. Heimdall is a
//! defensive sandbox; the corpus attacks only Heimdall's own limits.
//! Run only with HEIMDALL_KERNEL_TESTS=1 (scripts/seccomp-tests.sh).
use std::path::Path;
use heimdall_probe::Workload;
use heimdall_profiles::ALL;
use heimdall_seccomp::bpf::Mode;
use heimdall_seccomp::launch::{Exit, Launch, Stdio};
use heimdall_seccomp::{Arch, Profile};
fn main() {
if std::env::var_os("HEIMDALL_KERNEL_TESTS").is_none_or(|v| v != "1") {
println!("enforce tests skipped: set HEIMDALL_KERNEL_TESTS=1 (scripts/seccomp-tests.sh)");
return;
}
let profiles = Path::new(env!("CARGO_MANIFEST_DIR"));
let mut failed = 0;
let mut ran = 0;
for e in ALL {
let p = Profile::from(e.profile);
if Some(p.arch) != Arch::native() {
continue;
}
let w = Workload::load(profiles, &p.workload).unwrap();
let bin = match w.binary(&heimdall_probe::ProbeOptions {
libc: Some(p.libc),
..Default::default()
}) {
Ok((b, _)) => b,
Err(err) => {
println!("skip {} {} {}: {err}", p.workload, p.arch, p.libc);
continue;
}
};
for r in &w.manifest.runs {
ran += 1;
let tmp =
std::env::temp_dir().join(format!("heim-enforce-{}-{ran}", std::process::id()));
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp).unwrap();
let expand = |s: &str| {
s.replace("{corpus}", &w.dir.join("corpus").display().to_string())
.replace("{dir}", &w.dir.display().to_string())
.replace("{tmp}", &tmp.display().to_string())
};
let mut l = Launch::new(&bin)
.args(r.args.iter().map(|a| expand(a)))
.env_clear()
.env("PATH", "/usr/bin:/bin")
.cwd(&tmp)
.stdin(Stdio::Null)
.stdout(Stdio::Null)
.stderr(Stdio::Null)
.profile(&p, &Mode::Enforce);
for (k, v) in w.manifest.env.iter().chain(r.env.iter()) {
l = l.env(k, v);
}
let exit = l.spawn().unwrap().wait().unwrap();
let want = Exit::Code(r.expect_exit.unwrap_or(0));
let ok = exit == want;
println!(
"test {} {} {} {} ... {} ({exit})",
p.workload,
p.arch,
p.libc,
r.name,
if ok { "ok" } else { "FAILED" }
);
if !ok {
failed += 1;
}
let _ = std::fs::remove_dir_all(&tmp);
}
}
println!("\nenforce tests: {} passed; {failed} failed", ran - failed);
if failed > 0 {
std::process::exit(1);
}
}