Refuse prepared-lower child mount crossings

This commit is contained in:
ms2k 2026-07-16 21:09:48 +09:00
commit b35582a9e6

View file

@ -22,6 +22,8 @@ use sha2::{Digest, Sha256};
use tar::{Builder, EntryType, Header};
use tempfile::TempDir;
const MOUNT_CHILD_ENV: &str = "LAZARET_PREPARED_LOWER_MOUNT_CHILD";
const OCI_TAR: &str = "application/vnd.oci.image.layer.v1.tar";
const OCI_GZIP: &str = "application/vnd.oci.image.layer.v1.tar+gzip";
const OCI_ZSTD: &str = "application/vnd.oci.image.layer.v1.tar+zstd";
@ -83,6 +85,64 @@ fn preexisting_fixed_children_require_exact_private_mode() {
}
}
#[test]
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
fn retained_fixed_child_bind_mount_is_refused_when_mount_setup_is_available() {
if std::env::var_os(MOUNT_CHILD_ENV).is_some() {
let directory = TempDir::new().unwrap();
let root = directory.path().join("storage");
let source = directory.path().join("source");
std::fs::create_dir(&root).unwrap();
std::fs::create_dir(&source).unwrap();
let catalog = Catalog::new(root.join("catalog.redb"), root.join("storage-catalog.lock"));
catalog.initialize().unwrap();
PreparedLowerStore::initialize(&root, catalog.clone()).unwrap();
let target = root.join("artifacts");
if let Err(error) = rustix::mount::mount_bind(&source, &target) {
eprintln!(
"skipping physical mount-crossing assertion: bind mount unavailable: {error}"
);
return;
}
let result = PreparedLowerStore::initialize(&root, catalog);
rustix::mount::unmount(&target, rustix::mount::UnmountFlags::DETACH).unwrap();
assert!(result.is_err());
return;
}
let output = std::process::Command::new("unshare")
.args([
"--user",
"--map-root-user",
"--mount",
"--propagation",
"private",
"--",
])
.arg(std::env::current_exe().unwrap())
.args([
"--exact",
"retained_fixed_child_bind_mount_is_refused_when_mount_setup_is_available",
])
.env(MOUNT_CHILD_ENV, "1")
.output();
let output = match output {
Ok(output) => output,
Err(error) => {
eprintln!("skipping physical mount-crossing assertion: unshare unavailable: {error}");
return;
}
};
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("Operation not permitted") {
eprintln!("skipping physical mount-crossing assertion: {stderr}");
return;
}
panic!("mount-crossing child failed: {stderr}");
}
}
#[derive(Clone)]
struct Entry<'a> {
path: &'a str,