From 6dc224fbd5f68e149877d2594523df8751332ac8 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Wed, 12 May 2021 17:06:25 -0700 Subject: Add minijail_copy_jail crosvm's main process sets up many identical jails. For example, by default there are four serial devices that use the same jail. Allowing a jail to be cloned places all that set up in one place. TEST=Added a marshall test as that infrastructure is re-used. BUG=b/187741537 Change-Id: I8743e35a47fb118d300e831d3dd6b5dc54cc7c34 --- libminijail.c | 20 ++++++++++++++++++++ libminijail.h | 10 ++++++++++ libminijail_unittest.cc | 4 ++++ rust/minijail-sys/libminijail.rs | 3 +++ rust/minijail/src/lib.rs | 25 +++++++++++++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/libminijail.c b/libminijail.c index b09a05c..0820dbb 100644 --- a/libminijail.c +++ b/libminijail.c @@ -2518,6 +2518,26 @@ error: return err; } +int API minijail_copy_jail(const struct minijail *from, struct minijail *out) +{ + size_t sz = minijail_size(from); + if (!sz) + return -EINVAL; + + char *buf = malloc(sz); + if (!buf) + return -ENOMEM; + + int err = minijail_marshal(from, buf, sz); + if (err) + goto error; + + err = minijail_unmarshal(out, buf, sz); +error: + free(buf); + return err; +} + static int setup_preload(const struct minijail *j attribute_unused, char ***child_env attribute_unused) { diff --git a/libminijail.h b/libminijail.h index 067fabd..cfd42d2 100644 --- a/libminijail.h +++ b/libminijail.h @@ -483,6 +483,16 @@ int minijail_wait(struct minijail *j); */ void minijail_destroy(struct minijail *j); +/* + * Deep copies the minijail in |from| to |out| providing two identical jails + * that can be used to contain separate children created with minijail_fork(). + * + * Duplicating a jail is invalid after a jail has been passed to + * minijail_fork(). Many minijail_*() calls will yield undefined + * results when called on a jail duplicated post-fork. + */ +int minijail_copy_jail(const struct minijail *from, struct minijail *out); + /* * minijail_log_to_fd: redirects the module-wide logging to an FD instead of * syslog. diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc index 521982f..78e3cfb 100644 --- a/libminijail_unittest.cc +++ b/libminijail_unittest.cc @@ -221,6 +221,10 @@ TEST_F(MarshalTest, 0xff) { EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_))); } +TEST_F(MarshalTest, copy_empty) { + ASSERT_EQ(0, minijail_copy_jail(m_, j_)); +} + TEST(KillTest, running_process) { const ScopedMinijail j(minijail_new()); char* const argv[] = {"sh", "-c", "sleep 1000", nullptr}; diff --git a/rust/minijail-sys/libminijail.rs b/rust/minijail-sys/libminijail.rs index aa613cb..594a479 100644 --- a/rust/minijail-sys/libminijail.rs +++ b/rust/minijail-sys/libminijail.rs @@ -285,6 +285,9 @@ extern "C" { writeable: ::std::os::raw::c_int, ) -> ::std::os::raw::c_int; } +extern "C" { + pub fn minijail_copy_jail(from: *const minijail, out: *mut minijail) -> ::std::os::raw::c_int; +} extern "C" { pub fn minijail_add_hook( j: *mut minijail, diff --git a/rust/minijail/src/lib.rs b/rust/minijail/src/lib.rs index ba9c8af..ba59075 100644 --- a/rust/minijail/src/lib.rs +++ b/rust/minijail/src/lib.rs @@ -266,6 +266,22 @@ impl Minijail { Ok(Minijail { jail: j }) } + /// Clones self to a new `Minijail`. Useful because `fork` can only be called once on a + /// `Minijail`. + pub fn try_clone(&self) -> Result { + let jail_out = Minijail::new()?; + unsafe { + // Safe to clone one minijail to the other as minijail_clone doesn't modify the source + // jail(`self`) and leaves a valid minijail in the destination(`jail_out`). + let ret = minijail_copy_jail(self.jail, jail_out.jail); + if ret < 0 { + return Err(Error::ReturnCode(ret as u8)); + } + } + + Ok(jail_out) + } + // The following functions are safe because they only set values in the // struct already owned by minijail. The struct's lifetime is tied to // `struct Minijail` so it is guaranteed to be valid @@ -990,6 +1006,15 @@ mod tests { j.run("/bin/true", &[], &EMPTY_STRING_SLICE).unwrap(); } + #[test] + fn run_clone() { + let j = Minijail::new().unwrap(); + let b = j.try_clone().unwrap(); + // Pass the same FDs to both clones and make sure they don't conflict. + j.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap(); + b.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap(); + } + #[test] fn run_string_vec() { let j = Minijail::new().unwrap(); -- cgit v1.2.3