Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/jail/src/fork.rs
5394 views
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)`.
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.
57
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 {
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.
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
156