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