cloudfox-coreshift-core 2.18.1

Low-level Linux and Android systems primitives for CoreShift (CloudFox)
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/

//! `clone3(2)`/`exec` backend.
//!
//! Owns the `clone3` launch path for [`SpawnBackend::Clone3`] and
//! [`SpawnBackend::Clone3Pidfd`](crate::spawn::SpawnBackend::Clone3Pidfd).
//! `clone3` creates a child with copy-on-write memory and a copied descriptor
//! table, then runs the same child setup as the fork backend. With
//! `CLONE_PIDFD` the kernel additionally returns a pidfd that the resulting
//! [`Process`](crate::spawn::Process) uses for race-free signaling and reaping.
//!
//! `clone3` is a Linux 5.3+ syscall; on older kernels it returns `ENOSYS`,
//! which propagates as a spawn error (these backends never silently fall back).

use std::os::unix::io::RawFd;

use crate::CoreError;
use crate::error::syscall_ret;

use super::fork::{child_entry, collect_required_pipe_fds, prepare_child_context, reap_and_drain};
use super::{CLONE_PIDFD, Process, SYS_CLONE3, SpawnDrain, SpawnOptions, make_cloexec_pipe};

/// `struct clone_args` layout for `clone3(2)` (linux/clone.h). Every field is
/// an `__aligned_u64`; the syscall takes a pointer to it and its size in
/// bytes.
#[repr(C)]
struct CloneArgs {
    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,
}

impl CloneArgs {
    fn new(pidfd: bool) -> Self {
        Self {
            flags: if pidfd { CLONE_PIDFD } else { 0 },
            // CLONE_PIDFD out-param: the kernel writes the child's pidfd here.
            pidfd: 0,
            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,
        }
    }
}

pub(super) fn spawn_clone3_internal(
    opts: SpawnOptions,
    pidfd: bool,
) -> Result<(Process, SpawnDrain), CoreError> {
    let ctx = prepare_child_context(&opts);
    let mut pipes = super::Pipes::new(
        opts.stdin.as_deref(),
        opts.capture_stdout,
        opts.capture_stderr,
    )?;

    let (child_error_r, child_error_w) = make_cloexec_pipe()?;
    let mut required_fds = collect_required_pipe_fds(&pipes);
    required_fds.push(child_error_w);

    let mut args = CloneArgs::new(pidfd);

    // `args.pidfd` is a *pointer* to a user-space slot where the kernel writes
    // the child's pidfd when CLONE_PIDFD is set; NULL with CLONE_PIDFD is
    // EFAULT. Sentinelled to `u64::MAX`, never `0` — fd 0 is a valid pidfd
    // when stdio has been closed, so `0` is not a safe "not delivered" marker.
    let mut pidfd_out: u64 = u64::MAX;
    args.pidfd = if pidfd {
        (&mut pidfd_out as *mut u64) as u64
    } else {
        0
    };

    let pid = unsafe {
        libc::syscall(
            SYS_CLONE3,
            &args as *const CloneArgs,
            std::mem::size_of::<CloneArgs>(),
        )
    };

    if pid < 0 {
        unsafe {
            libc::close(child_error_r);
            libc::close(child_error_w);
        }
        pipes.close_all();
        syscall_ret(-1, "clone3")?;
    }

    if pid == 0 {
        // Child: COW copy of the address space with a copied descriptor table,
        // exactly the fork child contract. Never returns.
        // SAFETY: child-only setup after clone3.
        unsafe {
            child_entry(
                &pipes,
                &opts,
                &ctx,
                &required_fds,
                child_error_r,
                child_error_w,
            );
        }
    }

    // Parent. With CLONE_PIDFD the kernel wrote the child's pidfd into the
    // slot `args.pidfd` pointed at; carry it on the Process handle for
    // pidfd-based reap/signal.
    let process = if pidfd && pid >= 0 && pidfd_out != u64::MAX {
        Process::with_pidfd(pid as libc::pid_t, pidfd_out as RawFd)
    } else if pidfd {
        // CLONE_PIDFD was requested but no pidfd was delivered. `Clone3Pidfd`
        // is selected precisely for the pid-reuse-immune reap/signal
        // guarantee, so degrading to a plain `Process` would silently weaken
        // the caller's contract (review finding 17). Fail closed instead; the
        // live child is handed to the reaper so it is not leaked.
        let cpid = pid as libc::pid_t;
        unsafe {
            libc::close(child_error_r);
            libc::close(child_error_w);
        }
        pipes.close_all();
        super::orphan_child(cpid);
        return Err(CoreError::sys(
            libc::EIO,
            "clone3: CLONE_PIDFD requested but no pidfd delivered",
        ));
    } else {
        Process::new(pid as libc::pid_t)
    };

    let drain = reap_and_drain(
        pid as libc::pid_t,
        pipes,
        child_error_r,
        child_error_w,
        opts.stdin,
        opts.max_output,
        opts.early_exit,
    )?;

    Ok((process, drain))
}