Skip to main content

hyperlight_js/sandbox/
sandbox_builder.rs

1#[cfg(target_os = "linux")]
2use std::time::Duration;
3
4use hyperlight_host::sandbox::{is_hypervisor_present, SandboxConfiguration};
5use hyperlight_host::{GuestBinary, HyperlightError, Result};
6
7use super::proto_js_sandbox::ProtoJSSandbox;
8use crate::HostPrintFn;
9
10/// A builder for a ProtoJSSandbox
11pub struct SandboxBuilder {
12    config: SandboxConfiguration,
13    host_print_fn: Option<HostPrintFn>,
14}
15
16const MIN_STACK_SIZE: u64 = 256 * 1024;
17// The minimum heap size is 4096KB.
18const MIN_HEAP_SIZE: u64 = 4096 * 1024;
19
20impl SandboxBuilder {
21    /// Create a new SandboxBuilder
22    pub fn new() -> Self {
23        let mut config = SandboxConfiguration::default();
24        config.set_stack_size(MIN_STACK_SIZE);
25        config.set_heap_size(MIN_HEAP_SIZE);
26
27        Self {
28            config,
29            host_print_fn: None,
30        }
31    }
32
33    /// Set the host print function
34    pub fn with_host_print_fn(mut self, host_print_fn: HostPrintFn) -> Self {
35        self.host_print_fn = Some(host_print_fn);
36        self
37    }
38
39    /// Set the guest output buffer size
40    pub fn with_guest_output_buffer_size(mut self, guest_output_buffer_size: usize) -> Self {
41        self.config.set_output_data_size(guest_output_buffer_size);
42        self
43    }
44
45    /// Set the guest input buffer size
46    /// This is the size of the buffer that the guest can write to
47    /// to send data to the host
48    /// The host can read from this buffer
49    /// The guest can write to this buffer
50    pub fn with_guest_input_buffer_size(mut self, guest_input_buffer_size: usize) -> Self {
51        self.config.set_input_data_size(guest_input_buffer_size);
52        self
53    }
54
55    /// Set the guest stack size
56    /// This is the size of the stack that code executing in the guest can use.
57    /// If this value is too small then the guest will fail with a stack overflow error
58    /// The default value (and minimum) is set to the value of the MIN_STACK_SIZE const.
59    pub fn with_guest_stack_size(mut self, guest_stack_size: u64) -> Self {
60        if guest_stack_size > MIN_STACK_SIZE {
61            self.config.set_stack_size(guest_stack_size);
62        }
63        self
64    }
65
66    /// Set the guest heap size
67    /// This is the size of the heap that code executing in the guest can use.
68    /// If this value is too small then the guest will fail, usually with a malloc failed error
69    /// The default (and minimum) value for this is set to the value of the MIN_HEAP_SIZE const.
70    pub fn with_guest_heap_size(mut self, guest_heap_size: u64) -> Self {
71        if guest_heap_size > MIN_HEAP_SIZE {
72            self.config.set_heap_size(guest_heap_size);
73        }
74        self
75    }
76
77    /// Sets the offset from `SIGRTMIN` to determine the real-time signal used for
78    /// interrupting the VCPU thread.
79    ///
80    /// The final signal number is computed as `SIGRTMIN + offset`, and it must fall within
81    /// the valid range of real-time signals supported by the host system.
82    ///
83    /// Returns Ok(()) if the offset is valid, or an error if it exceeds the maximum real-time signal number.
84    #[cfg(target_os = "linux")]
85    pub fn set_interrupt_vcpu_sigrtmin_offset(&mut self, offset: u8) -> Result<()> {
86        self.config.set_interrupt_vcpu_sigrtmin_offset(offset)?;
87        Ok(())
88    }
89
90    /// Sets the interrupt retry delay
91    /// This controls the delay between sending signals to the VCPU thread to interrupt it.
92    #[cfg(target_os = "linux")]
93    pub fn with_interrupt_retry_delay(mut self, delay: Duration) -> Self {
94        self.config.set_interrupt_retry_delay(delay);
95        self
96    }
97
98    /// Get the current configuration
99    pub fn get_config(&self) -> &SandboxConfiguration {
100        &self.config
101    }
102
103    /// Enable or disable crashdump generation for the sandbox
104    /// When enabled, core dumps will be generated when the guest crashes
105    /// This requires the `crashdump` feature to be enabled
106    #[cfg(feature = "crashdump")]
107    pub fn with_crashdump_enabled(mut self, enabled: bool) -> Self {
108        self.config.set_guest_core_dump(enabled);
109        self
110    }
111
112    /// Enable debugging for the guest runtime
113    /// This will allow the guest runtime to be natively debugged using GDB or
114    /// other debugging tools
115    ///
116    /// # Example:
117    /// ```rust
118    /// use hyperlight_js::SandboxBuilder;
119    /// let sandbox = SandboxBuilder::new()
120    ///    .with_debugging_enabled(8080) // Enable debugging on port 8080
121    ///    .build()
122    ///    .expect("Failed to build sandbox");
123    /// ```
124    /// # Note:
125    /// This method is only available when the `gdb` feature is enabled
126    /// and the code is compiled in debug mode.
127    #[cfg(all(feature = "gdb", debug_assertions))]
128    pub fn with_debugging_enabled(mut self, port: u16) -> Self {
129        let debug_info = hyperlight_host::sandbox::config::DebugInfo { port };
130        self.config.set_guest_debug_info(debug_info);
131        self
132    }
133
134    /// Build the ProtoJSSandbox
135    pub fn build(self) -> Result<ProtoJSSandbox> {
136        if !is_hypervisor_present() {
137            return Err(HyperlightError::NoHypervisorFound());
138        }
139        let guest_binary = GuestBinary::Buffer(super::JSRUNTIME);
140        let proto_js_sandbox =
141            ProtoJSSandbox::new(guest_binary, Some(self.config), self.host_print_fn)?;
142        Ok(proto_js_sandbox)
143    }
144}
145
146impl Default for SandboxBuilder {
147    fn default() -> Self {
148        Self::new()
149    }
150}