Skip to main content

sandbox_cgroup/
rlimit.rs

1//! setrlimit-based resource limits (no privileges needed)
2//!
3//! Provides process resource limits using setrlimit/prlimit64.
4//! This is the fallback when cgroups are not available.
5
6use sandbox_core::{Result, SandboxError};
7
8/// Resource limits via setrlimit (unprivileged fallback)
9#[derive(Debug, Clone, Default)]
10pub struct RlimitConfig {
11    /// Maximum address space size in bytes (RLIMIT_AS)
12    pub max_memory: Option<u64>,
13    /// Maximum CPU time in seconds (RLIMIT_CPU)
14    pub max_cpu_seconds: Option<u64>,
15    /// Maximum number of processes (RLIMIT_NPROC)
16    pub max_processes: Option<u64>,
17    /// Maximum file size in bytes (RLIMIT_FSIZE)
18    pub max_file_size: Option<u64>,
19    /// Maximum number of open files (RLIMIT_NOFILE)
20    pub max_open_files: Option<u64>,
21}
22
23impl RlimitConfig {
24    /// Apply resource limits to the current process.
25    /// This should be called in the child process after fork/clone.
26    pub fn apply(&self) -> Result<()> {
27        if let Some(mem) = self.max_memory {
28            set_rlimit(libc::RLIMIT_AS, mem)?;
29        }
30        if let Some(cpu) = self.max_cpu_seconds {
31            set_rlimit(libc::RLIMIT_CPU, cpu)?;
32        }
33        if let Some(nproc) = self.max_processes {
34            set_rlimit(libc::RLIMIT_NPROC, nproc)?;
35        }
36        if let Some(fsize) = self.max_file_size {
37            set_rlimit(libc::RLIMIT_FSIZE, fsize)?;
38        }
39        if let Some(nofile) = self.max_open_files {
40            set_rlimit(libc::RLIMIT_NOFILE, nofile)?;
41        }
42        Ok(())
43    }
44}
45
46fn set_rlimit(resource: libc::__rlimit_resource_t, limit: u64) -> Result<()> {
47    let rlim = libc::rlimit {
48        rlim_cur: limit,
49        rlim_max: limit,
50    };
51
52    let ret = unsafe { libc::setrlimit(resource, &rlim) };
53    if ret != 0 {
54        let resource_name = match resource {
55            libc::RLIMIT_AS => "RLIMIT_AS",
56            libc::RLIMIT_CPU => "RLIMIT_CPU",
57            libc::RLIMIT_NPROC => "RLIMIT_NPROC",
58            libc::RLIMIT_FSIZE => "RLIMIT_FSIZE",
59            libc::RLIMIT_NOFILE => "RLIMIT_NOFILE",
60            _ => "UNKNOWN",
61        };
62        return Err(SandboxError::Syscall(format!(
63            "setrlimit({}) failed: {}",
64            resource_name,
65            std::io::Error::last_os_error()
66        )));
67    }
68    Ok(())
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_rlimit_config_default() {
77        let config = RlimitConfig::default();
78        assert!(config.max_memory.is_none());
79        assert!(config.max_cpu_seconds.is_none());
80    }
81
82    #[test]
83    fn test_empty_config_apply_succeeds() {
84        let config = RlimitConfig::default();
85        assert!(config.apply().is_ok());
86    }
87}