• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::ffi::CString;
6 use std::fmt::{self, Display};
7 use std::fs;
8 use std::io;
9 use std::os::raw::{c_char, c_int, c_ulong, c_ushort};
10 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
11 use std::path::{Path, PathBuf};
12 use std::ptr::{null, null_mut};
13 use std::result::Result as StdResult;
14 use std::sync::Once;
15 
16 use libc::pid_t;
17 use minijail_sys::*;
18 
19 enum Program {
20     Filename(PathBuf),
21     FileDescriptor(RawFd),
22 }
23 
24 /// Configuration of a command to be run in a jail.
25 pub struct Command {
26     program: Program,
27     preserve_fds: Vec<(RawFd, RawFd)>,
28 
29     // Ownership of the backing data of args_cptr is provided by args_cstr.
30     args_cstr: Vec<CString>,
31     args_cptr: Vec<*const c_char>,
32 
33     // Ownership of the backing data of env_cptr is provided by env_cstr.
34     env_cstr: Option<Vec<CString>>,
35     env_cptr: Option<Vec<*const c_char>>,
36 }
37 
38 impl Command {
39     /// This exposes a subset of what Command can do, before we are ready to commit to a stable
40     /// API.
new_for_path<P: AsRef<Path>, S: AsRef<str>, A: AsRef<str>>( path: P, keep_fds: &[RawFd], args: &[S], env_vars: Option<&[A]>, ) -> Result<Command>41     pub fn new_for_path<P: AsRef<Path>, S: AsRef<str>, A: AsRef<str>>(
42         path: P,
43         keep_fds: &[RawFd],
44         args: &[S],
45         env_vars: Option<&[A]>,
46     ) -> Result<Command> {
47         let mut cmd = Command::new(Program::Filename(path.as_ref().to_path_buf()))
48             .keep_fds(keep_fds)
49             .args(args)?;
50         if let Some(env_vars) = env_vars {
51             cmd = cmd.envs(env_vars)?;
52         }
53 
54         Ok(cmd)
55     }
56 
new(program: Program) -> Command57     fn new(program: Program) -> Command {
58         Command {
59             program,
60             preserve_fds: Vec::new(),
61             args_cstr: Vec::new(),
62             args_cptr: Vec::new(),
63             env_cstr: None,
64             env_cptr: None,
65         }
66     }
67 
keep_fds(mut self, keep_fds: &[RawFd]) -> Command68     fn keep_fds(mut self, keep_fds: &[RawFd]) -> Command {
69         self.preserve_fds = keep_fds
70             .iter()
71             .map(|&a| (a, a))
72             .collect::<Vec<(RawFd, RawFd)>>();
73         self
74     }
75 
remap_fds(mut self, remap_fds: &[(RawFd, RawFd)]) -> Command76     fn remap_fds(mut self, remap_fds: &[(RawFd, RawFd)]) -> Command {
77         self.preserve_fds = remap_fds.to_vec();
78         self
79     }
80 
args<S: AsRef<str>>(mut self, args: &[S]) -> Result<Command>81     fn args<S: AsRef<str>>(mut self, args: &[S]) -> Result<Command> {
82         let (args_cstr, args_cptr) = to_execve_cstring_array(args)?;
83         self.args_cstr = args_cstr;
84         self.args_cptr = args_cptr;
85         Ok(self)
86     }
87 
envs<S: AsRef<str>>(mut self, vars: &[S]) -> Result<Command>88     fn envs<S: AsRef<str>>(mut self, vars: &[S]) -> Result<Command> {
89         let (env_cstr, env_cptr) = to_execve_cstring_array(vars)?;
90         self.env_cstr = Some(env_cstr);
91         self.env_cptr = Some(env_cptr);
92         Ok(self)
93     }
94 
argv(&self) -> *const *mut c_char95     fn argv(&self) -> *const *mut c_char {
96         self.args_cptr.as_ptr() as *const *mut c_char
97     }
98 
envp(&self) -> *const *mut c_char99     fn envp(&self) -> *const *mut c_char {
100         (match self.env_cptr {
101             Some(ref env_cptr) => env_cptr.as_ptr(),
102             None => null_mut(),
103         }) as *const *mut c_char
104     }
105 }
106 
107 /// Abstracts paths and executable file descriptors in a way that the run implementation can cover
108 /// both.
109 trait Runnable {
run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t>110     fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t>;
111 }
112 
113 impl Runnable for &Path {
run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t>114     fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t> {
115         let path_str = self
116             .to_str()
117             .ok_or_else(|| Error::PathToCString(self.to_path_buf()))?;
118         let path_cstr =
119             CString::new(path_str).map_err(|_| Error::StrToCString(path_str.to_owned()))?;
120 
121         let mut pid: pid_t = 0;
122         let ret = unsafe {
123             minijail_run_env_pid_pipes(
124                 jail.jail,
125                 path_cstr.as_ptr(),
126                 cmd.argv(),
127                 cmd.envp(),
128                 &mut pid,
129                 null_mut(),
130                 null_mut(),
131                 null_mut(),
132             )
133         };
134         if ret < 0 {
135             return Err(Error::ForkingMinijail(ret));
136         }
137         Ok(pid)
138     }
139 }
140 
141 impl Runnable for RawFd {
run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t>142     fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t> {
143         let mut pid: pid_t = 0;
144         let ret = unsafe {
145             minijail_run_fd_env_pid_pipes(
146                 jail.jail,
147                 *self,
148                 cmd.argv(),
149                 cmd.envp(),
150                 &mut pid,
151                 null_mut(),
152                 null_mut(),
153                 null_mut(),
154             )
155         };
156         if ret < 0 {
157             return Err(Error::ForkingMinijail(ret));
158         }
159         Ok(pid)
160     }
161 }
162 
163 #[derive(Debug)]
164 pub enum Error {
165     // minijail failed to accept bind mount.
166     BindMount {
167         errno: i32,
168         src: PathBuf,
169         dst: PathBuf,
170     },
171     // minijail failed to accept mount.
172     Mount {
173         errno: i32,
174         src: PathBuf,
175         dest: PathBuf,
176         fstype: String,
177         flags: usize,
178         data: String,
179     },
180     /// Failure to count the number of threads in /proc/self/tasks.
181     CheckingMultiThreaded(io::Error),
182     /// minjail_new failed, this is an allocation failure.
183     CreatingMinijail,
184     /// minijail_fork failed with the given error code.
185     ForkingMinijail(i32),
186     /// Attempt to `fork` while already multithreaded.
187     ForkingWhileMultiThreaded,
188     /// The seccomp policy path doesn't exist.
189     SeccompPath(PathBuf),
190     /// The string passed in didn't parse to a valid CString.
191     StrToCString(String),
192     /// The path passed in didn't parse to a valid CString.
193     PathToCString(PathBuf),
194     /// Failed to call dup2 to set stdin, stdout, or stderr to /dev/null.
195     DupDevNull(i32),
196     /// Failed to set up /dev/null for FDs 0, 1, or 2.
197     OpenDevNull(io::Error),
198     /// Failed to read policy bpf from file.
199     ReadProgram(io::Error),
200     /// Setting the specified alt-syscall table failed with errno. Is the table in the kernel?
201     SetAltSyscallTable { errno: i32, name: String },
202     /// Setting the specified rlimit failed with errno.
203     SetRlimit { errno: i32, kind: libc::c_int },
204     /// chroot failed with the provided errno.
205     SettingChrootDirectory(i32, PathBuf),
206     /// pivot_root failed with the provided errno.
207     SettingPivotRootDirectory(i32, PathBuf),
208     /// There is an entry in /proc/self/fd that isn't a valid PID.
209     ReadFdDirEntry(io::Error),
210     /// /proc/self/fd failed to open.
211     ReadFdDir(io::Error),
212     /// An entry in /proc/self/fd is not an integer
213     ProcFd(String),
214     /// Minijail refused to preserve an FD in the inherit list of `fork()`.
215     PreservingFd(i32),
216     /// Program size is too large
217     ProgramTooLarge,
218     /// Alignment of file should be divisible by the alignment of sock_filter.
219     WrongProgramAlignment,
220     /// File size should be non-zero and a multiple of sock_filter
221     WrongProgramSize,
222 
223     /// The command was not found.
224     NoCommand,
225     /// The command could not be run.
226     NoAccess,
227     /// Process was killed by SIGSYS indicating a seccomp violation.
228     SeccompViolation(i32),
229     /// Process was killed by a signal other than SIGSYS.
230     Killed(u8),
231     /// Process finished returning a non-zero code.
232     ReturnCode(u8),
233     /// Failed to wait the process.
234     Wait(i32),
235     /// Failed to clone the log fd
236     CloneLogFd(io::Error),
237 }
238 
239 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result240     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241         use self::Error::*;
242 
243         match self {
244             BindMount { src, dst, errno } => write!(
245                 f,
246                 "failed to accept bind mount {} -> {}: {}",
247                 src.display(),
248                 dst.display(),
249                 io::Error::from_raw_os_error(*errno),
250             ),
251             Mount {
252                 errno,
253                 src,
254                 dest,
255                 fstype,
256                 flags,
257                 data,
258             } => write!(
259                 f,
260                 "failed to accept mount {} -> {} of type {:?} with flags 0x{:x} \
261                  and data {:?}: {}",
262                 src.display(),
263                 dest.display(),
264                 fstype,
265                 flags,
266                 data,
267                 io::Error::from_raw_os_error(*errno),
268             ),
269             CheckingMultiThreaded(e) => write!(
270                 f,
271                 "Failed to count the number of threads from /proc/self/tasks {}",
272                 e
273             ),
274             CreatingMinijail => write!(f, "minjail_new failed due to an allocation failure"),
275             ForkingMinijail(e) => write!(f, "minijail_fork failed with error {}", e),
276             ForkingWhileMultiThreaded => write!(f, "Attempt to call fork() while multithreaded"),
277             SeccompPath(p) => write!(f, "missing seccomp policy path: {}", p.display()),
278             StrToCString(s) => write!(f, "failed to convert string into CString: {}", s),
279             PathToCString(s) => write!(f, "failed to convert path into CString: {}", s.display()),
280             DupDevNull(errno) => write!(
281                 f,
282                 "failed to call dup2 to set stdin, stdout, or stderr to /dev/null: {}",
283                 io::Error::from_raw_os_error(*errno),
284             ),
285             OpenDevNull(e) => write!(
286                 f,
287                 "fail to open /dev/null for setting FDs 0, 1, or 2: {}",
288                 e,
289             ),
290             ReadProgram(e) => write!(f, "failed to read from bpf file: {}", e),
291             SetAltSyscallTable { name, errno } => write!(
292                 f,
293                 "failed to set alt-syscall table {}: {}",
294                 name,
295                 io::Error::from_raw_os_error(*errno),
296             ),
297             SetRlimit { errno, kind } => write!(f, "failed to set rlimit {}: {}", kind, errno),
298             SettingChrootDirectory(errno, p) => write!(
299                 f,
300                 "failed to set chroot {}: {}",
301                 p.display(),
302                 io::Error::from_raw_os_error(*errno),
303             ),
304             SettingPivotRootDirectory(errno, p) => write!(
305                 f,
306                 "failed to set pivot root {}: {}",
307                 p.display(),
308                 io::Error::from_raw_os_error(*errno),
309             ),
310             ReadFdDirEntry(e) => write!(f, "failed to read an entry in /proc/self/fd: {}", e),
311             ReadFdDir(e) => write!(f, "failed to open /proc/self/fd: {}", e),
312             ProcFd(s) => write!(f, "an entry in /proc/self/fd is not an integer: {}", s),
313             PreservingFd(e) => write!(f, "fork failed in minijail_preserve_fd with error {}", e),
314             ProgramTooLarge => write!(f, "bpf program is too large (max 64K instructions)"),
315             WrongProgramAlignment => write!(
316                 f,
317                 "the alignment of bpf file was not a multiple of that of sock_filter"
318             ),
319             WrongProgramSize => write!(f, "bpf file was empty or not a multiple of sock_filter"),
320             NoCommand => write!(f, "command was not found"),
321             NoAccess => write!(f, "unable to execute command"),
322             SeccompViolation(s) => write!(f, "seccomp violation syscall #{}", s),
323             Killed(s) => write!(f, "killed with signal number {}", s),
324             ReturnCode(e) => write!(f, "exited with code {}", e),
325             Wait(errno) => write!(
326                 f,
327                 "failed to wait: {}",
328                 io::Error::from_raw_os_error(*errno)
329             ),
330             CloneLogFd(e) => write!(f, "failed to clone log fd: {}", e),
331         }
332     }
333 }
334 
335 impl std::error::Error for Error {}
336 
337 pub type Result<T> = StdResult<T, Error>;
338 
339 /// Configuration to jail a process based on wrapping libminijail.
340 ///
341 /// Intentionally leave out everything related to `minijail_run`.  Forking is
342 /// hard to reason about w.r.t. memory and resource safety.  It is better to avoid
343 /// forking from rust code.  Leave forking to the library user, who can make
344 /// an informed decision about when to fork to minimize risk.
345 /// # Examples
346 /// * Load seccomp policy - like "minijail0 -n -S myfilter.policy"
347 ///
348 /// ```
349 /// # use minijail::Minijail;
350 /// # fn seccomp_filter_test() -> Result<(), ()> {
351 ///       let mut j = Minijail::new().map_err(|_| ())?;
352 ///       j.no_new_privs();
353 ///       j.parse_seccomp_filters("my_filter.policy").map_err(|_| ())?;
354 ///       j.use_seccomp_filter();
355 ///       unsafe { // `fork` will close all the programs FDs.
356 ///           j.fork(None).map_err(|_| ())?;
357 ///       }
358 /// #     Ok(())
359 /// # }
360 /// ```
361 ///
362 /// * Keep stdin, stdout, and stderr open after jailing.
363 ///
364 /// ```
365 /// # use minijail::Minijail;
366 /// # use std::os::unix::io::RawFd;
367 /// # fn seccomp_filter_test() -> Result<(), ()> {
368 ///       let j = Minijail::new().map_err(|_| ())?;
369 ///       let preserve_fds: Vec<RawFd> = vec![0, 1, 2];
370 ///       unsafe { // `fork` will close all the programs FDs.
371 ///           j.fork(Some(&preserve_fds)).map_err(|_| ())?;
372 ///       }
373 /// #     Ok(())
374 /// # }
375 /// ```
376 /// # Errors
377 /// The `fork` function might not return an error if it fails after forking. A
378 /// partial jail is not recoverable and will instead result in killing the
379 /// process.
380 pub struct Minijail {
381     jail: *mut minijail,
382     disable_multithreaded_check: bool,
383     // Meant to hold on to log_fd for the duration of the Minijail
384     // Is a combination of log_fd and min priority.
385     log_fd: Option<(std::os::fd::OwnedFd, libc::c_int)>,
386 }
387 
388 #[link(name = "c")]
389 extern "C" {
__libc_current_sigrtmax() -> libc::c_int390     fn __libc_current_sigrtmax() -> libc::c_int;
391 }
392 
translate_wait_error(ret: libc::c_int) -> Result<()>393 fn translate_wait_error(ret: libc::c_int) -> Result<()> {
394     if ret == 0 {
395         return Ok(());
396     }
397     if ret < 0 {
398         return Err(Error::Wait(ret));
399     }
400     if ret == MINIJAIL_ERR_NO_COMMAND as libc::c_int {
401         return Err(Error::NoCommand);
402     }
403     if ret == MINIJAIL_ERR_NO_ACCESS as libc::c_int {
404         return Err(Error::NoAccess);
405     }
406     let sig_base: libc::c_int = MINIJAIL_ERR_SIG_BASE as libc::c_int;
407     let sig_max_code: libc::c_int = unsafe { __libc_current_sigrtmax() } + sig_base;
408     if ret > sig_base && ret <= sig_max_code {
409         return Err(Error::Killed(
410             (ret - MINIJAIL_ERR_SIG_BASE as libc::c_int) as u8,
411         ));
412     }
413     if ret > 0 && ret <= 0xff {
414         return Err(Error::ReturnCode(ret as u8));
415     }
416     unreachable!("Unexpected returned value from wait: {}", ret);
417 }
418 
419 impl Minijail {
420     /// Creates a new jail configuration.
new() -> Result<Minijail>421     pub fn new() -> Result<Minijail> {
422         let j = unsafe {
423             // libminijail actually owns the minijail structure. It will live until we call
424             // minijail_destroy.
425             minijail_new()
426         };
427         if j.is_null() {
428             return Err(Error::CreatingMinijail);
429         }
430         Ok(Minijail {
431             jail: j,
432             disable_multithreaded_check: false,
433             log_fd: None,
434         })
435     }
436 
437     /// Clones self to a new `Minijail`. Useful because `fork` can only be called once on a
438     /// `Minijail`.
try_clone(&self) -> Result<Minijail>439     pub fn try_clone(&self) -> Result<Minijail> {
440         let mut jail_out = Minijail::new()?;
441         jail_out.disable_multithreaded_check = self.disable_multithreaded_check;
442         unsafe {
443             // Safe to clone one minijail to the other as minijail_clone doesn't modify the source
444             // jail(`self`) and leaves a valid minijail in the destination(`jail_out`).
445             let ret = minijail_copy_jail(self.jail, jail_out.jail);
446             if ret < 0 {
447                 return Err(Error::ReturnCode(ret as u8));
448             }
449         }
450 
451         if let Some((log_fd, min_priority)) = &self.log_fd {
452             jail_out.log_to_fd(
453                 log_fd.try_clone().map_err(Error::CloneLogFd)?,
454                 *min_priority,
455             );
456         }
457 
458         Ok(jail_out)
459     }
460 
461     // The following functions are safe because they only set values in the
462     // struct already owned by minijail.  The struct's lifetime is tied to
463     // `struct Minijail` so it is guaranteed to be valid
464 
change_uid(&mut self, uid: libc::uid_t)465     pub fn change_uid(&mut self, uid: libc::uid_t) {
466         unsafe {
467             minijail_change_uid(self.jail, uid);
468         }
469     }
change_gid(&mut self, gid: libc::gid_t)470     pub fn change_gid(&mut self, gid: libc::gid_t) {
471         unsafe {
472             minijail_change_gid(self.jail, gid);
473         }
474     }
change_user(&mut self, user: &str) -> Result<()>475     pub fn change_user(&mut self, user: &str) -> Result<()> {
476         let user_cstring = CString::new(user).map_err(|_| Error::StrToCString(user.to_owned()))?;
477         unsafe {
478             minijail_change_user(self.jail, user_cstring.as_ptr());
479         }
480         Ok(())
481     }
change_group(&mut self, group: &str) -> Result<()>482     pub fn change_group(&mut self, group: &str) -> Result<()> {
483         let group_cstring =
484             CString::new(group).map_err(|_| Error::StrToCString(group.to_owned()))?;
485         unsafe {
486             minijail_change_group(self.jail, group_cstring.as_ptr());
487         }
488         Ok(())
489     }
set_supplementary_gids(&mut self, ids: &[libc::gid_t])490     pub fn set_supplementary_gids(&mut self, ids: &[libc::gid_t]) {
491         unsafe {
492             minijail_set_supplementary_gids(self.jail, ids.len(), ids.as_ptr());
493         }
494     }
keep_supplementary_gids(&mut self)495     pub fn keep_supplementary_gids(&mut self) {
496         unsafe {
497             minijail_keep_supplementary_gids(self.jail);
498         }
499     }
500     // rlim_t is defined in minijail-sys to be u64 on all platforms, to avoid
501     // issues on 32-bit platforms. It's also useful to us here to avoid
502     // libc::rlim64_t, which is not defined at all on Android.
set_rlimit(&mut self, kind: libc::c_int, cur: rlim_t, max: rlim_t) -> Result<()>503     pub fn set_rlimit(&mut self, kind: libc::c_int, cur: rlim_t, max: rlim_t) -> Result<()> {
504         let errno = unsafe { minijail_rlimit(self.jail, kind, cur, max) };
505         if errno == 0 {
506             Ok(())
507         } else {
508             Err(Error::SetRlimit { errno, kind })
509         }
510     }
use_seccomp(&mut self)511     pub fn use_seccomp(&mut self) {
512         unsafe {
513             minijail_use_seccomp(self.jail);
514         }
515     }
no_new_privs(&mut self)516     pub fn no_new_privs(&mut self) {
517         unsafe {
518             minijail_no_new_privs(self.jail);
519         }
520     }
use_seccomp_filter(&mut self)521     pub fn use_seccomp_filter(&mut self) {
522         unsafe {
523             minijail_use_seccomp_filter(self.jail);
524         }
525     }
set_seccomp_filter_tsync(&mut self)526     pub fn set_seccomp_filter_tsync(&mut self) {
527         unsafe {
528             minijail_set_seccomp_filter_tsync(self.jail);
529         }
530     }
parse_seccomp_program<P: AsRef<Path>>(&mut self, path: P) -> Result<()>531     pub fn parse_seccomp_program<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
532         if !path.as_ref().is_file() {
533             return Err(Error::SeccompPath(path.as_ref().to_owned()));
534         }
535 
536         let buffer = fs::read(path).map_err(Error::ReadProgram)?;
537         self.parse_seccomp_bytes(&buffer)
538     }
parse_seccomp_bytes(&mut self, buffer: &[u8]) -> Result<()>539     pub fn parse_seccomp_bytes(&mut self, buffer: &[u8]) -> Result<()> {
540         if buffer.len() % std::mem::size_of::<sock_filter>() != 0 {
541             return Err(Error::WrongProgramSize);
542         }
543         let count = buffer.len() / std::mem::size_of::<sock_filter>();
544         if count > (!0 as u16) as usize {
545             return Err(Error::ProgramTooLarge);
546         }
547         if buffer.as_ptr() as usize % std::mem::align_of::<sock_filter>() != 0 {
548             return Err(Error::WrongProgramAlignment);
549         }
550 
551         // Safe cast because we checked that the buffer address is divisible by the alignment of
552         // sock_filter.
553         #[allow(clippy::cast_ptr_alignment)]
554         let header = sock_fprog {
555             len: count as c_ushort,
556             filter: buffer.as_ptr() as *mut sock_filter,
557         };
558         unsafe {
559             minijail_set_seccomp_filters(self.jail, &header);
560         }
561         Ok(())
562     }
parse_seccomp_filters<P: AsRef<Path>>(&mut self, path: P) -> Result<()>563     pub fn parse_seccomp_filters<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
564         if !path.as_ref().is_file() {
565             return Err(Error::SeccompPath(path.as_ref().to_owned()));
566         }
567 
568         let pathstring = path
569             .as_ref()
570             .as_os_str()
571             .to_str()
572             .ok_or_else(|| Error::PathToCString(path.as_ref().to_owned()))?;
573         let filename =
574             CString::new(pathstring).map_err(|_| Error::PathToCString(path.as_ref().to_owned()))?;
575         unsafe {
576             minijail_parse_seccomp_filters(self.jail, filename.as_ptr());
577         }
578         Ok(())
579     }
log_seccomp_filter_failures(&mut self)580     pub fn log_seccomp_filter_failures(&mut self) {
581         unsafe {
582             minijail_log_seccomp_filter_failures(self.jail);
583         }
584     }
use_caps(&mut self, capmask: u64)585     pub fn use_caps(&mut self, capmask: u64) {
586         unsafe {
587             minijail_use_caps(self.jail, capmask);
588         }
589     }
capbset_drop(&mut self, capmask: u64)590     pub fn capbset_drop(&mut self, capmask: u64) {
591         unsafe {
592             minijail_capbset_drop(self.jail, capmask);
593         }
594     }
set_ambient_caps(&mut self)595     pub fn set_ambient_caps(&mut self) {
596         unsafe {
597             minijail_set_ambient_caps(self.jail);
598         }
599     }
reset_signal_mask(&mut self)600     pub fn reset_signal_mask(&mut self) {
601         unsafe {
602             minijail_reset_signal_mask(self.jail);
603         }
604     }
run_as_init(&mut self)605     pub fn run_as_init(&mut self) {
606         unsafe {
607             minijail_run_as_init(self.jail);
608         }
609     }
namespace_pids(&mut self)610     pub fn namespace_pids(&mut self) {
611         unsafe {
612             minijail_namespace_pids(self.jail);
613         }
614     }
namespace_user(&mut self)615     pub fn namespace_user(&mut self) {
616         unsafe {
617             minijail_namespace_user(self.jail);
618         }
619     }
namespace_user_disable_setgroups(&mut self)620     pub fn namespace_user_disable_setgroups(&mut self) {
621         unsafe {
622             minijail_namespace_user_disable_setgroups(self.jail);
623         }
624     }
namespace_vfs(&mut self)625     pub fn namespace_vfs(&mut self) {
626         unsafe {
627             minijail_namespace_vfs(self.jail);
628         }
629     }
new_session_keyring(&mut self)630     pub fn new_session_keyring(&mut self) {
631         unsafe {
632             minijail_new_session_keyring(self.jail);
633         }
634     }
skip_remount_private(&mut self)635     pub fn skip_remount_private(&mut self) {
636         unsafe {
637             minijail_skip_remount_private(self.jail);
638         }
639     }
namespace_ipc(&mut self)640     pub fn namespace_ipc(&mut self) {
641         unsafe {
642             minijail_namespace_ipc(self.jail);
643         }
644     }
namespace_net(&mut self)645     pub fn namespace_net(&mut self) {
646         unsafe {
647             minijail_namespace_net(self.jail);
648         }
649     }
namespace_cgroups(&mut self)650     pub fn namespace_cgroups(&mut self) {
651         unsafe {
652             minijail_namespace_cgroups(self.jail);
653         }
654     }
remount_proc_readonly(&mut self)655     pub fn remount_proc_readonly(&mut self) {
656         unsafe {
657             minijail_remount_proc_readonly(self.jail);
658         }
659     }
set_remount_mode(&mut self, mode: c_ulong)660     pub fn set_remount_mode(&mut self, mode: c_ulong) {
661         unsafe { minijail_remount_mode(self.jail, mode) }
662     }
uidmap(&mut self, uid_map: &str) -> Result<()>663     pub fn uidmap(&mut self, uid_map: &str) -> Result<()> {
664         let map_cstring =
665             CString::new(uid_map).map_err(|_| Error::StrToCString(uid_map.to_owned()))?;
666         unsafe {
667             minijail_uidmap(self.jail, map_cstring.as_ptr());
668         }
669         Ok(())
670     }
gidmap(&mut self, gid_map: &str) -> Result<()>671     pub fn gidmap(&mut self, gid_map: &str) -> Result<()> {
672         let map_cstring =
673             CString::new(gid_map).map_err(|_| Error::StrToCString(gid_map.to_owned()))?;
674         unsafe {
675             minijail_gidmap(self.jail, map_cstring.as_ptr());
676         }
677         Ok(())
678     }
inherit_usergroups(&mut self)679     pub fn inherit_usergroups(&mut self) {
680         unsafe {
681             minijail_inherit_usergroups(self.jail);
682         }
683     }
use_alt_syscall(&mut self, table_name: &str) -> Result<()>684     pub fn use_alt_syscall(&mut self, table_name: &str) -> Result<()> {
685         let table_name_string =
686             CString::new(table_name).map_err(|_| Error::StrToCString(table_name.to_owned()))?;
687         let ret = unsafe { minijail_use_alt_syscall(self.jail, table_name_string.as_ptr()) };
688         if ret < 0 {
689             return Err(Error::SetAltSyscallTable {
690                 errno: ret,
691                 name: table_name.to_owned(),
692             });
693         }
694         Ok(())
695     }
log_to_fd(&mut self, fd: std::os::fd::OwnedFd, min_priority: c_int)696     pub fn log_to_fd(&mut self, fd: std::os::fd::OwnedFd, min_priority: c_int) {
697         // Minijail doesn't close the fd when it is destroyed, so this is safe.
698         unsafe {
699             minijail_log_to_fd(fd.as_raw_fd(), min_priority);
700         }
701         // minijail_log_to_fd "borrows" the fd  (in Rust parlance), so we need to store the
702         // fd as long as Minijail is alive for correctness.
703         self.log_fd = Some((fd, min_priority));
704     }
705 
enter_chroot<P: AsRef<Path>>(&mut self, dir: P) -> Result<()>706     pub fn enter_chroot<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
707         let pathstring = dir
708             .as_ref()
709             .as_os_str()
710             .to_str()
711             .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?;
712         let dirname =
713             CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?;
714         let ret = unsafe { minijail_enter_chroot(self.jail, dirname.as_ptr()) };
715         if ret < 0 {
716             return Err(Error::SettingChrootDirectory(ret, dir.as_ref().to_owned()));
717         }
718         Ok(())
719     }
enter_pivot_root<P: AsRef<Path>>(&mut self, dir: P) -> Result<()>720     pub fn enter_pivot_root<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
721         let pathstring = dir
722             .as_ref()
723             .as_os_str()
724             .to_str()
725             .ok_or_else(|| Error::PathToCString(dir.as_ref().to_owned()))?;
726         let dirname =
727             CString::new(pathstring).map_err(|_| Error::PathToCString(dir.as_ref().to_owned()))?;
728         let ret = unsafe { minijail_enter_pivot_root(self.jail, dirname.as_ptr()) };
729         if ret < 0 {
730             return Err(Error::SettingPivotRootDirectory(
731                 ret,
732                 dir.as_ref().to_owned(),
733             ));
734         }
735         Ok(())
736     }
mount<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, ) -> Result<()>737     pub fn mount<P1: AsRef<Path>, P2: AsRef<Path>>(
738         &mut self,
739         src: P1,
740         dest: P2,
741         fstype: &str,
742         flags: usize,
743     ) -> Result<()> {
744         self.mount_with_data(src, dest, fstype, flags, "")
745     }
mount_with_data<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, fstype: &str, flags: usize, data: &str, ) -> Result<()>746     pub fn mount_with_data<P1: AsRef<Path>, P2: AsRef<Path>>(
747         &mut self,
748         src: P1,
749         dest: P2,
750         fstype: &str,
751         flags: usize,
752         data: &str,
753     ) -> Result<()> {
754         let src_os = src
755             .as_ref()
756             .as_os_str()
757             .to_str()
758             .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?;
759         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
760         let dest_os = dest
761             .as_ref()
762             .as_os_str()
763             .to_str()
764             .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?;
765         let dest_path =
766             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
767         let fstype_string =
768             CString::new(fstype).map_err(|_| Error::StrToCString(fstype.to_owned()))?;
769         let data_string = CString::new(data).map_err(|_| Error::StrToCString(data.to_owned()))?;
770         let ret = unsafe {
771             minijail_mount_with_data(
772                 self.jail,
773                 src_path.as_ptr(),
774                 dest_path.as_ptr(),
775                 fstype_string.as_ptr(),
776                 flags as _,
777                 data_string.as_ptr(),
778             )
779         };
780         if ret < 0 {
781             return Err(Error::Mount {
782                 errno: ret,
783                 src: src.as_ref().to_owned(),
784                 dest: dest.as_ref().to_owned(),
785                 fstype: fstype.to_owned(),
786                 flags,
787                 data: data.to_owned(),
788             });
789         }
790         Ok(())
791     }
mount_dev(&mut self)792     pub fn mount_dev(&mut self) {
793         unsafe {
794             minijail_mount_dev(self.jail);
795         }
796     }
mount_tmp(&mut self)797     pub fn mount_tmp(&mut self) {
798         unsafe {
799             minijail_mount_tmp(self.jail);
800         }
801     }
mount_tmp_size(&mut self, size: usize)802     pub fn mount_tmp_size(&mut self, size: usize) {
803         unsafe {
804             minijail_mount_tmp_size(self.jail, size);
805         }
806     }
mount_bind<P1: AsRef<Path>, P2: AsRef<Path>>( &mut self, src: P1, dest: P2, writable: bool, ) -> Result<()>807     pub fn mount_bind<P1: AsRef<Path>, P2: AsRef<Path>>(
808         &mut self,
809         src: P1,
810         dest: P2,
811         writable: bool,
812     ) -> Result<()> {
813         let src_os = src
814             .as_ref()
815             .as_os_str()
816             .to_str()
817             .ok_or_else(|| Error::PathToCString(src.as_ref().to_owned()))?;
818         let src_path = CString::new(src_os).map_err(|_| Error::StrToCString(src_os.to_owned()))?;
819         let dest_os = dest
820             .as_ref()
821             .as_os_str()
822             .to_str()
823             .ok_or_else(|| Error::PathToCString(dest.as_ref().to_owned()))?;
824         let dest_path =
825             CString::new(dest_os).map_err(|_| Error::StrToCString(dest_os.to_owned()))?;
826         let ret = unsafe {
827             minijail_bind(
828                 self.jail,
829                 src_path.as_ptr(),
830                 dest_path.as_ptr(),
831                 writable as _,
832             )
833         };
834         if ret < 0 {
835             return Err(Error::BindMount {
836                 errno: ret,
837                 src: src.as_ref().to_owned(),
838                 dst: dest.as_ref().to_owned(),
839             });
840         }
841         Ok(())
842     }
843 
844     /// Disables the check that prevents forking in a multithreaded environment.
845     /// This is only safe if the child process calls exec immediately after
846     /// forking. The state of locks, and whether or not they will unlock
847     /// is undefined. Additionally, objects allocated on other threads that
848     /// expect to be dropped when those threads cease execution will not be
849     /// dropped.
850     /// Thus, nothing should be called that relies on shared synchronization
851     /// primitives referenced outside of the current thread. The safest
852     /// way to use this is to immediately exec in the child.
disable_multithreaded_check(&mut self)853     pub fn disable_multithreaded_check(&mut self) {
854         self.disable_multithreaded_check = true;
855     }
856 
857     /// Forks and execs a child and puts it in the previously configured minijail.
858     /// FDs 0, 1, and 2 are overwritten with /dev/null FDs unless they are included in the
859     /// inheritable_fds list. This function may abort in the child on error because a partially
860     /// entered jail isn't recoverable.
run<P: AsRef<Path>, S: AsRef<str>>( &self, cmd: P, inheritable_fds: &[RawFd], args: &[S], ) -> Result<pid_t>861     pub fn run<P: AsRef<Path>, S: AsRef<str>>(
862         &self,
863         cmd: P,
864         inheritable_fds: &[RawFd],
865         args: &[S],
866     ) -> Result<pid_t> {
867         self.run_internal(
868             Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
869                 .keep_fds(inheritable_fds)
870                 .args(args)?,
871         )
872     }
873 
874     /// Behaves the same as `run()` except `inheritable_fds` is a list of fd
875     /// mappings rather than just a list of fds to preserve.
run_remap<P: AsRef<Path>, S: AsRef<str>>( &self, cmd: P, inheritable_fds: &[(RawFd, RawFd)], args: &[S], ) -> Result<pid_t>876     pub fn run_remap<P: AsRef<Path>, S: AsRef<str>>(
877         &self,
878         cmd: P,
879         inheritable_fds: &[(RawFd, RawFd)],
880         args: &[S],
881     ) -> Result<pid_t> {
882         self.run_internal(
883             Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
884                 .remap_fds(inheritable_fds)
885                 .args(args)?,
886         )
887     }
888 
889     /// Behaves the same as `run()` except cmd is a file descriptor to the executable.
run_fd<F: AsRawFd, S: AsRef<str>>( &self, cmd: &F, inheritable_fds: &[RawFd], args: &[S], ) -> Result<pid_t>890     pub fn run_fd<F: AsRawFd, S: AsRef<str>>(
891         &self,
892         cmd: &F,
893         inheritable_fds: &[RawFd],
894         args: &[S],
895     ) -> Result<pid_t> {
896         self.run_internal(
897             Command::new(Program::FileDescriptor(cmd.as_raw_fd()))
898                 .keep_fds(inheritable_fds)
899                 .args(args)?,
900         )
901     }
902 
903     /// Behaves the same as `run()` except cmd is a file descriptor to the executable, and
904     /// `inheritable_fds` is a list of fd mappings rather than just a list of fds to preserve.
run_fd_remap<F: AsRawFd, S: AsRef<str>>( &self, cmd: &F, inheritable_fds: &[(RawFd, RawFd)], args: &[S], ) -> Result<pid_t>905     pub fn run_fd_remap<F: AsRawFd, S: AsRef<str>>(
906         &self,
907         cmd: &F,
908         inheritable_fds: &[(RawFd, RawFd)],
909         args: &[S],
910     ) -> Result<pid_t> {
911         self.run_internal(
912             Command::new(Program::FileDescriptor(cmd.as_raw_fd()))
913                 .remap_fds(inheritable_fds)
914                 .args(args)?,
915         )
916     }
917 
918     /// A generic version of `run()` that is a super set of all variants.
run_command(&self, cmd: Command) -> Result<pid_t>919     pub fn run_command(&self, cmd: Command) -> Result<pid_t> {
920         self.run_internal(cmd)
921     }
922 
run_internal(&self, cmd: Command) -> Result<pid_t>923     fn run_internal(&self, cmd: Command) -> Result<pid_t> {
924         for (src_fd, dst_fd) in cmd.preserve_fds.iter() {
925             let ret = unsafe { minijail_preserve_fd(self.jail, *src_fd, *dst_fd) };
926             if ret < 0 {
927                 return Err(Error::PreservingFd(ret));
928             }
929         }
930 
931         let dev_null = fs::OpenOptions::new()
932             .read(true)
933             .write(true)
934             .open("/dev/null")
935             .map_err(Error::OpenDevNull)?;
936         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
937         // These will only be closed when this process exits.
938         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
939             if !cmd.preserve_fds.iter().any(|(_, fd)| *fd == *io_fd) {
940                 let ret = unsafe { minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd) };
941                 if ret < 0 {
942                     return Err(Error::PreservingFd(ret));
943                 }
944             }
945         }
946 
947         unsafe {
948             minijail_close_open_fds(self.jail);
949         }
950 
951         match cmd.program {
952             Program::Filename(ref path) => path.as_path().run_command(self, &cmd),
953             Program::FileDescriptor(fd) => fd.run_command(self, &cmd),
954         }
955     }
956 
957     /// Forks a child and puts it in the previously configured minijail.
958     ///
959     /// # Safety
960     /// `fork` is unsafe because it closes all open FD for this process.  That
961     /// could cause a lot of trouble if not handled carefully.  FDs 0, 1, and 2
962     /// are overwritten with /dev/null FDs unless they are included in the
963     /// inheritable_fds list.
964     ///
965     /// Also, any Rust objects that own fds may try to close them after the fork. If they belong
966     /// to a fd number that was mapped to, the mapped fd will be closed instead.
967     ///
968     /// This Function may abort in the child on error because a partially
969     /// entered jail isn't recoverable.
970     ///
971     /// Once this is invoked the object is no longer usable, after this call
972     /// this minijail object is invalid.
fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t>973     pub unsafe fn fork(&self, inheritable_fds: Option<&[RawFd]>) -> Result<pid_t> {
974         let m: Vec<(RawFd, RawFd)> = inheritable_fds
975             .unwrap_or(&[])
976             .iter()
977             .map(|&a| (a, a))
978             .collect();
979         self.fork_remap(&m)
980     }
981 
982     /// Behaves the same as `fork()` except `inheritable_fds` is a list of fd
983     /// mappings rather than just a list of fds to preserve.
984     ///
985     /// # Safety
986     /// See `fork`.
fork_remap(&self, inheritable_fds: &[(RawFd, RawFd)]) -> Result<pid_t>987     pub unsafe fn fork_remap(&self, inheritable_fds: &[(RawFd, RawFd)]) -> Result<pid_t> {
988         if !self.disable_multithreaded_check
989             && !is_single_threaded().map_err(Error::CheckingMultiThreaded)?
990         {
991             // This test will fail during `cargo test` because the test harness always spawns a test
992             // thread. We will make an exception for that case because the tests for this module
993             // should always be run in a serial fashion using `--test-threads=1`.
994             #[cfg(not(test))]
995             return Err(Error::ForkingWhileMultiThreaded);
996         }
997 
998         for (src_fd, dst_fd) in inheritable_fds {
999             let ret = minijail_preserve_fd(self.jail, *src_fd, *dst_fd);
1000             if ret < 0 {
1001                 return Err(Error::PreservingFd(ret));
1002             }
1003         }
1004 
1005         let dev_null = fs::OpenOptions::new()
1006             .read(true)
1007             .write(true)
1008             .open("/dev/null")
1009             .map_err(Error::OpenDevNull)?;
1010         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
1011         // These will only be closed when this process exits.
1012         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
1013             if !inheritable_fds.iter().any(|(_, fd)| *fd == *io_fd) {
1014                 let ret = minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd);
1015                 if ret < 0 {
1016                     return Err(Error::PreservingFd(ret));
1017                 }
1018             }
1019         }
1020 
1021         minijail_close_open_fds(self.jail);
1022 
1023         let ret = minijail_fork(self.jail);
1024         if ret < 0 {
1025             return Err(Error::ForkingMinijail(ret));
1026         }
1027         if ret == 0 {
1028             // Safe because dev_null was remapped.
1029             dev_null.into_raw_fd();
1030         }
1031         Ok(ret as pid_t)
1032     }
1033 
wait(&self) -> Result<()>1034     pub fn wait(&self) -> Result<()> {
1035         let ret: libc::c_int;
1036         // This is safe because it does not modify the struct.
1037         unsafe {
1038             ret = minijail_wait(self.jail);
1039         }
1040         translate_wait_error(ret)
1041     }
1042 
1043     /// Send a SIGTERM to the child process and wait for its return code.
kill(&self) -> Result<()>1044     pub fn kill(&self) -> Result<()> {
1045         let ret = unsafe {
1046             // The kill does not change any internal state.
1047             minijail_kill(self.jail)
1048         };
1049         // minijail_kill waits for the process, so also translate the returned wait error.
1050         translate_wait_error(ret)
1051     }
1052 }
1053 
1054 impl Drop for Minijail {
1055     /// Frees the Minijail created in Minijail::new. This will not terminate the
1056     /// minijailed process.
drop(&mut self)1057     fn drop(&mut self) {
1058         unsafe {
1059             // Destroys the minijail's memory.  It is safe to do here because all references to
1060             // this object have been dropped.
1061             minijail_destroy(self.jail);
1062         }
1063     }
1064 }
1065 
1066 // Check if a `/proc/*/task/*` is a kthread.
task_is_kthread(path: &Path) -> io::Result<bool>1067 fn task_is_kthread(path: &Path) -> io::Result<bool> {
1068     let status = fs::read_to_string(path.join("status"))?;
1069     Ok(status.contains("\nKthread:\t1\n"))
1070 }
1071 
1072 // Count the number of threads in the current process.
num_threads() -> io::Result<usize>1073 fn num_threads() -> io::Result<usize> {
1074     let mut count = 0;
1075     for entry in fs::read_dir("/proc/self/task")? {
1076         let entry = entry?;
1077         if !task_is_kthread(&entry.path())? {
1078             count += 1;
1079         }
1080     }
1081     Ok(count)
1082 }
1083 
1084 // Return true if the current thread is the only thread in the process.
is_single_threaded() -> io::Result<bool>1085 fn is_single_threaded() -> io::Result<bool> {
1086     Ok(num_threads()? == 1)
1087 }
1088 
to_execve_cstring_array<S: AsRef<str>>( slice: &[S], ) -> Result<(Vec<CString>, Vec<*const c_char>)>1089 fn to_execve_cstring_array<S: AsRef<str>>(
1090     slice: &[S],
1091 ) -> Result<(Vec<CString>, Vec<*const c_char>)> {
1092     // Converts each incoming `str` to a `CString`, and then puts each `CString` pointer into a
1093     // null terminated array, suitable for use as an argv or envp parameter to `execve`.
1094     let mut vec_cstr = Vec::with_capacity(slice.len());
1095     let mut vec_cptr = Vec::with_capacity(slice.len() + 1);
1096     for s in slice {
1097         let cstr =
1098             CString::new(s.as_ref()).map_err(|_| Error::StrToCString(s.as_ref().to_owned()))?;
1099 
1100         vec_cstr.push(cstr);
1101         vec_cptr.push(vec_cstr.last().unwrap().as_ptr());
1102     }
1103 
1104     vec_cptr.push(null());
1105 
1106     Ok((vec_cstr, vec_cptr))
1107 }
1108 
1109 static LOGGING_INIT_LOCK: Once = Once::new();
1110 
init_default_logging()1111 pub fn init_default_logging() {
1112     LOGGING_INIT_LOCK.call_once(|| {
1113         log_to_fd(libc::STDERR_FILENO, 6 /* SYSLOG_LOG_INFO */)
1114     })
1115 }
1116 
log_to_fd(fd: RawFd, priority: c_int)1117 fn log_to_fd(fd: RawFd, priority: c_int) {
1118     unsafe {
1119         minijail_log_to_fd(fd, priority);
1120     }
1121 }
1122 
1123 #[cfg(test)]
1124 mod tests {
1125     use super::*;
1126 
1127     use std::fs::File;
1128 
1129     use libc::{FD_CLOEXEC, F_GETFD, F_SETFD};
1130 
1131     const SHELL: &str = "/bin/sh";
1132     const EMPTY_STRING_SLICE: &[&str] = &[];
1133 
clear_cloexec<A: AsRawFd>(fd_owner: &A) -> StdResult<(), io::Error>1134     fn clear_cloexec<A: AsRawFd>(fd_owner: &A) -> StdResult<(), io::Error> {
1135         let fd = fd_owner.as_raw_fd();
1136         // Safe because fd is read only.
1137         let flags = unsafe { libc::fcntl(fd, F_GETFD) };
1138         if flags == -1 {
1139             return Err(io::Error::last_os_error());
1140         }
1141 
1142         let masked_flags = flags & !FD_CLOEXEC;
1143         // Safe because this has no side effect(s) on the current process.
1144         if masked_flags != flags && unsafe { libc::fcntl(fd, F_SETFD, masked_flags) } == -1 {
1145             Err(io::Error::last_os_error())
1146         } else {
1147             Ok(())
1148         }
1149     }
1150 
1151     #[test]
create_and_free()1152     fn create_and_free() {
1153         init_default_logging();
1154 
1155         unsafe {
1156             let j = minijail_new();
1157             assert_ne!(std::ptr::null_mut(), j);
1158             minijail_destroy(j);
1159         }
1160 
1161         let j = Minijail::new().unwrap();
1162         drop(j);
1163     }
1164 
1165     #[test]
1166     // Test that setting a seccomp filter with no-new-privs works as non-root.
1167     // This is equivalent to minijail0 -n -S <seccomp_policy>
seccomp_no_new_privs()1168     fn seccomp_no_new_privs() {
1169         init_default_logging();
1170 
1171         let mut j = Minijail::new().unwrap();
1172         j.no_new_privs();
1173         j.parse_seccomp_filters("src/test_filter.policy").unwrap();
1174         j.use_seccomp_filter();
1175         j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap();
1176     }
1177 
1178     #[test]
1179     // Test that open FDs get closed and that FDs in the inherit list are left open.
close_fds()1180     fn close_fds() {
1181         init_default_logging();
1182 
1183         unsafe {
1184             // Using libc to open/close FDs for testing.
1185             const FILE_PATH: &[u8] = b"/dev/null\0";
1186             let j = Minijail::new().unwrap();
1187             let first = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY);
1188             assert!(first >= 0);
1189             // This appears unused but its function is to be a file descriptor that is closed
1190             // inside run_remap after the fork. If it is not closed, the script will fail.
1191             let second = libc::open(FILE_PATH.as_ptr() as *const c_char, libc::O_RDONLY);
1192             assert!(second >= 0);
1193 
1194             let fds: Vec<(RawFd, RawFd)> = vec![(first, 0), (1, 1), (2, 2)];
1195             j.run_remap(
1196                 SHELL,
1197                 &fds,
1198                 &[
1199                     SHELL,
1200                     "-c",
1201                     r#"
1202 if [ `ls -l /proc/self/fd/ | grep '/dev/null' | wc -l` != '1' ]; then
1203   ls -l /proc/self/fd/  # Included to make debugging easier.
1204   exit 1
1205 fi
1206 "#,
1207                 ],
1208             )
1209             .unwrap();
1210             j.wait().unwrap();
1211         }
1212     }
1213 
1214     macro_rules! expect_result {
1215         ($call:expr, $expected:pat) => {
1216             let got = $call;
1217             match got {
1218                 $expected => {}
1219                 _ => {
1220                     panic!("got {:?} expected {:?}", got, stringify!($expected));
1221                 }
1222             }
1223         };
1224     }
1225 
1226     #[test]
wait_success()1227     fn wait_success() {
1228         init_default_logging();
1229 
1230         let j = Minijail::new().unwrap();
1231         j.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap();
1232         expect_result!(j.wait(), Ok(()));
1233     }
1234 
1235     #[test]
wait_killed()1236     fn wait_killed() {
1237         init_default_logging();
1238 
1239         let j = Minijail::new().unwrap();
1240         j.run(
1241             SHELL,
1242             &[1, 2],
1243             &[SHELL, "-c", "kill -9 $$ &\n/usr/bin/sleep 5"],
1244         )
1245         .unwrap();
1246         expect_result!(j.wait(), Err(Error::Killed(9)));
1247     }
1248 
1249     #[test]
wait_returncode()1250     fn wait_returncode() {
1251         init_default_logging();
1252 
1253         let j = Minijail::new().unwrap();
1254         j.run("/bin/false", &[1, 2], EMPTY_STRING_SLICE).unwrap();
1255         expect_result!(j.wait(), Err(Error::ReturnCode(1)));
1256     }
1257 
1258     #[test]
wait_noaccess()1259     fn wait_noaccess() {
1260         init_default_logging();
1261 
1262         let j = Minijail::new().unwrap();
1263         j.run("/dev/null", &[1, 2], EMPTY_STRING_SLICE).unwrap();
1264         expect_result!(j.wait(), Err(Error::NoAccess));
1265     }
1266 
1267     #[test]
wait_nocommand()1268     fn wait_nocommand() {
1269         init_default_logging();
1270 
1271         let j = Minijail::new().unwrap();
1272         j.run("/bin/does not exist", &[1, 2], EMPTY_STRING_SLICE)
1273             .unwrap();
1274         // TODO(b/194221986) Fix libminijail so that Error::NoAccess is not sometimes returned.
1275         assert!(matches!(
1276             j.wait(),
1277             Err(Error::NoCommand) | Err(Error::NoAccess)
1278         ));
1279     }
1280 
1281     #[test]
1282     #[ignore] // TODO(b/323475944) Fix unit test failures.
runnable_fd_success()1283     fn runnable_fd_success() {
1284         init_default_logging();
1285 
1286         let bin_file = File::open("/bin/true").unwrap();
1287         // On ChromeOS targets /bin/true is actually a script, so drop CLOEXEC to prevent ENOENT.
1288         clear_cloexec(&bin_file).unwrap();
1289 
1290         let j = Minijail::new().unwrap();
1291         j.run_fd(&bin_file, &[1, 2], EMPTY_STRING_SLICE).unwrap();
1292         expect_result!(j.wait(), Ok(()));
1293     }
1294 
1295     #[test]
kill_success()1296     fn kill_success() {
1297         init_default_logging();
1298 
1299         let j = Minijail::new().unwrap();
1300         j.run(
1301             Path::new("/usr/bin/sleep"),
1302             &[1, 2],
1303             &["/usr/bin/sleep", "5"],
1304         )
1305         .unwrap();
1306         const EXPECTED_SIGNAL: u8 = libc::SIGTERM as u8;
1307         expect_result!(j.kill(), Err(Error::Killed(EXPECTED_SIGNAL)));
1308     }
1309 
1310     #[test]
1311     #[ignore] // privileged operation.
chroot()1312     fn chroot() {
1313         init_default_logging();
1314 
1315         let mut j = Minijail::new().unwrap();
1316         j.enter_chroot(".").unwrap();
1317         j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap();
1318     }
1319 
1320     #[test]
1321     #[ignore] // privileged operation.
namespace_vfs()1322     fn namespace_vfs() {
1323         init_default_logging();
1324 
1325         let mut j = Minijail::new().unwrap();
1326         j.namespace_vfs();
1327         j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap();
1328     }
1329 
1330     #[test]
run()1331     fn run() {
1332         init_default_logging();
1333 
1334         let j = Minijail::new().unwrap();
1335         j.run("/bin/true", &[], EMPTY_STRING_SLICE).unwrap();
1336     }
1337 
1338     #[test]
run_clone()1339     fn run_clone() {
1340         init_default_logging();
1341 
1342         let j = Minijail::new().unwrap();
1343         let b = j.try_clone().unwrap();
1344         // Pass the same FDs to both clones and make sure they don't conflict.
1345         j.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap();
1346         b.run("/bin/true", &[1, 2], EMPTY_STRING_SLICE).unwrap();
1347     }
1348 
1349     #[test]
run_string_vec()1350     fn run_string_vec() {
1351         init_default_logging();
1352 
1353         let j = Minijail::new().unwrap();
1354         let args = vec!["ignored".to_string()];
1355         j.run(Path::new("/bin/true"), &[], &args).unwrap();
1356     }
1357 }
1358