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