diff options
author | Dylan Reid <dgreid@chromium.org> | 2017-09-26 13:49:42 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-10-25 05:52:42 -0700 |
commit | d37aa9fab5dfa79e2859d86debd02ed11da932c9 (patch) | |
tree | af6d789f1009993884eb03651c8118b9d0a213c8 /io_jail | |
parent | 77ec85ea3bd9b0cf5e29f7365e7d00b3e4f882da (diff) | |
download | crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar.gz crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar.bz2 crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar.lz crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar.xz crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.tar.zst crosvm-d37aa9fab5dfa79e2859d86debd02ed11da932c9.zip |
Add ability to minijail_fork
Change-Id: I0c774816067449cbb838dcf29c6fa947ae5916e1 Reviewed-on: https://chromium-review.googlesource.com/719442 Commit-Ready: Dylan Reid <dgreid@chromium.org> Tested-by: Dylan Reid <dgreid@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'io_jail')
-rw-r--r-- | io_jail/src/lib.rs | 83 | ||||
-rw-r--r-- | io_jail/src/libminijail.rs | 5 |
2 files changed, 88 insertions, 0 deletions
diff --git a/io_jail/src/lib.rs b/io_jail/src/lib.rs index 460c22d..ec077d1 100644 --- a/io_jail/src/lib.rs +++ b/io_jail/src/lib.rs @@ -10,6 +10,7 @@ extern crate libc; #[allow(non_upper_case_globals)] mod libminijail; +use libc::pid_t; use std::fmt; use std::ffi::CString; use std::fs; @@ -26,8 +27,14 @@ pub enum Error { src: PathBuf, dst: PathBuf, }, + /// Failure to count the number of threads in /proc/self/tasks. + CheckingMultiThreaded(io::Error), /// minjail_new failed, this is an allocation failure. CreatingMinijail, + /// minijail_fork failed with the given error code. + ForkingMinijail(i32), + /// Attempt to `fork` while already multithreaded. + ForkingWhileMultiThreaded, /// The seccomp policy path doesn't exist. SeccompPath(PathBuf), /// The string passed in didn't parse to a valid CString. @@ -50,6 +57,8 @@ pub enum Error { ReadFdDir(io::Error), /// An entry in /proc/self/fd is not an integer ProcFd(String), + /// Minijail refused to preserve an FD in the inherit list of `fork()`. + PreservingFd(i32), } impl fmt::Display for Error { @@ -66,9 +75,18 @@ impl fmt::Display for Error { dst, errno) } + &Error::CheckingMultiThreaded(ref e) => { + write!(f, "Failed to count the number of threads from /proc/self/tasks {}", e) + }, &Error::CreatingMinijail => { write!(f, "minjail_new failed due to an allocation failure") } + &Error::ForkingMinijail(ref e) => { + write!(f, "minijail_fork failed with error {}", e) + }, + &Error::ForkingWhileMultiThreaded => { + write!(f, "Attempt to call fork() while multithreaded") + }, &Error::SeccompPath(ref p) => write!(f, "missing seccomp policy path: {:?}", p), &Error::StrToCString(ref s) => { write!(f, "failed to convert string into CString: {:?}", s) @@ -102,6 +120,9 @@ impl fmt::Display for Error { &Error::ProcFd(ref s) => { write!(f, "an entry in /proc/self/fd is not an integer: {}", s) } + &Error::PreservingFd(ref e) => { + write!(f, "fork failed in minijail_preserve_fd with error {}", e) + }, } } } @@ -341,6 +362,54 @@ impl Minijail { Ok(()) } + /// Forks a child and puts it in the previously configured minijail. + /// `fork` is unsafe because it closes all open FD for this process. That + /// could cause a lot of trouble if not handled carefully. FDs 0, 1, and 2 + /// are overwritten with /dev/null FDs unless they are included in the + /// inheritable_fds list. + /// This Function may abort in the child on error because a partially + /// entered jail isn't recoverable. + pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> { + if !is_single_threaded().map_err(Error::CheckingMultiThreaded)? { + return Err(Error::ForkingWhileMultiThreaded); + } + + if let Some(keep_fds) = inheritable_fds { + for fd in keep_fds { + let ret = libminijail::minijail_preserve_fd(self.jail, *fd, *fd); + if ret < 0 { + return Err(Error::PreservingFd(ret)); + } + } + } + + let dev_null = fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/null") + .map_err(Error::OpenDevNull)?; + // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list. + // These will only be closed when this process exits. + for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] { + if inheritable_fds.is_none() || !inheritable_fds.unwrap().contains(io_fd) { + let ret = libminijail::minijail_preserve_fd(self.jail, + dev_null.as_raw_fd(), + *io_fd); + if ret < 0 { + return Err(Error::PreservingFd(ret)); + } + } + } + + libminijail::minijail_close_open_fds(self.jail); + + let ret = libminijail::minijail_fork(self.jail); + if ret < 0 { + return Err(Error::ForkingMinijail(ret)); + } + Ok(ret as pid_t) + } + // Closing all open FDs could be unsafe if something is relying on an FD // that is closed unexpectedly. It is safe as long as it is called // immediately after forking and all needed FDs are in `inheritable_fds`. @@ -390,6 +459,20 @@ impl Drop for Minijail { } } +// Count the number of files in the directory specified by `path`. +fn count_dir_entries<P: AsRef<Path>>(path: P) -> io::Result<usize> { + Ok(fs::read_dir(path)?.count()) +} + +// Return true if the current thread is the only thread in the process. +fn is_single_threaded() -> io::Result<bool> { + match count_dir_entries("/proc/self/task") { + Ok(1) => Ok(true), + Ok(_) => Ok(false), + Err(e) => Err(e), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/io_jail/src/libminijail.rs b/io_jail/src/libminijail.rs index 803a4df..fbc5dc8 100644 --- a/io_jail/src/libminijail.rs +++ b/io_jail/src/libminijail.rs @@ -74,6 +74,7 @@ extern "C" { pub fn minijail_enter_pivot_root(j: *mut minijail, dir: *const c_char) -> c_int; + pub fn minijail_fork(j: *mut minijail) -> pid_t; pub fn minijail_get_original_path(j: *mut minijail, chroot_path: *const c_char) -> *mut c_char; @@ -97,6 +98,10 @@ extern "C" { dest: *const c_char, writeable: c_int) -> c_int; + pub fn minijail_preserve_fd(j: *mut minijail, + parent_fd: c_int, + child_fd: c_int) + -> c_int; pub fn minijail_enter(j: *const minijail); pub fn minijail_run(j: *mut minijail, filename: *const c_char, |