1use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
6use std::time::Duration;
7
8#[derive(Debug, Clone)]
10pub struct SandboxConfig {
11 pub max_memory_bytes: usize,
13 pub max_cpu_time_secs: u64,
15 pub max_call_duration: Duration,
17 pub allow_network: bool,
19 pub allow_filesystem: bool,
21 pub allowed_syscalls: Vec<u32>,
23}
24
25impl Default for SandboxConfig {
26 fn default() -> Self {
27 Self {
28 max_memory_bytes: 128 * 1024 * 1024, max_cpu_time_secs: 60,
30 max_call_duration: Duration::from_secs(30),
31 allow_network: false,
32 allow_filesystem: true,
33 allowed_syscalls: Vec::new(),
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct ResourceUsage {
41 pub memory_bytes: usize,
43 pub peak_memory_bytes: usize,
45 pub cpu_time_ms: u64,
47 pub total_calls: u64,
49 pub total_syscalls: u64,
51}
52
53#[derive(Debug)]
55pub struct ResourceTracker {
56 memory_bytes: AtomicUsize,
58 peak_memory_bytes: AtomicUsize,
60 cpu_time_ms: AtomicU64,
62 total_calls: AtomicU64,
64 total_syscalls: AtomicU64,
66}
67
68impl ResourceTracker {
69 pub fn new() -> Self {
71 Self {
72 memory_bytes: AtomicUsize::new(0),
73 peak_memory_bytes: AtomicUsize::new(0),
74 cpu_time_ms: AtomicU64::new(0),
75 total_calls: AtomicU64::new(0),
76 total_syscalls: AtomicU64::new(0),
77 }
78 }
79
80 pub fn update_memory(&self, bytes: usize) {
82 self.memory_bytes.store(bytes, Ordering::Release);
83
84 let mut peak = self.peak_memory_bytes.load(Ordering::Acquire);
86 while bytes > peak {
87 match self.peak_memory_bytes.compare_exchange_weak(
88 peak,
89 bytes,
90 Ordering::Relaxed,
91 Ordering::Relaxed,
92 ) {
93 Ok(_) => break,
94 Err(x) => peak = x,
95 }
96 }
97 }
98
99 pub fn record_call(&self) {
101 self.total_calls.fetch_add(1, Ordering::Relaxed);
102 }
103
104 pub fn record_cpu_time(&self, ms: u64) {
106 self.cpu_time_ms.fetch_add(ms, Ordering::Relaxed);
107 }
108
109 pub fn record_syscall(&self) {
111 self.total_syscalls.fetch_add(1, Ordering::Relaxed);
112 }
113
114 pub fn usage(&self) -> ResourceUsage {
116 ResourceUsage {
117 memory_bytes: self.memory_bytes.load(Ordering::Acquire),
118 peak_memory_bytes: self.peak_memory_bytes.load(Ordering::Acquire),
119 cpu_time_ms: self.cpu_time_ms.load(Ordering::Acquire),
120 total_calls: self.total_calls.load(Ordering::Acquire),
121 total_syscalls: self.total_syscalls.load(Ordering::Acquire),
122 }
123 }
124
125 pub fn exceeds_limits(&self, config: &SandboxConfig) -> bool {
127 self.memory_bytes.load(Ordering::Acquire) > config.max_memory_bytes
128 }
129
130 pub fn reset(&self) {
132 self.memory_bytes.store(0, Ordering::Release);
133 self.cpu_time_ms.store(0, Ordering::Relaxed);
134 self.total_calls.store(0, Ordering::Relaxed);
135 self.total_syscalls.store(0, Ordering::Relaxed);
136 }
137}
138
139impl Default for ResourceTracker {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145#[derive(Debug, Clone, Copy)]
147pub struct Permissions {
148 pub can_allocate: bool,
150 pub can_network: bool,
152 pub can_filesystem: bool,
154 pub can_thread: bool,
156 pub can_env: bool,
158}
159
160impl Default for Permissions {
161 fn default() -> Self {
162 Self {
163 can_allocate: true,
164 can_network: false,
165 can_filesystem: true,
166 can_thread: true,
167 can_env: false,
168 }
169 }
170}
171
172impl Permissions {
173 pub fn restrictive() -> Self {
175 Self {
176 can_allocate: true,
177 can_network: false,
178 can_filesystem: false,
179 can_thread: false,
180 can_env: false,
181 }
182 }
183
184 pub fn permissive() -> Self {
186 Self {
187 can_allocate: true,
188 can_network: true,
189 can_filesystem: true,
190 can_thread: true,
191 can_env: true,
192 }
193 }
194
195 pub fn check(&self, permission: Permission) -> bool {
197 match permission {
198 Permission::Allocate => self.can_allocate,
199 Permission::Network => self.can_network,
200 Permission::Filesystem => self.can_filesystem,
201 Permission::Thread => self.can_thread,
202 Permission::Env => self.can_env,
203 }
204 }
205}
206
207#[derive(Debug, Clone, Copy)]
209pub enum Permission {
210 Allocate,
211 Network,
212 Filesystem,
213 Thread,
214 Env,
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_sandbox_config() {
223 let config = SandboxConfig::default();
224 assert_eq!(config.max_memory_bytes, 128 * 1024 * 1024);
225 assert!(!config.allow_network);
226 }
227
228 #[test]
229 fn test_resource_tracker() {
230 let tracker = ResourceTracker::new();
231
232 tracker.update_memory(1000);
233 tracker.record_call();
234
235 let usage = tracker.usage();
236 assert_eq!(usage.memory_bytes, 1000);
237 assert_eq!(usage.total_calls, 1);
238 }
239
240 #[test]
241 fn test_permissions() {
242 let perms = Permissions::restrictive();
243 assert!(perms.can_allocate);
244 assert!(!perms.can_network);
245 assert!(!perms.can_filesystem);
246 }
247}