#[derive(Debug, Clone)]
pub struct SandboxConfig {
pub cpu_secs: u64,
pub mem_mb: u64,
pub fsize_mb: u64,
pub nofile: u64,
pub nproc: u64,
}
impl Default for SandboxConfig {
fn default() -> Self {
Self {
cpu_secs: 30,
mem_mb: 512,
fsize_mb: 10,
nofile: 64,
nproc: 64,
}
}
}
#[cfg(unix)]
pub fn apply_sandbox(cmd: &mut tokio::process::Command, config: &SandboxConfig) {
let cpu = config.cpu_secs;
#[cfg(not(target_os = "macos"))]
let mem = config.mem_mb * 1024 * 1024;
let fsize = config.fsize_mb * 1024 * 1024;
let nofile = config.nofile;
let nproc = config.nproc;
unsafe {
cmd.pre_exec(move || {
let _ = set_rlimit(libc::RLIMIT_CPU, cpu);
let _ = set_rlimit(libc::RLIMIT_FSIZE, fsize);
let _ = set_rlimit(libc::RLIMIT_NOFILE, nofile);
let _ = set_rlimit(libc::RLIMIT_NPROC, nproc);
#[cfg(not(target_os = "macos"))]
let _ = set_rlimit(libc::RLIMIT_AS, mem);
Ok(())
});
}
}
#[cfg(target_os = "macos")]
type RlimitResource = libc::c_int;
#[cfg(target_os = "linux")]
type RlimitResource = libc::__rlimit_resource_t;
#[cfg(unix)]
fn set_rlimit(resource: RlimitResource, limit: u64) -> std::io::Result<()> {
let rlim = libc::rlimit {
rlim_cur: limit as libc::rlim_t,
rlim_max: limit as libc::rlim_t,
};
let ret = unsafe { libc::setrlimit(resource, &rlim) };
if ret != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = SandboxConfig::default();
assert_eq!(config.cpu_secs, 30);
assert_eq!(config.mem_mb, 512);
assert_eq!(config.fsize_mb, 10);
assert_eq!(config.nofile, 64);
assert_eq!(config.nproc, 64);
}
#[cfg(unix)]
#[tokio::test]
async fn test_sandbox_applies_to_command() {
let config = SandboxConfig::default();
let mut cmd = tokio::process::Command::new("echo");
cmd.arg("hello");
apply_sandbox(&mut cmd, &config);
let output = cmd.output().await.unwrap();
assert!(output.status.success());
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "hello");
}
}