#![cfg(all(target_os = "linux", feature = "backend-linux"))]
use bvisor::linux::launch::{self, AuthorityFd, LaunchObservation};
use bvisor::linux::protocol::{
DescriptorKind, DescriptorRole, DescriptorShape, DescriptorSlotV1, LinuxLaunchBodyV1,
LinuxLaunchPlanV1, LoweringWireEntryV1, LoweringWireV1, TargetSpecV1,
};
use bvisor::{AdmissionProgramHash, AttemptId, BackendProfileHash, BoundaryPlanHash};
use std::os::fd::{OwnedFd, RawFd};
use std::path::PathBuf;
const ID_AMBIENT_SCRUB: &str = "linux.ambient.scrub.v1";
const ID_EXEC: &str = "linux.exec.v1";
const PHASE_CODE_SCRUB: u8 = 3; const PHASE_CODE_EXEC: u8 = 5;
const SLOT_EXE: RawFd = 10;
const LAUNCHER_CHANNEL_ENV: &[&str] = &[
"BVISOR_LAUNCH_PLAN_FD",
"BVISOR_CONTROL_FD",
"BVISOR_ERROR_FD",
"BVISOR_ERROR_READ_FD",
];
const DECLARED_MARKER: &str = "BVISOR_DECLARED_ONLY=present";
fn launcher_path() -> PathBuf {
launch::resolve_launcher_path(env!("CARGO_BIN_EXE_bvisor-linux-launcher"))
}
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 exe_slot() -> DescriptorSlotV1 {
DescriptorSlotV1 {
slot_index: u32::try_from(SLOT_EXE).expect("fd fits u32"),
role: DescriptorRole::TargetExe,
expected: DescriptorShape {
kind: DescriptorKind::Regular,
writable: false,
},
}
}
fn exec_only_plan(argv: Vec<String>, envp: Vec<(String, String)>) -> LinuxLaunchPlanV1 {
let lowering = LoweringWireV1 {
entries: vec![
entry(ID_AMBIENT_SCRUB, PHASE_CODE_SCRUB),
entry(ID_EXEC, PHASE_CODE_EXEC),
],
};
let bytes = batpak::canonical::to_bytes(&lowering).expect("encode lowering");
let h_l = batpak::event::hash::compute_hash(&bytes);
let body = 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: vec![exe_slot()],
target: TargetSpecV1 {
argv,
envp,
exe_slot: u32::try_from(SLOT_EXE).expect("fd fits u32"),
user_namespace: None,
network_namespace: None,
seccomp: None,
},
};
LinuxLaunchPlanV1 { body }
}
fn exe_authority() -> AuthorityFd {
AuthorityFd {
slot_index: SLOT_EXE,
handle: OwnedFd::from(std::fs::File::open("/bin/sh").expect("open /bin/sh")),
}
}
fn run_env_workload() -> LaunchObservation {
let argv = vec!["sh".to_string(), "-c".to_string(), "env".to_string()];
let envp = vec![
("PATH".to_owned(), "/usr/bin:/bin".to_owned()),
("BVISOR_DECLARED_ONLY".to_owned(), "present".to_owned()),
];
let plan = exec_only_plan(argv, envp);
launch::run_launcher(&launcher_path(), &plan, vec![exe_authority()])
.expect("the launcher harness runs the env workload to a verdict")
}
#[test]
fn workload_environment_is_exactly_the_declared_envp() {
let obs = run_env_workload();
if launch::launch_confinement_unavailable(&obs) {
use std::io::Write as _;
let mut sink = std::io::stderr();
let _ = writeln!(
sink,
"SKIP workload_environment_is_exactly_the_declared_envp: kernel/container lacks \
landlock/userns/seccomp (ENOSYS); the launcher faulted before exec — exercised on \
capable kernels + the bvisor-linux CI lane"
);
return;
}
assert!(
obs.exec_succeeded(),
"the env workload must reach ExecSucceeded; terminal={:?} notes={:?}",
obs.terminal,
obs.notes
);
let env = String::from_utf8_lossy(&obs.captured_stdout);
assert!(
env.contains(DECLARED_MARKER),
"the declared envp must reach the workload; expected {DECLARED_MARKER:?} in env output:\n{env}"
);
for leaked in LAUNCHER_CHANNEL_ENV {
assert!(
!env.contains(leaked),
"launcher channel env var {leaked:?} leaked into the workload environment \
(env is inherited, not explicit); env output:\n{env}"
);
}
assert!(
!env.contains("BVISOR_LAUNCH"),
"no launcher plumbing variable may reach the workload; env output:\n{env}"
);
}
#[test]
fn environment_isolation_is_deterministic_across_runs() {
for run in 0..5 {
let obs = run_env_workload();
if launch::launch_confinement_unavailable(&obs) {
use std::io::Write as _;
let mut sink = std::io::stderr();
let _ = writeln!(
sink,
"SKIP environment_isolation_is_deterministic_across_runs: kernel/container lacks \
landlock/userns/seccomp (ENOSYS); the launcher faulted before exec — exercised \
on capable kernels + the bvisor-linux CI lane"
);
return;
}
let env = String::from_utf8_lossy(&obs.captured_stdout);
assert!(
env.contains(DECLARED_MARKER) && !env.contains("BVISOR_LAUNCH"),
"run {run}: environment isolation must be deterministic; env output:\n{env}"
);
}
}