xref: /aosp_15_r20/external/minijail/rust/minijail/tests/fork_remap.rs (revision 4b9c6d91573e8b3a96609339b46361b5476dd0f9)
1 // Copyright 2021 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 //! A test of Minijail::fork_remap.
6 //!
7 //! It needs to be run on its own because it forks the process and by default cargo test is
8 //! multi-threaded, and we do not want copies of the other worker threads leaking into the child
9 //! process.
10 
11 use std::fs::{read_link, File, OpenOptions};
12 use std::io::{self, Read};
13 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
14 use std::path::Path;
15 
16 use minijail::Minijail;
17 
18 const DEV_NULL: &str = "/dev/null";
19 const DEV_ZERO: &str = "/dev/zero";
20 const PROC_CMDLINE: &str = "/proc/self/cmdline";
21 
open_path(path: &str) -> Result<File, io::Error>22 fn open_path(path: &str) -> Result<File, io::Error> {
23     OpenOptions::new()
24         .read(true)
25         .write(false)
26         .open(Path::new(path))
27 }
28 
main()29 fn main() {
30     let mut check_file1 = open_path(DEV_ZERO).unwrap();
31     let mut check_file2 = open_path(PROC_CMDLINE).unwrap();
32     let j = Minijail::new().unwrap();
33 
34     let mut stdio_expected = String::new();
35     let mut file2_expected = String::new();
36     for &p in &[0, 1, 2, check_file1.as_raw_fd(), check_file2.as_raw_fd()] {
37         let path = format!("/proc/self/fd/{}", p);
38         let target = read_link(Path::new(&path));
39         eprintln!("P: {} -> {:?}", p, &target);
40         if p == 2 {
41             stdio_expected = target.unwrap().to_string_lossy().to_string();
42         } else if p == check_file2.as_raw_fd() {
43             file2_expected = target.unwrap().to_string_lossy().to_string();
44         }
45     }
46 
47     // Swap fd1 and fd2.
48     let dest_fd1: RawFd = check_file2.as_raw_fd();
49     let dest_fd2: RawFd = check_file1.as_raw_fd();
50 
51     if unsafe {
52         j.fork_remap(&[
53             // fd 0 tests stdio mapped to /dev/null.
54             (2, 1),                              // One-to-many.
55             (2, 2),                              // Identity.
56             (check_file1.as_raw_fd(), dest_fd1), // Cross-over.
57             (check_file2.as_raw_fd(), dest_fd2), // Cross-over.
58         ])
59     }
60     .unwrap()
61         != 0
62     {
63         j.wait().unwrap();
64         eprintln!("Parent done.");
65         return;
66     }
67 
68     // Safe because we are re-taking ownership of remapped fds after forking.
69     unsafe {
70         check_file1.into_raw_fd();
71         check_file1 = File::from_raw_fd(dest_fd1);
72 
73         check_file2.into_raw_fd();
74         check_file2 = File::from_raw_fd(dest_fd2);
75     }
76 
77     for (p, expected) in &[
78         (0, DEV_NULL),
79         (1, &stdio_expected),
80         (2, &stdio_expected),
81         (dest_fd1, DEV_ZERO),
82         (dest_fd2, &file2_expected),
83     ] {
84         let path = format!("/proc/self/fd/{}", p);
85         let target = read_link(Path::new(&path));
86         eprintln!("  C: {} -> {:?}", p, &target);
87         if !matches!(&target, Ok(p) if p == Path::new(expected)) {
88             panic!("  C: got {:?}; expected Ok({:?})", target, expected);
89         }
90     }
91 
92     const BUFFER_LEN: usize = 16;
93     let mut buffer = [0xffu8; BUFFER_LEN];
94     check_file1.read_exact(&mut buffer).unwrap();
95     assert_eq!(&buffer, &[0u8; BUFFER_LEN]);
96 
97     let mut file2_contents = Vec::<u8>::new();
98     check_file2.read_to_end(&mut file2_contents).unwrap();
99 
100     eprintln!("  Child done.");
101 }
102