use super::LinuxBackend;
use crate::contract::capability::{Capability, SpawnPolicy};
use crate::contract::plan::{BoundaryPlan, BoundaryRequirement};
use crate::contract::report::ObservedFact;
pub(super) struct ChildTaskLowering {
pub(super) deny_new_tasks: bool,
pub(super) observed: Vec<ObservedFact>,
}
pub(super) fn lower_child_spawn(
_backend: &LinuxBackend,
plan: &BoundaryPlan,
mut observed: Vec<ObservedFact>,
) -> Result<ChildTaskLowering, Vec<ObservedFact>> {
match admitted_spawn_policy(plan) {
None => Ok(ChildTaskLowering {
deny_new_tasks: false,
observed,
}),
Some(SpawnPolicy::DenyNewTasks) => {
observed.push(ObservedFact {
kind: "child_spawn_lowered".to_string(),
detail: "ChildSpawn::DenyNewTasks: the launcher installs a default-allow seccomp \
DENYLIST refusing clone/clone3/fork/vfork at the syscall-number level \
(LAST, after landlock, before fexecve; EPERM so the workload's fork \
fails observably). ONE composed layer — the broad confinement is \
landlock/cgroup/netns/fd-scrub."
.to_string(),
});
Ok(ChildTaskLowering {
deny_new_tasks: true,
observed,
})
}
Some(SpawnPolicy::AllowDescendantsWithinBoundary) => {
observed.push(ObservedFact {
kind: "child_spawn_lowered".to_string(),
detail: "ChildSpawn::AllowDescendantsWithinBoundary: NO seccomp deny — the \
descendant inherits the run cgroup (killable via cgroup.kill, counted \
by pids.max, namespace-trapped); the cgroup boundary is the mechanism."
.to_string(),
});
Ok(ChildTaskLowering {
deny_new_tasks: false,
observed,
})
}
Some(SpawnPolicy::AllowThreadsWithinBoundary) => {
observed.push(ObservedFact {
kind: "child_spawn_lowering_failed".to_string(),
detail: "refusing to launch: ChildSpawn::AllowThreadsWithinBoundary is NOT \
realized by this backend (the clone3-pointer / classic-BPF problem — \
seccomp cannot deref clone3 flags to permit-threads-but-deny-processes; \
denying clone3 outright breaks glibc threads). FailClosed; the target \
never runs."
.to_string(),
});
Err(observed)
}
}
}
fn admitted_spawn_policy(plan: &BoundaryPlan) -> Option<SpawnPolicy> {
plan.admitted.iter().find_map(|a| match &a.requirement {
BoundaryRequirement::Capability(Capability::ChildSpawn { policy }) => Some(*policy),
BoundaryRequirement::Capability(_) | BoundaryRequirement::HostControl(_) => None,
})
}