sandbox-rs 0.2.1

A comprehensive Rust sandbox implementation that provides process isolation, resource limiting, and syscall filtering for secure program execution.
Documentation
//! Minimal init process for sandbox

use nix::sys::signal::{SigHandler, Signal, signal};
use nix::unistd::execv;
use std::ffi::CString;
use std::process::exit;

/// Simple init process that manages sandbox
pub struct SandboxInit {
    /// Arguments to pass to user program
    pub program: String,
    pub args: Vec<String>,
}

impl SandboxInit {
    /// Create new init process
    pub fn new(program: String, args: Vec<String>) -> Self {
        Self { program, args }
    }

    /// Run init process
    /// This becomes PID 1 inside the sandbox
    pub fn run(&self) -> ! {
        Self::setup_signals();
        Self::mount_procfs();
        Self::mount_sysfs();
        self.exec_user_program();
    }

    /// Setup signal handlers for init
    fn setup_signals() {
        unsafe {
            let _ = signal(Signal::SIGCHLD, SigHandler::SigIgn);
            let _ = signal(Signal::SIGTERM, SigHandler::SigDfl);
        }
    }

    fn mount_procfs() {
        let _ = std::fs::create_dir("/proc");

        let source = CString::new("proc").unwrap();
        let target = CString::new("/proc").unwrap();
        let fstype = CString::new("proc").unwrap();

        unsafe {
            libc::mount(
                source.as_ptr(),
                target.as_ptr(),
                fstype.as_ptr(),
                0,
                std::ptr::null(),
            );
        }
    }

    fn mount_sysfs() {
        let _ = std::fs::create_dir("/sys");

        let source = CString::new("sysfs").unwrap();
        let target = CString::new("/sys").unwrap();
        let fstype = CString::new("sysfs").unwrap();

        unsafe {
            libc::mount(
                source.as_ptr(),
                target.as_ptr(),
                fstype.as_ptr(),
                0,
                std::ptr::null(),
            );
        }
    }

    /// Execute user program
    fn exec_user_program(&self) -> ! {
        let program_cstr = match CString::new(self.program.clone()) {
            Ok(s) => s,
            Err(_) => {
                eprintln!("Invalid program name");
                exit(1);
            }
        };

        let args_cstr: Vec<CString> = self
            .args
            .iter()
            .map(|arg| CString::new(arg.clone()).unwrap_or_else(|_| CString::new("").unwrap()))
            .collect();

        let args_refs: Vec<&CString> = vec![&program_cstr]
            .into_iter()
            .chain(args_cstr.iter())
            .collect();

        match execv(&program_cstr, &args_refs) {
            Ok(_) => {
                exit(0);
            }
            Err(e) => {
                eprintln!("Failed to execute program: {}", e);
                exit(1);
            }
        }
    }

    /// Reap zombie children
    pub fn reap_children() {
        use nix::sys::wait::{WaitStatus, waitpid};
        use nix::unistd::Pid;

        loop {
            match waitpid(
                Pid::from_raw(-1),
                Some(nix::sys::wait::WaitPidFlag::WNOHANG),
            ) {
                Ok(WaitStatus::Exited(pid, _status)) => {
                    eprintln!("[init] Child {} exited", pid);
                }
                Ok(WaitStatus::Signaled(pid, signal, _core)) => {
                    eprintln!("[init] Child {} killed by {:?}", pid, signal);
                }
                Ok(WaitStatus::StillAlive) => break,
                Ok(_) => continue,
                Err(_) => break,
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_init_creation() {
        let init = SandboxInit::new("/bin/echo".to_string(), vec!["hello".to_string()]);
        assert_eq!(init.program, "/bin/echo");
        assert_eq!(init.args.len(), 1);
    }

    #[test]
    fn test_init_empty_args() {
        let init = SandboxInit::new("/bin/sh".to_string(), Vec::new());
        assert!(init.args.is_empty());
    }

    #[test]
    fn test_mount_helpers_are_best_effort() {
        SandboxInit::mount_procfs();
        SandboxInit::mount_sysfs();
    }
}