From 5182669f8f0f2fa3febf11112d0d96e249cff68a Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 17:14:32 +0300 Subject: [PATCH 1/6] Support making binds using mount --- minion-tests/src/main.rs | 9 +++++++++ minion-tests/src/master.rs | 15 ++++++++++++--- minion-tests/src/worker.rs | 6 +++++- src/linux.rs | 5 +++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/minion-tests/src/main.rs b/minion-tests/src/main.rs index 92f3a56d..d557b43e 100644 --- a/minion-tests/src/main.rs +++ b/minion-tests/src/main.rs @@ -11,6 +11,8 @@ //! Worker: sets up sandbox, executes `minion-tests` in Test mode. //! Test: just executes test selected by name #![feature(asm, test)] + +use minion::linux::Settings; mod master; mod tests; mod worker; @@ -98,3 +100,10 @@ enum Role { Worker, Test, } + +fn configurations() -> Vec { + let s = Settings::new(); + let mut s_ext_mount = s.clone(); + s_ext_mount.use_mount_program = true; + vec![s, s_ext_mount] +} \ No newline at end of file diff --git a/minion-tests/src/master.rs b/minion-tests/src/master.rs index a83ac65e..6923aec2 100644 --- a/minion-tests/src/master.rs +++ b/minion-tests/src/master.rs @@ -1,4 +1,5 @@ //! Master source code + use crate::TestCase; #[derive(Copy, Clone)] @@ -58,6 +59,7 @@ struct ExecuteOptions { } fn execute_tests(test_cases: &[&dyn TestCase], exec_opts: ExecuteOptions) -> Outcome { + let configs_count = crate::configurations().len(); println!("will run:"); for &case in test_cases { println!(" - {}", case.name()); @@ -65,13 +67,19 @@ fn execute_tests(test_cases: &[&dyn TestCase], exec_opts: ExecuteOptions) -> Out println!("({} tests)", test_cases.len()); let mut outcome = Outcome::Success; for &case in test_cases { - outcome = outcome.and(execute_single_test(case, exec_opts)); + for idx in 0..configs_count { + outcome = outcome.and(execute_single_test(case, exec_opts, idx)); + } } outcome } -fn execute_single_test(case: &dyn TestCase, exec_opts: ExecuteOptions) -> Outcome { - println!("------ {} ------", case.name()); +fn execute_single_test( + case: &dyn TestCase, + exec_opts: ExecuteOptions, + config_idx: usize, +) -> Outcome { + println!("------ {} (config #{}) ------", case.name(), config_idx); let self_exe = std::env::current_exe().unwrap(); let mut cmd = if exec_opts.trace { let mut cmd = std::process::Command::new("strace"); @@ -91,6 +99,7 @@ fn execute_single_test(case: &dyn TestCase, exec_opts: ExecuteOptions) -> Outcom } cmd.env(crate::WORKER_ENV_NAME, "1"); cmd.env("TEST", case.name()); + cmd.env("CONFIG_INDEX", config_idx.to_string()); let status = cmd.status().unwrap(); if status.success() { Outcome::Success diff --git a/minion-tests/src/worker.rs b/minion-tests/src/worker.rs index 4a8018da..7609975c 100644 --- a/minion-tests/src/worker.rs +++ b/minion-tests/src/worker.rs @@ -13,8 +13,12 @@ async fn inner_main(test_cases: &[&'static dyn TestCase]) { .find(|&tc| tc.name() == test_case_name) .unwrap(); + let config_idx = std::env::var("CONFIG_INDEX").unwrap().parse().unwrap(); + let config = crate::configurations().into_iter().nth(config_idx).unwrap(); + let tempdir = tempfile::TempDir::new().expect("cannot create temporary dir"); - let backend = minion::erased::setup().expect("backend creation failed"); + let backend = minion::LinuxBackend::new(config).expect("backend creation failed"); + let backend: Box = Box::new(backend); let opts = minion::SandboxOptions { cpu_time_limit: test_case.time_limit(), real_time_limit: test_case.real_time_limit(), diff --git a/src/linux.rs b/src/linux.rs index 818472e8..329296bd 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -222,6 +222,10 @@ pub struct Settings { /// If enabled, minion will ignore clone(MOUNT_NEWNS) error. /// This flag has to be enabled for gVisor support. pub allow_unsupported_mount_namespace: bool, + + /// If enabled, minion will use `mount` CLI utility + /// for creating bind-mounts. + pub use_mount_program: bool, } impl Default for Settings { @@ -232,6 +236,7 @@ impl Default for Settings { cgroupfs: std::env::var_os("MINION_CGROUPFS") .map(PathBuf::from) .unwrap_or_else(|| PathBuf::from("/sys/fs/cgroup")), + use_mount_program: false, } } } From 1e57f3ef409763438de59ae1ef81a130d5a88f47 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 17:27:28 +0300 Subject: [PATCH 2/6] imp --- src/lib.rs | 2 +- src/linux/jail_common.rs | 1 + src/linux/sandbox.rs | 1 + src/linux/zygote/setup.rs | 63 +++++++++++++++++++++++++++++++-------- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ed28ba95..7ce3dc7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ pub use command::Command; /// Anyway, SUID-bit will be disabled. /// /// Warning: this type is __unstable__ (i.e. not covered by SemVer) and __non-portable__ -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum SharedDirKind { Readonly, Full, diff --git a/src/linux/jail_common.rs b/src/linux/jail_common.rs index 5b3fd01a..95186b15 100644 --- a/src/linux/jail_common.rs +++ b/src/linux/jail_common.rs @@ -15,6 +15,7 @@ pub(crate) struct JailOptions { pub(crate) real_time_limit: Duration, pub(crate) isolation_root: PathBuf, pub(crate) exposed_paths: Vec, + pub(crate) use_mount_for_binds: bool, pub(crate) jail_id: String, pub(crate) watchdog_chan: RawFd, pub(crate) allow_mount_ns_failure: bool, diff --git a/src/linux/sandbox.rs b/src/linux/sandbox.rs index e0e2e527..17e6b977 100644 --- a/src/linux/sandbox.rs +++ b/src/linux/sandbox.rs @@ -172,6 +172,7 @@ impl LinuxSandbox { real_time_limit: options.real_time_limit, isolation_root: options.isolation_root.clone(), exposed_paths: options.exposed_paths.clone(), + use_mount_for_binds: settings.use_mount_program, jail_id: jail_id.clone(), watchdog_chan: write_end, allow_mount_ns_failure: settings.allow_unsupported_mount_namespace, diff --git a/src/linux/zygote/setup.rs b/src/linux/zygote/setup.rs index c4b4614d..02ae0ed9 100644 --- a/src/linux/zygote/setup.rs +++ b/src/linux/zygote/setup.rs @@ -15,8 +15,9 @@ use std::{ ffi::CString, fs, io, io::Write, - os::unix::{ffi::OsStrExt, io::RawFd}, + os::unix::{ffi::OsStrExt, io::RawFd, prelude::ExitStatusExt}, path::Path, + process::Command, ptr, time, }; use tiny_nix_ipc::Socket; @@ -49,15 +50,7 @@ fn configure_dir(dir_path: &Path) -> Result<(), Error> { Ok(()) } -fn expose_dir(jail_root: &Path, system_path: &Path, alias_path: &Path, kind: SharedDirKind) { - let bind_target = jail_root.join(alias_path); - fs::create_dir_all(&bind_target).unwrap(); - let stat = fs::metadata(&system_path) - .unwrap_or_else(|err| panic!("failed to stat {}: {}", system_path.display(), err)); - if stat.is_file() { - fs::remove_dir(&bind_target).unwrap(); - fs::write(&bind_target, &"").unwrap(); - } +fn expose_dir_via_syscall(bind_target: &Path, system_path: &Path, kind: SharedDirKind) { let bind_target = CString::new(bind_target.as_os_str().as_bytes()).unwrap(); let bind_src = CString::new(system_path.as_os_str().as_bytes()).unwrap(); unsafe { @@ -87,10 +80,50 @@ fn expose_dir(jail_root: &Path, system_path: &Path, alias_path: &Path, kind: Sha } } -pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path) { +fn expose_dir_via_mount(bind_target: &Path, system_path: &Path, kind: SharedDirKind) { + let mut cmd = Command::new("mount"); + match kind { + SharedDirKind::Full => { + cmd.arg("--bind"); + } + SharedDirKind::Readonly => { + cmd.arg("-o").arg("bind,ro"); + } + } + cmd.arg(system_path); + cmd.arg(bind_target); + match cmd.status() { + Ok(st) => { + if !st.success() { + panic!( + "mount program failed: exit code {:?}, signal {:?}", + st.code(), + st.signal() + ); + } + } + Err(err) => { + panic!("failed to spawn mount: {:#}", err); + } + } +} + +pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path, use_mount: bool) { // mount --bind for x in expose { - expose_dir(jail_root, &x.src, &x.dest, x.kind.clone()) + let bind_target = jail_root.join(&x.dest); + fs::create_dir_all(&bind_target).unwrap(); + let stat = fs::metadata(&x.src) + .unwrap_or_else(|err| panic!("failed to stat {}: {}", x.src.display(), err)); + if stat.is_file() { + fs::remove_dir(&bind_target).unwrap(); + fs::write(&bind_target, &"").unwrap(); + } + if use_mount { + expose_dir_via_mount(&bind_target, &x.src, x.kind) + } else { + expose_dir_via_syscall(&bind_target, &x.src, x.kind) + } } } @@ -183,7 +216,11 @@ fn setup_time_watch( } fn setup_expositions(options: &JailOptions) { - expose_dirs(&options.exposed_paths, &options.isolation_root); + expose_dirs( + &options.exposed_paths, + &options.isolation_root, + options.use_mount_for_binds, + ); } fn setup_panic_hook() { From b598499dcf76c9612fe5d097e9751eb81f1cc126 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 17:29:03 +0300 Subject: [PATCH 3/6] fmt --- minion-tests/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minion-tests/src/main.rs b/minion-tests/src/main.rs index d557b43e..a66ef085 100644 --- a/minion-tests/src/main.rs +++ b/minion-tests/src/main.rs @@ -106,4 +106,4 @@ fn configurations() -> Vec { let mut s_ext_mount = s.clone(); s_ext_mount.use_mount_program = true; vec![s, s_ext_mount] -} \ No newline at end of file +} From 74cb83a37b5e2db9eca766e0882a9da23ff53ef2 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 17:55:33 +0300 Subject: [PATCH 4/6] reorder --- src/linux/zygote/setup.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/linux/zygote/setup.rs b/src/linux/zygote/setup.rs index 02ae0ed9..e378f280 100644 --- a/src/linux/zygote/setup.rs +++ b/src/linux/zygote/setup.rs @@ -119,6 +119,7 @@ pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path, use_mount: boo fs::remove_dir(&bind_target).unwrap(); fs::write(&bind_target, &"").unwrap(); } + configure_dir(&bind_target).expect("failed to change access rights on the mount point"); if use_mount { expose_dir_via_mount(&bind_target, &x.src, x.kind) } else { @@ -249,10 +250,10 @@ pub(in crate::linux) fn setup( ) -> Result { setup_panic_hook(); setup_sighandler(); + setup_expositions(&jail_params); // must be done before `configure_dir`. setup_uid_mapping(sock)?; configure_dir(&jail_params.isolation_root)?; - setup_expositions(&jail_params); setup_procfs(&jail_params)?; let cgroup_join_handle = cgroup_driver.create_group( &jail_params.jail_id, From 30e04f9eb625e62e6c774bd60410d5c21439b460 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 18:01:32 +0300 Subject: [PATCH 5/6] not configure rights --- src/linux/zygote/setup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linux/zygote/setup.rs b/src/linux/zygote/setup.rs index e378f280..24c19e6e 100644 --- a/src/linux/zygote/setup.rs +++ b/src/linux/zygote/setup.rs @@ -119,7 +119,7 @@ pub(crate) fn expose_dirs(expose: &[SharedDir], jail_root: &Path, use_mount: boo fs::remove_dir(&bind_target).unwrap(); fs::write(&bind_target, &"").unwrap(); } - configure_dir(&bind_target).expect("failed to change access rights on the mount point"); + // configure_dir(&bind_target).expect("failed to change access rights on the mount point"); if use_mount { expose_dir_via_mount(&bind_target, &x.src, x.kind) } else { From f2ab1d110ab7ba763cf81caa9e066694cbcd3720 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 Jan 2021 18:12:08 +0300 Subject: [PATCH 6/6] fix log name --- minion-tests/src/master.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minion-tests/src/master.rs b/minion-tests/src/master.rs index 6923aec2..25d3fecf 100644 --- a/minion-tests/src/master.rs +++ b/minion-tests/src/master.rs @@ -84,7 +84,8 @@ fn execute_single_test( let mut cmd = if exec_opts.trace { let mut cmd = std::process::Command::new("strace"); cmd.arg("-f"); // follow forks - cmd.arg("-o").arg(format!("strace-log-{}.txt", case.name())); + cmd.arg("-o") + .arg(format!("strace-log-{}-{}.txt", case.name(), config_idx)); cmd.arg(self_exe); cmd } else {