xref: /aosp_15_r20/external/crosvm/jail/src/fork.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 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 //! Provides [fork_process] to fork a process.
6 
7 #![deny(missing_docs)]
8 
9 use std::ffi::CString;
10 use std::mem::ManuallyDrop;
11 use std::os::unix::process::ExitStatusExt;
12 use std::process;
13 
14 use base::error;
15 use base::linux::wait_for_pid;
16 use base::Pid;
17 use base::RawDescriptor;
18 #[cfg(feature = "seccomp_trace")]
19 use log::debug;
20 use log::warn;
21 use minijail::Minijail;
22 
23 /// Child represents the forked process.
24 pub struct Child {
25     /// The pid of the child process.
26     pub pid: Pid,
27 }
28 
29 impl Child {
30     /// Wait for the child process exit using `waitpid(2)`.
wait(self) -> base::Result<u8>31     pub fn wait(self) -> base::Result<u8> {
32         // Suppress warning from the drop().
33         let pid = self.into_pid();
34         let (_, status) = wait_for_pid(pid, 0)?;
35         if let Some(exit_code) = status.code() {
36             Ok(exit_code as u8)
37         } else if let Some(signal) = status.signal() {
38             let exit_code = if signal >= 128 {
39                 warn!("wait for child: unexpected signal({:?})", signal);
40                 255
41             } else {
42                 128 + signal as u8
43             };
44             Ok(exit_code)
45         } else {
46             unreachable!("waitpid with option 0 only waits for exited and signaled status");
47         }
48     }
49 
50     /// Convert [Child] into [Pid].
51     ///
52     /// If [Child] is dropped without `Child::wait()`, it logs warning message. Users who wait
53     /// processes in other ways should suppress the warning by unwrapping [Child] into [Pid].
54     ///
55     /// The caller of this method now owns the process and is responsible for managing the
56     /// termination of the process.
into_pid(self) -> Pid57     pub fn into_pid(self) -> Pid {
58         let pid = self.pid;
59         // Suppress warning from the drop().
60         let _ = ManuallyDrop::new(self);
61         pid
62     }
63 }
64 
65 impl Drop for Child {
drop(&mut self)66     fn drop(&mut self) {
67         warn!("the child process have not been waited.: {}", self.pid);
68     }
69 }
70 
71 /// Forks this process using [Minijail] and calls a closure in the new process.
72 ///
73 /// After `post_fork_cb` returns, the new process exits with `0` code. If `post_fork_cb` panics, the
74 /// new process exits with `101` code.
75 ///
76 /// This function never returns in the forked process.
77 ///
78 /// # Arguments
79 ///
80 /// * `jail` - [Minijail] instance to fork.
81 /// * `keep_rds` - [RawDescriptor]s to be kept in the forked process. other file descriptors will be
82 ///   closed by [Minijail] in the forked process.
83 /// * `debug_label` - (optional) thread name. this will be trimmed to 15 charactors.
84 /// * `post_fork_cb` - Callback to run in the new process.
fork_process<F>( jail: Minijail, mut keep_rds: Vec<RawDescriptor>, debug_label: Option<String>, post_fork_cb: F, ) -> minijail::Result<Child> where F: FnOnce(),85 pub fn fork_process<F>(
86     jail: Minijail,
87     mut keep_rds: Vec<RawDescriptor>,
88     debug_label: Option<String>,
89     post_fork_cb: F,
90 ) -> minijail::Result<Child>
91 where
92     F: FnOnce(),
93 {
94     // Deduplicate the FDs since minijail expects this.
95     keep_rds.sort_unstable();
96     keep_rds.dedup();
97 
98     // SAFETY:
99     // Safe because the program is still single threaded.
100     // We own the jail object and nobody else will try to reuse it.
101     let pid = match unsafe { jail.fork(Some(&keep_rds)) }? {
102         0 => {
103             struct ExitGuard;
104             impl Drop for ExitGuard {
105                 fn drop(&mut self) {
106                     // Rust exits with 101 when panics.
107                     process::exit(101);
108                 }
109             }
110             // Prevents a panic in post_fork_cb from bypassing the process::exit.
111             let _exit_guard = ExitGuard {};
112 
113             if let Some(debug_label) = debug_label {
114                 // pthread_setname_np() limit on Linux
115                 const MAX_THREAD_LABEL_LEN: usize = 15;
116                 let debug_label_trimmed = &debug_label.as_bytes()
117                     [..std::cmp::min(MAX_THREAD_LABEL_LEN, debug_label.len())];
118                 match CString::new(debug_label_trimmed) {
119                     Ok(thread_name) => {
120                         // SAFETY:
121                         // Safe because thread_name is a valid pointer and setting name of this
122                         // thread should be safe.
123                         let _ = unsafe {
124                             libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr())
125                         };
126                     }
127                     Err(e) => {
128                         error!("failed to compile thread name: {:?}", e);
129                     }
130                 }
131             }
132 
133             post_fork_cb();
134             // ! Never returns
135             process::exit(0);
136         }
137         pid => pid,
138     };
139     #[cfg(feature = "seccomp_trace")]
140     debug!(
141             // Proxy and swap devices fork here
142             "seccomp_trace {{\"event\": \"minijail_fork\", \"pid\": \"{}\", \"name\": \"{}\", \"jail_addr\": \"0x{:x}\"}}",
143             pid,
144             match debug_label {
145                 Some(debug_label) => debug_label,
146                 None => "process.rs: no debug label".to_owned(),
147             },
148             // Can't use safe wrapper because jail crate depends on base
149             // SAFETY:
150             // Safe because it's only doing a read within bound checked by static assert
151             unsafe {*(&jail as *const Minijail as *const usize)}
152         );
153     Ok(Child { pid })
154 }
155