Skip to content

Commit

Permalink
Split do_fork into two
Browse files Browse the repository at this point in the history
  • Loading branch information
voidc committed Mar 16, 2021
1 parent 5ac8a31 commit a266bd8
Showing 1 changed file with 73 additions and 69 deletions.
142 changes: 73 additions & 69 deletions library/std/src/sys/unix/process/process_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,84 +118,88 @@ impl Command {
}
}

// Attempts to fork the process. If successful, returns
// Ok((0, -1)) in the child, and Ok((child_pid, child_pidfd)) in the parent.
// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, -1)) in the parent.
#[cfg(not(target_os = "linux"))]
fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
cvt(unsafe { libc::fork() }).map(|res| (res, -1))
}

// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, child_pidfd)) in the parent.
#[cfg(target_os = "linux")]
fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
use crate::sync::atomic::{AtomicBool, Ordering};

static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
const CLONE_PIDFD: u64 = 0x00001000;

#[repr(C)]
struct clone_args {
flags: u64,
pidfd: u64,
child_tid: u64,
parent_tid: u64,
exit_signal: u64,
stack: u64,
stack_size: u64,
tls: u64,
set_tid: u64,
set_tid_size: u64,
cgroup: u64,
}

syscall! {
fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
}

// If we fail to create a pidfd for any reason, this will
// stay as -1, which indicates an error
// stay as -1, which indicates an error.
let mut pidfd: pid_t = -1;

// On Linux, attempt to use the `clone3` syscall, which
// supports more arguments (in particular, the ability to create a pidfd).
// If this fails, we will fall through this block to a call to `fork()`
#[cfg(target_os = "linux")]
{
use crate::sync::atomic::{AtomicBool, Ordering};
static HAS_CLONE3: AtomicBool = AtomicBool::new(true);

const CLONE_PIDFD: u64 = 0x00001000;

#[repr(C)]
struct clone_args {
flags: u64,
pidfd: u64,
child_tid: u64,
parent_tid: u64,
exit_signal: u64,
stack: u64,
stack_size: u64,
tls: u64,
set_tid: u64,
set_tid_size: u64,
cgroup: u64,
}

syscall! {
fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
// Attempt to use the `clone3` syscall, which supports more arguments
// (in particular, the ability to create a pidfd). If this fails,
// we will fall through this block to a call to `fork()`
if HAS_CLONE3.load(Ordering::Relaxed) {
let mut flags = 0;
if self.create_pidfd {
flags |= CLONE_PIDFD;
}

if HAS_CLONE3.load(Ordering::Relaxed) {
let mut flags = 0;
if self.create_pidfd {
flags |= CLONE_PIDFD;
}

let mut args = clone_args {
flags,
pidfd: &mut pidfd as *mut pid_t as u64,
child_tid: 0,
parent_tid: 0,
exit_signal: libc::SIGCHLD as u64,
stack: 0,
stack_size: 0,
tls: 0,
set_tid: 0,
set_tid_size: 0,
cgroup: 0,
};

let args_ptr = &mut args as *mut clone_args;
let args_size = crate::mem::size_of::<clone_args>();

let res = cvt(unsafe { clone3(args_ptr, args_size) });
match res {
Ok(n) => return Ok((n as pid_t, pidfd)),
Err(e) => match e.raw_os_error() {
// Multiple threads can race to execute this store,
// but that's fine - that just means that multiple threads
// will have tried and failed to execute the same syscall,
// with no other side effects.
Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
// Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
Some(libc::EPERM) => {}
_ => return Err(e),
},
}
let mut args = clone_args {
flags,
pidfd: &mut pidfd as *mut pid_t as u64,
child_tid: 0,
parent_tid: 0,
exit_signal: libc::SIGCHLD as u64,
stack: 0,
stack_size: 0,
tls: 0,
set_tid: 0,
set_tid_size: 0,
cgroup: 0,
};

let args_ptr = &mut args as *mut clone_args;
let args_size = crate::mem::size_of::<clone_args>();

let res = cvt(unsafe { clone3(args_ptr, args_size) });
match res {
Ok(n) => return Ok((n as pid_t, pidfd)),
Err(e) => match e.raw_os_error() {
// Multiple threads can race to execute this store,
// but that's fine - that just means that multiple threads
// will have tried and failed to execute the same syscall,
// with no other side effects.
Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
// Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
Some(libc::EPERM) => {}
_ => return Err(e),
},
}
}

// If we get here, we are either not on Linux,
// or we are on Linux and the 'clone3' syscall does not exist
// If we get here, the 'clone3' syscall does not exist
// or we do not have permission to call it
cvt(unsafe { libc::fork() }).map(|res| (res, pidfd))
}
Expand Down

0 comments on commit a266bd8

Please sign in to comment.