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