1use std::cmp::max;
18use std::time::Duration;
19
20#[cfg(target_os = "linux")]
21use libc::c_int;
22use tracing::{Span, instrument};
23
24use crate::mem::exe::ExeInfo;
25
26#[cfg(gdb)]
28#[derive(Copy, Clone, Debug, Eq, PartialEq)]
29pub struct DebugInfo {
30 pub port: u16,
32}
33
34#[derive(Copy, Clone, Debug, Eq, PartialEq)]
36#[repr(C)]
37pub struct SandboxConfiguration {
38 #[cfg(crashdump)]
45 guest_core_dump: bool,
46 #[cfg(gdb)]
48 guest_debug_info: Option<DebugInfo>,
49 host_function_definition_size: usize,
52 input_data_size: usize,
55 output_data_size: usize,
58 stack_size_override: u64,
65 heap_size_override: u64,
72 interrupt_retry_delay: Duration,
79 interrupt_vcpu_sigrtmin_offset: u8,
87}
88
89impl SandboxConfiguration {
90 pub const DEFAULT_INPUT_SIZE: usize = 0x4000;
92 pub const MIN_INPUT_SIZE: usize = 0x2000;
94 pub const DEFAULT_OUTPUT_SIZE: usize = 0x4000;
96 pub const MIN_OUTPUT_SIZE: usize = 0x2000;
98 pub const DEFAULT_HOST_FUNCTION_DEFINITION_SIZE: usize = 0x1000;
102 pub const MIN_HOST_FUNCTION_DEFINITION_SIZE: usize = 0x1000;
104 pub const DEFAULT_INTERRUPT_RETRY_DELAY: Duration = Duration::from_micros(500);
106 pub const INTERRUPT_VCPU_SIGRTMIN_OFFSET: u8 = 0;
108
109 #[allow(clippy::too_many_arguments)]
110 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
112 fn new(
113 input_data_size: usize,
114 output_data_size: usize,
115 function_definition_size: usize,
116 stack_size_override: Option<u64>,
117 heap_size_override: Option<u64>,
118 interrupt_retry_delay: Duration,
119 interrupt_vcpu_sigrtmin_offset: u8,
120 #[cfg(gdb)] guest_debug_info: Option<DebugInfo>,
121 #[cfg(crashdump)] guest_core_dump: bool,
122 ) -> Self {
123 Self {
124 input_data_size: max(input_data_size, Self::MIN_INPUT_SIZE),
125 output_data_size: max(output_data_size, Self::MIN_OUTPUT_SIZE),
126 host_function_definition_size: max(
127 function_definition_size,
128 Self::MIN_HOST_FUNCTION_DEFINITION_SIZE,
129 ),
130 stack_size_override: stack_size_override.unwrap_or(0),
131 heap_size_override: heap_size_override.unwrap_or(0),
132 interrupt_retry_delay,
133 interrupt_vcpu_sigrtmin_offset,
134 #[cfg(gdb)]
135 guest_debug_info,
136 #[cfg(crashdump)]
137 guest_core_dump,
138 }
139 }
140
141 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
144 pub fn set_host_function_definition_size(&mut self, host_function_definition_size: usize) {
145 self.host_function_definition_size = max(
146 host_function_definition_size,
147 Self::MIN_HOST_FUNCTION_DEFINITION_SIZE,
148 );
149 }
150
151 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
154 pub fn set_input_data_size(&mut self, input_data_size: usize) {
155 self.input_data_size = max(input_data_size, Self::MIN_INPUT_SIZE);
156 }
157
158 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
161 pub fn set_output_data_size(&mut self, output_data_size: usize) {
162 self.output_data_size = max(output_data_size, Self::MIN_OUTPUT_SIZE);
163 }
164
165 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
167 pub fn set_stack_size(&mut self, stack_size: u64) {
168 self.stack_size_override = stack_size;
169 }
170
171 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
173 pub fn set_heap_size(&mut self, heap_size: u64) {
174 self.heap_size_override = heap_size;
175 }
176
177 #[cfg(target_os = "linux")]
179 pub fn set_interrupt_retry_delay(&mut self, delay: Duration) {
180 self.interrupt_retry_delay = delay;
181 }
182
183 #[cfg(target_os = "linux")]
185 pub fn get_interrupt_retry_delay(&self) -> Duration {
186 self.interrupt_retry_delay
187 }
188
189 #[cfg(target_os = "linux")]
191 pub fn get_interrupt_vcpu_sigrtmin_offset(&self) -> u8 {
192 self.interrupt_vcpu_sigrtmin_offset
193 }
194
195 #[cfg(target_os = "linux")]
203 pub fn set_interrupt_vcpu_sigrtmin_offset(&mut self, offset: u8) -> crate::Result<()> {
204 if libc::SIGRTMIN() + offset as c_int > libc::SIGRTMAX() {
205 return Err(crate::new_error!(
206 "Invalid SIGRTMIN offset: {}. It exceeds the maximum real-time signal number.",
207 offset
208 ));
209 }
210 self.interrupt_vcpu_sigrtmin_offset = offset;
211 Ok(())
212 }
213
214 #[cfg(crashdump)]
218 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
219 pub fn set_guest_core_dump(&mut self, enable: bool) {
220 self.guest_core_dump = enable;
221 }
222
223 #[cfg(gdb)]
225 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
226 pub fn set_guest_debug_info(&mut self, debug_info: DebugInfo) {
227 self.guest_debug_info = Some(debug_info);
228 }
229
230 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
231 pub(crate) fn get_host_function_definition_size(&self) -> usize {
232 self.host_function_definition_size
233 }
234
235 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
236 pub(crate) fn get_input_data_size(&self) -> usize {
237 self.input_data_size
238 }
239
240 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
241 pub(crate) fn get_output_data_size(&self) -> usize {
242 self.output_data_size
243 }
244
245 #[cfg(crashdump)]
246 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
247 pub(crate) fn get_guest_core_dump(&self) -> bool {
248 self.guest_core_dump
249 }
250
251 #[cfg(gdb)]
252 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
253 pub(crate) fn get_guest_debug_info(&self) -> Option<DebugInfo> {
254 self.guest_debug_info
255 }
256
257 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
258 fn stack_size_override_opt(&self) -> Option<u64> {
259 (self.stack_size_override > 0).then_some(self.stack_size_override)
260 }
261
262 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
263 fn heap_size_override_opt(&self) -> Option<u64> {
264 (self.heap_size_override > 0).then_some(self.heap_size_override)
265 }
266
267 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
270 pub(crate) fn get_stack_size(&self, exe_info: &ExeInfo) -> u64 {
271 self.stack_size_override_opt()
272 .unwrap_or_else(|| exe_info.stack_reserve())
273 }
274
275 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
278 pub(crate) fn get_heap_size(&self, exe_info: &ExeInfo) -> u64 {
279 self.heap_size_override_opt()
280 .unwrap_or_else(|| exe_info.heap_reserve())
281 }
282}
283
284impl Default for SandboxConfiguration {
285 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
286 fn default() -> Self {
287 Self::new(
288 Self::DEFAULT_INPUT_SIZE,
289 Self::DEFAULT_OUTPUT_SIZE,
290 Self::DEFAULT_HOST_FUNCTION_DEFINITION_SIZE,
291 None,
292 None,
293 Self::DEFAULT_INTERRUPT_RETRY_DELAY,
294 Self::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
295 #[cfg(gdb)]
296 None,
297 #[cfg(crashdump)]
298 true,
299 )
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::SandboxConfiguration;
306 use crate::testing::simple_guest_exe_info;
307
308 #[test]
309 fn overrides() {
310 const STACK_SIZE_OVERRIDE: u64 = 0x10000;
311 const HEAP_SIZE_OVERRIDE: u64 = 0x50000;
312 const INPUT_DATA_SIZE_OVERRIDE: usize = 0x4000;
313 const OUTPUT_DATA_SIZE_OVERRIDE: usize = 0x4001;
314 const HOST_FUNCTION_DEFINITION_SIZE_OVERRIDE: usize = 0x4002;
315 let mut cfg = SandboxConfiguration::new(
316 INPUT_DATA_SIZE_OVERRIDE,
317 OUTPUT_DATA_SIZE_OVERRIDE,
318 HOST_FUNCTION_DEFINITION_SIZE_OVERRIDE,
319 Some(STACK_SIZE_OVERRIDE),
320 Some(HEAP_SIZE_OVERRIDE),
321 SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
322 SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
323 #[cfg(gdb)]
324 None,
325 #[cfg(crashdump)]
326 true,
327 );
328 let exe_info = simple_guest_exe_info().unwrap();
329
330 let stack_size = cfg.get_stack_size(&exe_info);
331 let heap_size = cfg.get_heap_size(&exe_info);
332 assert_eq!(STACK_SIZE_OVERRIDE, stack_size);
333 assert_eq!(HEAP_SIZE_OVERRIDE, heap_size);
334
335 cfg.stack_size_override = 1024;
336 cfg.heap_size_override = 2048;
337 assert_eq!(1024, cfg.stack_size_override);
338 assert_eq!(2048, cfg.heap_size_override);
339 assert_eq!(INPUT_DATA_SIZE_OVERRIDE, cfg.input_data_size);
340 assert_eq!(OUTPUT_DATA_SIZE_OVERRIDE, cfg.output_data_size);
341 assert_eq!(
342 HOST_FUNCTION_DEFINITION_SIZE_OVERRIDE,
343 cfg.host_function_definition_size
344 );
345 }
346
347 #[test]
348 fn min_sizes() {
349 let mut cfg = SandboxConfiguration::new(
350 SandboxConfiguration::MIN_INPUT_SIZE - 1,
351 SandboxConfiguration::MIN_OUTPUT_SIZE - 1,
352 SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE - 1,
353 None,
354 None,
355 SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
356 SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
357 #[cfg(gdb)]
358 None,
359 #[cfg(crashdump)]
360 true,
361 );
362 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
363 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
364 assert_eq!(
365 SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE,
366 cfg.host_function_definition_size
367 );
368 assert_eq!(0, cfg.stack_size_override);
369 assert_eq!(0, cfg.heap_size_override);
370
371 cfg.set_input_data_size(SandboxConfiguration::MIN_INPUT_SIZE - 1);
372 cfg.set_output_data_size(SandboxConfiguration::MIN_OUTPUT_SIZE - 1);
373 cfg.set_host_function_definition_size(
374 SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE - 1,
375 );
376
377 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
378 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
379 assert_eq!(
380 SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE,
381 cfg.host_function_definition_size
382 );
383 }
384
385 mod proptests {
386 use proptest::prelude::*;
387
388 use super::SandboxConfiguration;
389 #[cfg(gdb)]
390 use crate::sandbox::config::DebugInfo;
391
392 proptest! {
393 #[test]
394 fn input_data_size(size in SandboxConfiguration::MIN_INPUT_SIZE..=SandboxConfiguration::MIN_INPUT_SIZE * 10) {
395 let mut cfg = SandboxConfiguration::default();
396 cfg.set_input_data_size(size);
397 prop_assert_eq!(size, cfg.get_input_data_size());
398 }
399
400 #[test]
401 fn output_data_size(size in SandboxConfiguration::MIN_OUTPUT_SIZE..=SandboxConfiguration::MIN_OUTPUT_SIZE * 10) {
402 let mut cfg = SandboxConfiguration::default();
403 cfg.set_output_data_size(size);
404 prop_assert_eq!(size, cfg.get_output_data_size());
405 }
406
407 #[test]
408 fn host_function_definition_size(size in SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE..=SandboxConfiguration::MIN_HOST_FUNCTION_DEFINITION_SIZE * 10) {
409 let mut cfg = SandboxConfiguration::default();
410 cfg.set_host_function_definition_size(size);
411 prop_assert_eq!(size, cfg.get_host_function_definition_size());
412 }
413
414 #[test]
415 fn stack_size_override(size in 0x1000..=0x10000u64) {
416 let mut cfg = SandboxConfiguration::default();
417 cfg.set_stack_size(size);
418 prop_assert_eq!(size, cfg.stack_size_override);
419 }
420
421 #[test]
422 fn heap_size_override(size in 0x1000..=0x10000u64) {
423 let mut cfg = SandboxConfiguration::default();
424 cfg.set_heap_size(size);
425 prop_assert_eq!(size, cfg.heap_size_override);
426 }
427
428 #[test]
429 #[cfg(gdb)]
430 fn guest_debug_info(port in 9000..=u16::MAX) {
431 let mut cfg = SandboxConfiguration::default();
432 let debug_info = DebugInfo { port };
433 cfg.set_guest_debug_info(debug_info);
434 prop_assert_eq!(debug_info, *cfg.get_guest_debug_info().as_ref().unwrap());
435 }
436 }
437 }
438}