#![cfg(all(target_os = "linux", feature = "backend-linux"))]
use bvisor::linux::launch::{launch_confinement_unavailable, run_launcher, AuthorityFd};
use bvisor::linux::protocol::{
DescriptorKind, DescriptorRole, DescriptorShape, DescriptorSlotV1, LauncherState,
LinuxLaunchBodyV1, LinuxLaunchPlanV1, LoweringWireEntryV1, LoweringWireV1, TargetSpecV1,
};
use bvisor::{AdmissionProgramHash, AttemptId, BackendProfileHash, BoundaryPlanHash};
use std::io::Write;
use std::os::fd::{OwnedFd, RawFd};
use std::path::{Path, PathBuf};
const ID_AMBIENT_SCRUB: &str = "linux.ambient.scrub.v1";
const ID_LANDLOCK_APPLY: &str = "linux.landlock.apply.v1";
const ID_EXEC: &str = "linux.exec.v1";
const PHASE_CODE_SCRUB: u8 = 3; const PHASE_CODE_CONFINE: u8 = 4; const PHASE_CODE_EXEC: u8 = 5;
const LANDLOCK_ABI_FLOOR: i64 = 3;
const SLOT_EXE: RawFd = 10;
const SLOT_READ_ROOT: RawFd = 15;
const SLOT_WRITE_ROOT: RawFd = 16;
const SLOT_SYS_ROOT_BASE: RawFd = 20;
const SYSTEM_EXEC_ROOTS: &[&str] = &["/usr", "/lib", "/lib64", "/bin", "/sbin", "/etc"];
fn live_landlock_abi() -> i64 {
const LANDLOCK_CREATE_RULESET_VERSION: libc::c_uint = 1;
let raw = unsafe {
libc::syscall(
libc::SYS_landlock_create_ruleset,
std::ptr::null::<libc::c_void>(),
0usize,
LANDLOCK_CREATE_RULESET_VERSION,
)
};
if raw < 0 {
0
} else {
raw
}
}
fn landlock_available() -> bool {
live_landlock_abi() >= LANDLOCK_ABI_FLOOR
}
struct Scratch {
root: PathBuf,
}
impl Scratch {
fn new(tag: &str) -> Self {
let pid = std::process::id();
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let root = std::env::temp_dir().join(format!("bvisor-harness-fs-{tag}-{pid}-{nanos}"));
std::fs::create_dir_all(&root).expect("scratch root");
Self { root }
}
fn path(&self, name: &str) -> PathBuf {
self.root.join(name)
}
}
impl Drop for Scratch {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
struct FsGroundTruth {
marker: String,
witness_path: PathBuf,
}
impl FsGroundTruth {
fn danger_occurred(&self) -> bool {
match std::fs::read(&self.witness_path) {
Ok(bytes) => String::from_utf8_lossy(&bytes).contains(&self.marker),
Err(_) => false,
}
}
fn effect_landed(&self) -> bool {
self.danger_occurred()
}
}
fn entry(id: &str, phase_code: u8) -> LoweringWireEntryV1 {
LoweringWireEntryV1 {
id: id.to_owned(),
version: 1,
phase_code,
param_digest: [0u8; 32],
decl_digest: [0u8; 32],
}
}
fn body_with(
lowering: LoweringWireV1,
table: Vec<DescriptorSlotV1>,
argv: Vec<String>,
) -> LinuxLaunchBodyV1 {
let bytes = batpak::canonical::to_bytes(&lowering).expect("encode lowering");
let h_l = batpak::event::hash::compute_hash(&bytes);
LinuxLaunchBodyV1 {
attempt_id: AttemptId([7u8; 32]),
plan_id: BoundaryPlanHash([1u8; 32]),
h_a: AdmissionProgramHash([2u8; 32]),
h_p: BackendProfileHash([3u8; 32]),
h_l,
lowering,
descriptor_table: table,
target: TargetSpecV1 {
argv,
envp: vec![("PATH".to_owned(), "/usr/bin:/bin".to_owned())],
exe_slot: u32::try_from(SLOT_EXE).expect("fd fits u32"),
user_namespace: None,
network_namespace: None,
seccomp: None,
},
}
}
fn exe_slot() -> DescriptorSlotV1 {
DescriptorSlotV1 {
slot_index: u32::try_from(SLOT_EXE).expect("fd"),
role: DescriptorRole::TargetExe,
expected: DescriptorShape {
kind: DescriptorKind::Regular,
writable: false,
},
}
}
fn root_slot(fd: RawFd, role: DescriptorRole) -> DescriptorSlotV1 {
DescriptorSlotV1 {
slot_index: u32::try_from(fd).expect("fd"),
role,
expected: DescriptorShape {
kind: DescriptorKind::Directory,
writable: false,
},
}
}
fn present_system_roots() -> Vec<&'static str> {
SYSTEM_EXEC_ROOTS
.iter()
.copied()
.filter(|p| Path::new(p).is_dir())
.collect()
}
fn confined_plan(argv: Vec<String>, n_sys: usize) -> LinuxLaunchPlanV1 {
let lowering = LoweringWireV1 {
entries: vec![
entry(ID_AMBIENT_SCRUB, PHASE_CODE_SCRUB),
entry(ID_LANDLOCK_APPLY, PHASE_CODE_CONFINE),
entry(ID_EXEC, PHASE_CODE_EXEC),
],
};
let mut table = vec![
exe_slot(),
root_slot(SLOT_READ_ROOT, DescriptorRole::ReadRoot),
root_slot(SLOT_WRITE_ROOT, DescriptorRole::WriteRoot),
];
for i in 0..n_sys {
let fd = SLOT_SYS_ROOT_BASE + RawFd::try_from(i).expect("fd");
table.push(root_slot(fd, DescriptorRole::ReadRoot));
}
LinuxLaunchPlanV1 {
body: body_with(lowering, table, argv),
}
}
fn unconfined_plan(argv: Vec<String>) -> LinuxLaunchPlanV1 {
let lowering = LoweringWireV1 {
entries: vec![
entry(ID_AMBIENT_SCRUB, PHASE_CODE_SCRUB),
entry(ID_EXEC, PHASE_CODE_EXEC),
],
};
LinuxLaunchPlanV1 {
body: body_with(lowering, vec![exe_slot()], argv),
}
}
fn open_sh() -> OwnedFd {
OwnedFd::from(std::fs::File::open("/bin/sh").expect("open /bin/sh"))
}
fn open_dir(path: &Path) -> OwnedFd {
OwnedFd::from(std::fs::File::open(path).expect("open dir"))
}
fn launcher_bin() -> PathBuf {
bvisor::linux::launch::resolve_launcher_path(env!("CARGO_BIN_EXE_bvisor-linux-launcher"))
}
#[test]
fn harness_landlock_denies_secret_read_and_escape_write_allows_in_root() {
if !landlock_available() {
let mut sink = std::io::stderr();
let _ = writeln!(
sink,
"SKIP harness G1/G3: live landlock ABI {} < floor {} \
(kernel lacks landlock or the sandbox blocks it)",
live_landlock_abi(),
LANDLOCK_ABI_FLOOR
);
return;
}
let scratch = Scratch::new("g1g3");
let quarantine = scratch.path("quarantine"); std::fs::create_dir_all(&quarantine).expect("quarantine dir");
let readable = scratch.path("readable"); std::fs::create_dir_all(&readable).expect("readable dir");
let in_root_src = readable.join("inside.txt");
let control_marker = "CONTROL-IN-ROOT-MARKER".to_string();
std::fs::write(&in_root_src, &control_marker).expect("plant in-root source");
let secret = scratch.path("secret.key");
let secret_marker = "TOPSECRET-HARNESS-G1".to_string();
std::fs::write(&secret, &secret_marker).expect("plant secret");
let exfil = quarantine.join("exfil.txt"); let control_sink = quarantine.join("control.txt");
let escape = scratch.path("escape.txt");
let escape_marker = "ESCAPED-HARNESS-G3".to_string();
let cmd = format!(
"cat {secret} > {exfil} ; echo {escape_marker} > {escape} ; cat {src} > {control_sink}",
secret = secret.to_string_lossy(),
exfil = exfil.to_string_lossy(),
escape_marker = escape_marker,
escape = escape.to_string_lossy(),
src = in_root_src.to_string_lossy(),
control_sink = control_sink.to_string_lossy(),
);
let sys_dirs = present_system_roots();
let plan = confined_plan(vec!["sh".into(), "-c".into(), cmd], sys_dirs.len());
let mut authority = vec![
AuthorityFd {
slot_index: SLOT_EXE,
handle: open_sh(),
},
AuthorityFd {
slot_index: SLOT_READ_ROOT,
handle: open_dir(&readable),
},
AuthorityFd {
slot_index: SLOT_WRITE_ROOT,
handle: open_dir(&quarantine),
},
];
for (i, p) in sys_dirs.iter().enumerate() {
authority.push(AuthorityFd {
slot_index: SLOT_SYS_ROOT_BASE + RawFd::try_from(i).expect("fd"),
handle: open_dir(Path::new(p)),
});
}
let obs = run_launcher(&launcher_bin(), &plan, authority).expect("run launcher harness");
if launch_confinement_unavailable(&obs) {
let mut sink = std::io::stderr();
let _ = writeln!(
sink,
"SKIP harness_landlock_denies_secret_read_and_escape_write_allows_in_root: \
kernel/container lacks landlock/userns/seccomp (ENOSYS); the launcher faulted before \
exec — exercised on capable kernels + the bvisor-linux CI lane"
);
return;
}
let g1 = FsGroundTruth {
marker: secret_marker,
witness_path: exfil,
};
assert!(
!g1.danger_occurred(),
"G1: landlock must block the out-of-root secret READ; the secret leaked into the \
in-root exfil sink on disk. observation:\n{obs:?}"
);
let g3 = FsGroundTruth {
marker: escape_marker,
witness_path: escape,
};
assert!(
!g3.danger_occurred(),
"G3: landlock must block the out-of-quarantine WRITE; the escape file exists on \
disk. observation:\n{obs:?}"
);
let control = FsGroundTruth {
marker: control_marker,
witness_path: control_sink,
};
assert!(
control.effect_landed(),
"CONTROL: an in-root read→in-root write must be ALLOWED through landlock (the deny \
tests above are otherwise vacuous). observation:\n{obs:?}"
);
assert_eq!(
obs.terminal,
Some(LauncherState::ExecSucceeded),
"the harness must collect terminal ExecSucceeded (workload ran, confined): {obs:?}"
);
assert!(
obs.exec_succeeded(),
"exec_succeeded() must agree with the terminal: {obs:?}"
);
assert!(
obs.transcript
.contains(&LauncherState::ConfinementPhaseResolved),
"the harness transcript must resolve the Confinement phase: {obs:?}"
);
assert!(
obs.confinement_installed,
"the harness must collect REAL confinement evidence (installed=true): {obs:?}"
);
assert_eq!(
obs.outcome(),
Some(bvisor::Outcome::Completed),
"ExecSucceeded maps to Outcome::Completed (the honest 7b mapping): {obs:?}"
);
}
#[test]
fn harness_without_landlock_lets_the_escape_land() {
let scratch = Scratch::new("novacuous");
let escape = scratch.path("escape.txt");
let marker = "ESCAPED-HARNESS-NOLANDLOCK".to_string();
let cmd = format!(
"echo {marker} > {escape}",
marker = marker,
escape = escape.to_string_lossy(),
);
let plan = unconfined_plan(vec!["sh".into(), "-c".into(), cmd]);
let authority = vec![AuthorityFd {
slot_index: SLOT_EXE,
handle: open_sh(),
}];
let obs = run_launcher(&launcher_bin(), &plan, authority).expect("run launcher harness");
if launch_confinement_unavailable(&obs) {
let mut sink = std::io::stderr();
let _ = writeln!(
sink,
"SKIP harness_without_landlock_lets_the_escape_land: kernel/container lacks \
landlock/userns/seccomp (ENOSYS); the launcher faulted before exec — exercised on \
capable kernels + the bvisor-linux CI lane"
);
return;
}
let gt = FsGroundTruth {
marker,
witness_path: escape,
};
assert!(
gt.danger_occurred(),
"NON-VACUOUS: an UNCONFINED launch must let the escape write land on disk (so the \
confined G3 deny is meaningful). observation:\n{obs:?}"
);
assert_eq!(
obs.terminal,
Some(LauncherState::ExecSucceeded),
"the unconfined workload still runs to success: {obs:?}"
);
assert!(
!obs.confinement_installed,
"an exec-only plan must report NO confinement install (no over-claim): {obs:?}"
);
}