1use std::cmp::{max, min};
18use std::time::Duration;
19
20use tracing::{instrument, Span};
21
22use crate::mem::exe::ExeInfo;
23
24#[cfg(gdb)]
26#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27pub struct DebugInfo {
28 pub port: u16,
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34#[repr(C)]
35pub struct SandboxConfiguration {
36 #[cfg(gdb)]
38 guest_debug_info: Option<DebugInfo>,
39 input_data_size: usize,
42 output_data_size: usize,
45 stack_size_override: u64,
52 heap_size_override: u64,
59 max_execution_time: u16,
68 max_wait_for_cancellation: u8,
76 max_initialization_time: u16,
84}
85
86impl SandboxConfiguration {
87 pub const DEFAULT_INPUT_SIZE: usize = 0x4000;
89 pub const MIN_INPUT_SIZE: usize = 0x2000;
91 pub const DEFAULT_OUTPUT_SIZE: usize = 0x4000;
93 pub const MIN_OUTPUT_SIZE: usize = 0x2000;
95 pub const DEFAULT_MAX_INITIALIZATION_TIME: u16 = 2000;
97 pub const MIN_MAX_INITIALIZATION_TIME: u16 = 1;
99 pub const MAX_MAX_INITIALIZATION_TIME: u16 = u16::MAX;
101 pub const DEFAULT_MAX_EXECUTION_TIME: u16 = 1000;
103 pub const MIN_MAX_EXECUTION_TIME: u16 = 1;
105 pub const MAX_MAX_EXECUTION_TIME: u16 = u16::MAX;
107 pub const DEFAULT_MAX_WAIT_FOR_CANCELLATION: u8 = 100;
109 pub const MIN_MAX_WAIT_FOR_CANCELLATION: u8 = 10;
111 pub const MAX_MAX_WAIT_FOR_CANCELLATION: u8 = u8::MAX;
113
114 #[allow(clippy::too_many_arguments)]
115 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
117 fn new(
118 input_data_size: usize,
119 output_data_size: usize,
120 stack_size_override: Option<u64>,
121 heap_size_override: Option<u64>,
122 max_execution_time: Option<Duration>,
123 max_initialization_time: Option<Duration>,
124 max_wait_for_cancellation: Option<Duration>,
125 #[cfg(gdb)] guest_debug_info: Option<DebugInfo>,
126 ) -> Self {
127 Self {
128 input_data_size: max(input_data_size, Self::MIN_INPUT_SIZE),
129 output_data_size: max(output_data_size, Self::MIN_OUTPUT_SIZE),
130 stack_size_override: stack_size_override.unwrap_or(0),
131 heap_size_override: heap_size_override.unwrap_or(0),
132 max_execution_time: {
133 match max_execution_time {
134 Some(max_execution_time) => match max_execution_time.as_millis() {
135 0 => Self::DEFAULT_MAX_EXECUTION_TIME,
136 1.. => min(
137 Self::MAX_MAX_EXECUTION_TIME.into(),
138 max(
139 max_execution_time.as_millis(),
140 Self::MIN_MAX_EXECUTION_TIME.into(),
141 ),
142 ) as u16,
143 },
144 None => Self::DEFAULT_MAX_EXECUTION_TIME,
145 }
146 },
147 max_wait_for_cancellation: {
148 match max_wait_for_cancellation {
149 Some(max_wait_for_cancellation) => {
150 match max_wait_for_cancellation.as_millis() {
151 0 => Self::DEFAULT_MAX_WAIT_FOR_CANCELLATION,
152 1.. => min(
153 Self::MAX_MAX_WAIT_FOR_CANCELLATION.into(),
154 max(
155 max_wait_for_cancellation.as_millis(),
156 Self::MIN_MAX_WAIT_FOR_CANCELLATION.into(),
157 ),
158 ) as u8,
159 }
160 }
161 None => Self::DEFAULT_MAX_WAIT_FOR_CANCELLATION,
162 }
163 },
164 max_initialization_time: {
165 match max_initialization_time {
166 Some(max_initialization_time) => match max_initialization_time.as_millis() {
167 0 => Self::DEFAULT_MAX_INITIALIZATION_TIME,
168 1.. => min(
169 Self::MAX_MAX_INITIALIZATION_TIME.into(),
170 max(
171 max_initialization_time.as_millis(),
172 Self::MIN_MAX_INITIALIZATION_TIME.into(),
173 ),
174 ) as u16,
175 },
176 None => Self::DEFAULT_MAX_INITIALIZATION_TIME,
177 }
178 },
179 #[cfg(gdb)]
180 guest_debug_info,
181 }
182 }
183
184 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
187 pub fn set_input_data_size(&mut self, input_data_size: usize) {
188 self.input_data_size = max(input_data_size, Self::MIN_INPUT_SIZE);
189 }
190
191 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
194 pub fn set_output_data_size(&mut self, output_data_size: usize) {
195 self.output_data_size = max(output_data_size, Self::MIN_OUTPUT_SIZE);
196 }
197
198 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
200 pub fn set_stack_size(&mut self, stack_size: u64) {
201 self.stack_size_override = stack_size;
202 }
203
204 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
206 pub fn set_heap_size(&mut self, heap_size: u64) {
207 self.heap_size_override = heap_size;
208 }
209
210 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
214 pub fn set_max_execution_time(&mut self, max_execution_time: Duration) {
215 match max_execution_time.as_millis() {
216 0 => self.max_execution_time = Self::DEFAULT_MAX_EXECUTION_TIME,
217 1.. => {
218 self.max_execution_time = min(
219 Self::MAX_MAX_EXECUTION_TIME.into(),
220 max(
221 max_execution_time.as_millis(),
222 Self::MIN_MAX_EXECUTION_TIME.into(),
223 ),
224 ) as u16
225 }
226 }
227 }
228
229 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
233 pub fn set_max_execution_cancel_wait_time(&mut self, max_wait_for_cancellation: Duration) {
234 match max_wait_for_cancellation.as_millis() {
235 0 => self.max_wait_for_cancellation = Self::DEFAULT_MAX_WAIT_FOR_CANCELLATION,
236 1.. => {
237 self.max_wait_for_cancellation = min(
238 Self::MAX_MAX_WAIT_FOR_CANCELLATION.into(),
239 max(
240 max_wait_for_cancellation.as_millis(),
241 Self::MIN_MAX_WAIT_FOR_CANCELLATION.into(),
242 ),
243 ) as u8
244 }
245 }
246 }
247
248 pub fn set_max_initialization_time(&mut self, max_initialization_time: Duration) {
252 match max_initialization_time.as_millis() {
253 0 => self.max_initialization_time = Self::DEFAULT_MAX_INITIALIZATION_TIME,
254 1.. => {
255 self.max_initialization_time = min(
256 Self::MAX_MAX_INITIALIZATION_TIME.into(),
257 max(
258 max_initialization_time.as_millis(),
259 Self::MIN_MAX_INITIALIZATION_TIME.into(),
260 ),
261 ) as u16
262 }
263 }
264 }
265
266 #[cfg(gdb)]
268 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
269 pub fn set_guest_debug_info(&mut self, debug_info: DebugInfo) {
270 self.guest_debug_info = Some(debug_info);
271 }
272
273 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
274 pub(crate) fn get_input_data_size(&self) -> usize {
275 self.input_data_size
276 }
277
278 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
279 pub(crate) fn get_output_data_size(&self) -> usize {
280 self.output_data_size
281 }
282
283 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
284 pub(crate) fn get_max_execution_time(&self) -> u16 {
285 self.max_execution_time
286 }
287
288 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
289 pub(crate) fn get_max_wait_for_cancellation(&self) -> u8 {
290 self.max_wait_for_cancellation
291 }
292
293 pub(crate) fn get_max_initialization_time(&self) -> u16 {
294 self.max_initialization_time
295 }
296
297 #[cfg(gdb)]
298 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
299 pub(crate) fn get_guest_debug_info(&self) -> Option<DebugInfo> {
300 self.guest_debug_info
301 }
302
303 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
304 fn stack_size_override_opt(&self) -> Option<u64> {
305 (self.stack_size_override > 0).then_some(self.stack_size_override)
306 }
307
308 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
309 fn heap_size_override_opt(&self) -> Option<u64> {
310 (self.heap_size_override > 0).then_some(self.heap_size_override)
311 }
312
313 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
316 pub(crate) fn get_stack_size(&self, exe_info: &ExeInfo) -> u64 {
317 self.stack_size_override_opt()
318 .unwrap_or_else(|| exe_info.stack_reserve())
319 }
320
321 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
324 pub(crate) fn get_heap_size(&self, exe_info: &ExeInfo) -> u64 {
325 self.heap_size_override_opt()
326 .unwrap_or_else(|| exe_info.heap_reserve())
327 }
328}
329
330impl Default for SandboxConfiguration {
331 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
332 fn default() -> Self {
333 Self::new(
334 Self::DEFAULT_INPUT_SIZE,
335 Self::DEFAULT_OUTPUT_SIZE,
336 None,
337 None,
338 None,
339 None,
340 None,
341 #[cfg(gdb)]
342 None,
343 )
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use std::time::Duration;
350
351 use super::SandboxConfiguration;
352 use crate::testing::simple_guest_exe_info;
353
354 #[test]
355 fn overrides() {
356 const STACK_SIZE_OVERRIDE: u64 = 0x10000;
357 const HEAP_SIZE_OVERRIDE: u64 = 0x50000;
358 const INPUT_DATA_SIZE_OVERRIDE: usize = 0x4000;
359 const OUTPUT_DATA_SIZE_OVERRIDE: usize = 0x4001;
360 const MAX_EXECUTION_TIME_OVERRIDE: u16 = 1010;
361 const MAX_WAIT_FOR_CANCELLATION_OVERRIDE: u8 = 200;
362 const MAX_INITIALIZATION_TIME_OVERRIDE: u16 = 2000;
363 let mut cfg = SandboxConfiguration::new(
364 INPUT_DATA_SIZE_OVERRIDE,
365 OUTPUT_DATA_SIZE_OVERRIDE,
366 Some(STACK_SIZE_OVERRIDE),
367 Some(HEAP_SIZE_OVERRIDE),
368 Some(Duration::from_millis(MAX_EXECUTION_TIME_OVERRIDE as u64)),
369 Some(Duration::from_millis(
370 MAX_INITIALIZATION_TIME_OVERRIDE as u64,
371 )),
372 Some(Duration::from_millis(
373 MAX_WAIT_FOR_CANCELLATION_OVERRIDE as u64,
374 )),
375 #[cfg(gdb)]
376 None,
377 );
378 let exe_info = simple_guest_exe_info().unwrap();
379
380 let stack_size = cfg.get_stack_size(&exe_info);
381 let heap_size = cfg.get_heap_size(&exe_info);
382 assert_eq!(STACK_SIZE_OVERRIDE, stack_size);
383 assert_eq!(HEAP_SIZE_OVERRIDE, heap_size);
384
385 cfg.stack_size_override = 1024;
386 cfg.heap_size_override = 2048;
387 assert_eq!(1024, cfg.stack_size_override);
388 assert_eq!(2048, cfg.heap_size_override);
389 assert_eq!(INPUT_DATA_SIZE_OVERRIDE, cfg.input_data_size);
390 assert_eq!(OUTPUT_DATA_SIZE_OVERRIDE, cfg.output_data_size);
391 assert_eq!(MAX_EXECUTION_TIME_OVERRIDE, cfg.max_execution_time);
392 assert_eq!(
393 MAX_WAIT_FOR_CANCELLATION_OVERRIDE,
394 cfg.max_wait_for_cancellation
395 );
396 assert_eq!(
397 MAX_WAIT_FOR_CANCELLATION_OVERRIDE,
398 cfg.max_wait_for_cancellation
399 );
400 }
401
402 #[test]
403 fn min_sizes() {
404 let mut cfg = SandboxConfiguration::new(
405 SandboxConfiguration::MIN_INPUT_SIZE - 1,
406 SandboxConfiguration::MIN_OUTPUT_SIZE - 1,
407 None,
408 None,
409 Some(Duration::from_millis(
410 SandboxConfiguration::MIN_MAX_EXECUTION_TIME as u64,
411 )),
412 Some(Duration::from_millis(
413 SandboxConfiguration::MIN_MAX_INITIALIZATION_TIME as u64,
414 )),
415 Some(Duration::from_millis(
416 SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION as u64 - 1,
417 )),
418 #[cfg(gdb)]
419 None,
420 );
421 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
422 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
423 assert_eq!(0, cfg.stack_size_override);
424 assert_eq!(0, cfg.heap_size_override);
425 assert_eq!(
426 SandboxConfiguration::MIN_MAX_EXECUTION_TIME,
427 cfg.max_execution_time
428 );
429 assert_eq!(
430 SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION,
431 cfg.max_wait_for_cancellation
432 );
433 assert_eq!(
434 SandboxConfiguration::MIN_MAX_EXECUTION_TIME,
435 cfg.max_initialization_time
436 );
437
438 cfg.set_input_data_size(SandboxConfiguration::MIN_INPUT_SIZE - 1);
439 cfg.set_output_data_size(SandboxConfiguration::MIN_OUTPUT_SIZE - 1);
440 cfg.set_max_execution_time(Duration::from_millis(
441 SandboxConfiguration::MIN_MAX_EXECUTION_TIME as u64,
442 ));
443 cfg.set_max_initialization_time(Duration::from_millis(
444 SandboxConfiguration::MIN_MAX_INITIALIZATION_TIME as u64 - 1,
445 ));
446 cfg.set_max_execution_cancel_wait_time(Duration::from_millis(
447 SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION as u64 - 1,
448 ));
449
450 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
451 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
452 assert_eq!(
453 SandboxConfiguration::MIN_MAX_EXECUTION_TIME,
454 cfg.max_execution_time
455 );
456 assert_eq!(
457 SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION,
458 cfg.max_wait_for_cancellation
459 );
460 }
461
462 mod proptests {
463 use proptest::prelude::*;
464
465 use super::SandboxConfiguration;
466 #[cfg(gdb)]
467 use crate::sandbox::config::DebugInfo;
468
469 proptest! {
470 #[test]
471 fn input_data_size(size in SandboxConfiguration::MIN_INPUT_SIZE..=SandboxConfiguration::MIN_INPUT_SIZE * 10) {
472 let mut cfg = SandboxConfiguration::default();
473 cfg.set_input_data_size(size);
474 prop_assert_eq!(size, cfg.get_input_data_size());
475 }
476
477 #[test]
478 fn output_data_size(size in SandboxConfiguration::MIN_OUTPUT_SIZE..=SandboxConfiguration::MIN_OUTPUT_SIZE * 10) {
479 let mut cfg = SandboxConfiguration::default();
480 cfg.set_output_data_size(size);
481 prop_assert_eq!(size, cfg.get_output_data_size());
482 }
483
484 #[test]
485 fn max_execution_time(time in SandboxConfiguration::MIN_MAX_EXECUTION_TIME..=SandboxConfiguration::MIN_MAX_EXECUTION_TIME * 10) {
486 let mut cfg = SandboxConfiguration::default();
487 cfg.set_max_execution_time(std::time::Duration::from_millis(time.into()));
488 prop_assert_eq!(time, cfg.get_max_execution_time());
489 }
490
491 #[test]
492 fn max_wait_for_cancellation(time in SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION..=SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION * 10) {
493 let mut cfg = SandboxConfiguration::default();
494 cfg.set_max_execution_cancel_wait_time(std::time::Duration::from_millis(time.into()));
495 prop_assert_eq!(time, cfg.get_max_wait_for_cancellation());
496 }
497
498 #[test]
499 fn max_initialization_time(time in SandboxConfiguration::MIN_MAX_INITIALIZATION_TIME..=SandboxConfiguration::MIN_MAX_INITIALIZATION_TIME * 10) {
500 let mut cfg = SandboxConfiguration::default();
501 cfg.set_max_initialization_time(std::time::Duration::from_millis(time.into()));
502 prop_assert_eq!(time, cfg.get_max_initialization_time());
503 }
504
505 #[test]
506 fn stack_size_override(size in 0x1000..=0x10000u64) {
507 let mut cfg = SandboxConfiguration::default();
508 cfg.set_stack_size(size);
509 prop_assert_eq!(size, cfg.stack_size_override);
510 }
511
512 #[test]
513 fn heap_size_override(size in 0x1000..=0x10000u64) {
514 let mut cfg = SandboxConfiguration::default();
515 cfg.set_heap_size(size);
516 prop_assert_eq!(size, cfg.heap_size_override);
517 }
518
519 #[test]
520 #[cfg(gdb)]
521 fn guest_debug_info(port in 9000..=u16::MAX) {
522 let mut cfg = SandboxConfiguration::default();
523 let debug_info = DebugInfo { port };
524 cfg.set_guest_debug_info(debug_info);
525 prop_assert_eq!(debug_info, *cfg.get_guest_debug_info().as_ref().unwrap());
526 }
527 }
528 }
529}