Enforce exact recovery ownership before cold start

This commit is contained in:
ms2k 2026-07-17 06:48:49 +09:00
commit 2bc5982a62

View file

@ -1,14 +1,22 @@
use std::fs::{self, File};
use std::io;
use std::os::fd::OwnedFd;
use std::os::unix::fs::PermissionsExt;
use std::process::ExitCode;
use lazaret_core::identity::{
ConfigurationFingerprint, CoordinatorId, DomainId, GenerationId, NetworkSidecarId, SandboxId,
BootId, BundleDigest, ConfigurationFingerprint, ContainerId, CoordinatorId, DomainId,
GenerationId, HostPath, HostPidLocator, MountId, MountIdentity, NetworkSidecarId,
PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, PidfsProcessIdentityV1,
ProcessStartTimeTicks, SandboxId,
};
use lazaret_core::records::{
DurableDecision, Generation, Participant, ParticipantOwnership, ParticipantRole,
ParticipantState, PersistedHostProcessOwnership, StateAnchorOwnership,
ParticipantState, PersistedHostProcessOwnership, SandboxOwnership, StateAnchorOwnership,
};
use lazaret_host_coordinator::recovery::{
delete_runsc_container, prove_runtime_owners_absent, runtime_recovery_ownership,
};
use lazaret_host_coordinator::recovery::{prove_runtime_owners_absent, runtime_recovery_ownership};
use lazaret_linux::process::{TargetCommand, child_broker, dispatch_private_process_role};
use rustix::process::Signal;
@ -31,19 +39,61 @@ fn exit(result: io::Result<()>) -> ExitCode {
}
fn run() -> io::Result<()> {
verify_exact_runsc_delete()?;
verify_exact_action(None)?;
verify_exact_action(Some(Signal::STOP))
}
fn verify_exact_runsc_delete() -> io::Result<()> {
let tree = tempfile::tempdir()?;
let root_path = tree.path().join("runsc");
fs::create_dir(&root_path)?;
fs::create_dir(root_path.join("container"))?;
let program = tree.path().join("runsc-fixture");
fs::write(
&program,
b"#!/bin/sh\nroot=${1#-root=}\n[ \"$2\" = delete ] && [ \"$3\" = --force ] && rmdir \"$root/$4\"\n",
)?;
fs::set_permissions(&program, fs::Permissions::from_mode(0o700))?;
let root: OwnedFd = File::open(&root_path)?.into();
delete_runsc_container(&root, "container", program.as_os_str()).map_err(io::Error::other)?;
if root_path.join("container").exists() {
return Err(io::Error::other("exact runsc container remained"));
}
Ok(())
}
fn verify_exact_action(signal: Option<Signal>) -> io::Result<()> {
let captured =
let state =
child_broker().run_transient_captured(&TargetCommand::new("/bin/sleep").argument("60"))?;
if let Some(signal) = signal {
captured.process().send_signal(signal)?;
state.process().send_signal(signal)?;
}
let process = captured.into_process();
let state = state.into_process();
let supervisor = child_broker()
.run_transient_captured(&TargetCommand::new("/bin/sleep").argument("60"))?
.into_process();
let generation = generation();
let participants = [
Participant::snapshot(
generation.generation_id,
ParticipantRole::Sandbox,
ParticipantState::Intent,
Some(ParticipantOwnership::Sandbox(SandboxOwnership {
container_id: Some(ContainerId::new([6; 32])),
runtime_root: Some(HostPath::new(b"/runtime/runsc".to_vec())),
runtime_root_identity: Some(MountIdentity {
mount_id: MountId::new(7),
device: lazaret_core::identity::DeviceId::new(8),
inode: lazaret_core::identity::InodeId::new(9),
}),
bundle_digest: Some(BundleDigest::new([10; 32])),
host_coordinator: PersistedHostProcessOwnership::committed(absent_process()),
runtime_supervisor: PersistedHostProcessOwnership::staged(supervisor.identity()),
attached_runsc: PersistedHostProcessOwnership::unassigned(),
})),
)
.map_err(|_| io::Error::other("Sandbox participant is invalid"))?,
Participant::bare_intent(generation.generation_id, ParticipantRole::PreparedLower),
Participant::snapshot(
generation.generation_id,
@ -54,7 +104,7 @@ fn verify_exact_action(signal: Option<Signal>) -> io::Result<()> {
upper_path: None,
work_path: None,
merged_path: None,
daemon: PersistedHostProcessOwnership::staged(process.identity()),
daemon: PersistedHostProcessOwnership::staged(state.identity()),
user_namespace: None,
mount_namespace: None,
mount: None,
@ -65,7 +115,20 @@ fn verify_exact_action(signal: Option<Signal>) -> io::Result<()> {
let ownership =
runtime_recovery_ownership(&participants, &generation).map_err(io::Error::other)?;
prove_runtime_owners_absent(&ownership)?;
child_broker().reap(process)
child_broker().reap(state)?;
child_broker().reap(supervisor)
}
fn absent_process() -> PidfsProcessIdentityV1 {
PidfsProcessIdentityV1::new(
PidfsProcessAuthorityV1 {
boot_id: BootId::new([0xff; 16]),
pidfs_st_dev: PidfsDeviceId::new(1),
pidfs_st_ino: PidfsInodeId::new(2),
},
HostPidLocator::new(1),
ProcessStartTimeTicks::new(3),
)
}
fn generation() -> Generation {