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