1use std::cmp::max;
18use std::time::Duration;
19
20#[cfg(target_os = "linux")]
21use libc::c_int;
22use tracing::{Span, instrument};
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(crashdump)]
43 guest_core_dump: bool,
44 #[cfg(gdb)]
46 guest_debug_info: Option<DebugInfo>,
47 input_data_size: usize,
50 output_data_size: usize,
53 heap_size_override: u64,
60 interrupt_retry_delay: Duration,
67 interrupt_vcpu_sigrtmin_offset: u8,
75 scratch_size: usize,
77}
78
79impl SandboxConfiguration {
80 pub const DEFAULT_INPUT_SIZE: usize = 0x4000;
82 pub const MIN_INPUT_SIZE: usize = 0x2000;
84 pub const DEFAULT_OUTPUT_SIZE: usize = 0x4000;
86 pub const MIN_OUTPUT_SIZE: usize = 0x2000;
88 pub const DEFAULT_INTERRUPT_RETRY_DELAY: Duration = Duration::from_micros(500);
90 pub const INTERRUPT_VCPU_SIGRTMIN_OFFSET: u8 = 0;
92 pub const DEFAULT_HEAP_SIZE: u64 = 131072;
94 pub const DEFAULT_SCRATCH_SIZE: usize = 0x48000;
96
97 #[allow(clippy::too_many_arguments)]
98 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
100 fn new(
101 input_data_size: usize,
102 output_data_size: usize,
103 heap_size_override: Option<u64>,
104 scratch_size: usize,
105 interrupt_retry_delay: Duration,
106 interrupt_vcpu_sigrtmin_offset: u8,
107 #[cfg(gdb)] guest_debug_info: Option<DebugInfo>,
108 #[cfg(crashdump)] guest_core_dump: bool,
109 ) -> Self {
110 Self {
111 input_data_size: max(input_data_size, Self::MIN_INPUT_SIZE),
112 output_data_size: max(output_data_size, Self::MIN_OUTPUT_SIZE),
113 heap_size_override: heap_size_override.unwrap_or(0),
114 scratch_size,
115 interrupt_retry_delay,
116 interrupt_vcpu_sigrtmin_offset,
117 #[cfg(gdb)]
118 guest_debug_info,
119 #[cfg(crashdump)]
120 guest_core_dump,
121 }
122 }
123
124 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
127 pub fn set_input_data_size(&mut self, input_data_size: usize) {
128 self.input_data_size = max(input_data_size, Self::MIN_INPUT_SIZE);
129 }
130
131 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
134 pub fn set_output_data_size(&mut self, output_data_size: usize) {
135 self.output_data_size = max(output_data_size, Self::MIN_OUTPUT_SIZE);
136 }
137
138 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
140 pub fn set_heap_size(&mut self, heap_size: u64) {
141 self.heap_size_override = heap_size;
142 }
143
144 #[cfg(target_os = "linux")]
146 pub fn set_interrupt_retry_delay(&mut self, delay: Duration) {
147 self.interrupt_retry_delay = delay;
148 }
149
150 #[cfg(target_os = "linux")]
152 pub fn get_interrupt_retry_delay(&self) -> Duration {
153 self.interrupt_retry_delay
154 }
155
156 #[cfg(target_os = "linux")]
158 pub fn get_interrupt_vcpu_sigrtmin_offset(&self) -> u8 {
159 self.interrupt_vcpu_sigrtmin_offset
160 }
161
162 #[cfg(target_os = "linux")]
170 pub fn set_interrupt_vcpu_sigrtmin_offset(&mut self, offset: u8) -> crate::Result<()> {
171 if libc::SIGRTMIN() + offset as c_int > libc::SIGRTMAX() {
172 return Err(crate::new_error!(
173 "Invalid SIGRTMIN offset: {}. It exceeds the maximum real-time signal number.",
174 offset
175 ));
176 }
177 self.interrupt_vcpu_sigrtmin_offset = offset;
178 Ok(())
179 }
180
181 #[cfg(crashdump)]
185 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
186 pub fn set_guest_core_dump(&mut self, enable: bool) {
187 self.guest_core_dump = enable;
188 }
189
190 #[cfg(gdb)]
192 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
193 pub fn set_guest_debug_info(&mut self, debug_info: DebugInfo) {
194 self.guest_debug_info = Some(debug_info);
195 }
196
197 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
198 pub(crate) fn get_input_data_size(&self) -> usize {
199 self.input_data_size
200 }
201
202 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
203 pub(crate) fn get_output_data_size(&self) -> usize {
204 self.output_data_size
205 }
206
207 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
208 pub(crate) fn get_scratch_size(&self) -> usize {
209 self.scratch_size
210 }
211
212 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
214 pub fn set_scratch_size(&mut self, scratch_size: usize) {
215 self.scratch_size = scratch_size;
216 }
217
218 #[cfg(crashdump)]
219 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
220 pub(crate) fn get_guest_core_dump(&self) -> bool {
221 self.guest_core_dump
222 }
223
224 #[cfg(gdb)]
225 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
226 pub(crate) fn get_guest_debug_info(&self) -> Option<DebugInfo> {
227 self.guest_debug_info
228 }
229
230 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
231 fn heap_size_override_opt(&self) -> Option<u64> {
232 (self.heap_size_override > 0).then_some(self.heap_size_override)
233 }
234
235 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
238 pub(crate) fn get_heap_size(&self) -> u64 {
239 self.heap_size_override_opt()
240 .unwrap_or(Self::DEFAULT_HEAP_SIZE)
241 }
242}
243
244impl Default for SandboxConfiguration {
245 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
246 fn default() -> Self {
247 Self::new(
248 Self::DEFAULT_INPUT_SIZE,
249 Self::DEFAULT_OUTPUT_SIZE,
250 None,
251 Self::DEFAULT_SCRATCH_SIZE,
252 Self::DEFAULT_INTERRUPT_RETRY_DELAY,
253 Self::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
254 #[cfg(gdb)]
255 None,
256 #[cfg(crashdump)]
257 true,
258 )
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::SandboxConfiguration;
265
266 #[test]
267 fn overrides() {
268 const HEAP_SIZE_OVERRIDE: u64 = 0x50000;
269 const INPUT_DATA_SIZE_OVERRIDE: usize = 0x4000;
270 const OUTPUT_DATA_SIZE_OVERRIDE: usize = 0x4001;
271 const SCRATCH_SIZE_OVERRIDE: usize = 0x60000;
272 let mut cfg = SandboxConfiguration::new(
273 INPUT_DATA_SIZE_OVERRIDE,
274 OUTPUT_DATA_SIZE_OVERRIDE,
275 Some(HEAP_SIZE_OVERRIDE),
276 SCRATCH_SIZE_OVERRIDE,
277 SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
278 SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
279 #[cfg(gdb)]
280 None,
281 #[cfg(crashdump)]
282 true,
283 );
284
285 let heap_size = cfg.get_heap_size();
286 let scratch_size = cfg.get_scratch_size();
287 assert_eq!(HEAP_SIZE_OVERRIDE, heap_size);
288 assert_eq!(SCRATCH_SIZE_OVERRIDE, scratch_size);
289
290 cfg.heap_size_override = 2048;
291 cfg.scratch_size = 0x40000;
292 assert_eq!(2048, cfg.heap_size_override);
293 assert_eq!(0x40000, cfg.scratch_size);
294 assert_eq!(INPUT_DATA_SIZE_OVERRIDE, cfg.input_data_size);
295 assert_eq!(OUTPUT_DATA_SIZE_OVERRIDE, cfg.output_data_size);
296 }
297
298 #[test]
299 fn min_sizes() {
300 let mut cfg = SandboxConfiguration::new(
301 SandboxConfiguration::MIN_INPUT_SIZE - 1,
302 SandboxConfiguration::MIN_OUTPUT_SIZE - 1,
303 None,
304 SandboxConfiguration::DEFAULT_SCRATCH_SIZE,
305 SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
306 SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
307 #[cfg(gdb)]
308 None,
309 #[cfg(crashdump)]
310 true,
311 );
312 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
313 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
314 assert_eq!(0, cfg.heap_size_override);
315
316 cfg.set_input_data_size(SandboxConfiguration::MIN_INPUT_SIZE - 1);
317 cfg.set_output_data_size(SandboxConfiguration::MIN_OUTPUT_SIZE - 1);
318
319 assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size);
320 assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size);
321 }
322
323 mod proptests {
324 use proptest::prelude::*;
325
326 use super::SandboxConfiguration;
327 #[cfg(gdb)]
328 use crate::sandbox::config::DebugInfo;
329
330 proptest! {
331 #[test]
332 fn input_data_size(size in SandboxConfiguration::MIN_INPUT_SIZE..=SandboxConfiguration::MIN_INPUT_SIZE * 10) {
333 let mut cfg = SandboxConfiguration::default();
334 cfg.set_input_data_size(size);
335 prop_assert_eq!(size, cfg.get_input_data_size());
336 }
337
338 #[test]
339 fn output_data_size(size in SandboxConfiguration::MIN_OUTPUT_SIZE..=SandboxConfiguration::MIN_OUTPUT_SIZE * 10) {
340 let mut cfg = SandboxConfiguration::default();
341 cfg.set_output_data_size(size);
342 prop_assert_eq!(size, cfg.get_output_data_size());
343 }
344
345
346 #[test]
347 fn heap_size_override(size in 0x1000..=0x10000u64) {
348 let mut cfg = SandboxConfiguration::default();
349 cfg.set_heap_size(size);
350 prop_assert_eq!(size, cfg.heap_size_override);
351 }
352
353 #[test]
354 #[cfg(gdb)]
355 fn guest_debug_info(port in 9000..=u16::MAX) {
356 let mut cfg = SandboxConfiguration::default();
357 let debug_info = DebugInfo { port };
358 cfg.set_guest_debug_info(debug_info);
359 prop_assert_eq!(debug_info, *cfg.get_guest_debug_info().as_ref().unwrap());
360 }
361 }
362 }
363}