summary refs log tree commit diff
path: root/io_jail
diff options
context:
space:
mode:
authorDylan Reid <dgreid@chromium.org>2017-09-26 13:49:42 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-10-25 05:52:42 -0700
commitd37aa9fab5dfa79e2859d86debd02ed11da932c9 (patch)
treeaf6d789f1009993884eb03651c8118b9d0a213c8 /io_jail
parent77ec85ea3bd9b0cf5e29f7365e7d00b3e4f882da (diff)
downloadcrosvm-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.rs83
-rw-r--r--io_jail/src/libminijail.rs5
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,