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