Files
KF-OnlineJudge/judge/crates/sandbox/tests/seccomp_vm.rs
T

117 lines
4.3 KiB
Rust

#![cfg(target_os = "linux")]
mod support;
use kf_sandbox::{
artifacts::ArtifactRoot,
launcher::seccomp::FilteredTarget,
outcome::ExecutionStage,
spec::{ExecutionProfile, SpecPolicy},
};
use std::{fs, os::unix::fs::PermissionsExt, path::Path, process::Command};
fn check(profile: ExecutionProfile, name: &str) {
let root = support::Root::new();
let before = support::children();
let path = format!("/kf/seccomp-publications-{}", std::process::id());
fs::create_dir(&path).unwrap();
fs::set_permissions(&path, fs::Permissions::from_mode(0o700)).unwrap();
fs::write(format!("{path}/program-001"), b"fixed-non-task-payload").unwrap();
fs::write(format!("{path}/input-001"), b"private-stdin").unwrap();
let artifacts = ArtifactRoot::from_directory(
rustix::fs::open(
&path,
rustix::fs::OFlags::PATH | rustix::fs::OFlags::DIRECTORY | rustix::fs::OFlags::CLOEXEC,
rustix::fs::Mode::empty(),
)
.unwrap(),
)
.unwrap();
let mut spec = support::spec("seccomp-fixture", 64 << 20, 8).spec().clone();
spec.run_id = format!("seccomp-{name}");
spec.profile = profile;
if profile != ExecutionProfile::Native {
spec.argv = vec![
if profile == ExecutionProfile::CompileC {
"/usr/bin/gcc"
} else {
"/usr/bin/g++"
}
.into(),
"/work/main".into(),
];
}
let result = FilteredTarget::prepare(
&root.validated(),
&spec.validate(&SpecPolicy::default()).unwrap(),
&support::binary(),
&artifacts,
);
if std::env::var("KF_SANDBOX_DENY_SECCOMP").as_deref() == Ok("1") {
let Err(e) = result else {
panic!("kernel policy failure ignored")
};
assert_eq!(e.failure.stage, ExecutionStage::Seccomp);
assert_eq!(e.failure.errno, Some(1));
assert!(e.cleanup_failure.is_none());
println!("KF_SECCOMP_FAILURE_PROOF real_eperm=1 no_ack=1 target_reaped=1 lease_released=1");
} else {
let target = result.unwrap();
let status =
fs::read_to_string(format!("/proc/{}/status", target.root().init_pid())).unwrap();
assert!(status.contains("Seccomp:\t2\n"));
assert!(status.contains("Seccomp_filters:\t1\n"));
assert!(status.contains("NoNewPrivs:\t1\n"));
assert_eq!(target.identity(), (100000, 100000));
assert!(target.root().snapshot_outputs().unwrap().is_empty());
let e = target.refuse_remaining_isolation();
assert_eq!(e.failure.stage, ExecutionStage::Exec);
assert!(e.cleanup_failure.is_none());
let output = Command::new("/kf/bin/seccomp-probe")
.arg(name)
.env_clear()
.env("KF_SANDBOX_INDEPENDENT_VM", "1")
.output()
.unwrap();
assert!(
output.status.success(),
"{:?} {:?}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
print!("{}", String::from_utf8_lossy(&output.stdout));
println!(
"KF_SECCOMP_INIT_PROOF profile={name} frozen=1 filter_count=1 identity=1 run_refused=1"
);
}
assert_eq!(support::children(), before);
assert_eq!(
fs::read_dir("/var/lib/kf-judge/identities")
.unwrap()
.count(),
0
);
assert!(!Path::new(&format!("/var/lib/kf-judge/tasks/kf-seccomp-{name}")).exists());
assert!(!root.path.join(format!("kf-seccomp-{name}")).exists());
fs::remove_dir_all(path).unwrap();
}
#[test]
#[ignore = "requires independent capped VM"]
fn native_policy_installs_and_inherits() {
check(ExecutionProfile::Native, "native");
}
#[test]
#[ignore = "requires independent capped VM"]
fn compile_c_policy_installs_and_inherits() {
check(ExecutionProfile::CompileC, "compile-c");
}
#[test]
#[ignore = "requires independent capped VM"]
fn compile_cpp_policy_installs_and_inherits() {
check(ExecutionProfile::CompileCpp, "compile-cpp");
}
#[test]
#[ignore = "requires independent capped VM with kernel seccomp failure injector"]
fn kernel_seccomp_failure_refuses_and_cleans() {
assert_eq!(std::env::var("KF_SANDBOX_DENY_SECCOMP").as_deref(), Ok("1"));
check(ExecutionProfile::Native, "native");
}