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