From eeca10c9574ecb3eb03955350e91b6f58d562792 Mon Sep 17 00:00:00 2001 From: Simon Davies Date: Wed, 25 Feb 2026 21:01:14 +0000 Subject: [PATCH] Updates for the latest hypleright version Signed-off-by: Simon Davies --- Cargo.lock | 140 ++++++++---------- Cargo.toml | 8 +- src/hyperlight-js/benches/benchmarks.rs | 2 +- src/hyperlight-js/examples/interrupt/main.rs | 2 +- src/hyperlight-js/src/sandbox/js_sandbox.rs | 10 +- .../src/sandbox/loaded_js_sandbox.rs | 12 +- .../src/sandbox/sandbox_builder.rs | 44 ++++-- src/hyperlight-js/tests/monitors.rs | 6 +- src/hyperlight-js/tests/termination.rs | 2 +- src/js-host-api/README.md | 11 +- src/js-host-api/examples/README.md | 2 +- src/js-host-api/examples/simple.js | 2 +- src/js-host-api/lib.js | 7 +- src/js-host-api/src/lib.rs | 27 ++-- src/js-host-api/tests/sandbox.test.js | 6 +- 15 files changed, 145 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9bbeb8..f46ac0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,7 +198,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -220,21 +220,22 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures 0.2.17", ] [[package]] @@ -258,11 +259,11 @@ dependencies = [ [[package]] name = "buddy_system_allocator" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" +checksum = "b672b945a3e4f4f40bfd4cd5ee07df9e796a42254ce7cd6d2599ad969244c44a" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -525,9 +526,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" @@ -678,7 +679,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", @@ -914,7 +915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -971,11 +972,11 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flatbuffers" -version = "25.9.23" +version = "25.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "rustc_version", ] @@ -1118,11 +1119,11 @@ dependencies = [ [[package]] name = "gdbstub" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72742d2b395902caf8a5d520d0dd3334ba6d1138938429200e58d5174e275f3f" +checksum = "6bf845b08f7c2ef3b5ad19f80779d43ae20d278652b91bb80adda65baf2d8ed6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "log", "managed", @@ -1444,22 +1445,20 @@ dependencies = [ [[package]] name = "hyperlight-common" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98f80e9aba9fce2a899c9ff0c62dd39cd479fa2f22ef578faed1d510c5becf94" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "anyhow", "flatbuffers", "log", - "spin 0.10.0", - "thiserror 2.0.17", + "spin", + "thiserror 2.0.18", "tracing", ] [[package]] name = "hyperlight-guest" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7f3cfaa19d263d4110b6f1d371ad778d544f4f30369be8eca79092bc868840" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "anyhow", "flatbuffers", @@ -1472,8 +1471,7 @@ dependencies = [ [[package]] name = "hyperlight-guest-bin" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598a6f53bd1a356cff745af4b20e2e96ff9d4c956d13c52fd7981e91832291d8" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "buddy_system_allocator", "cc", @@ -1486,15 +1484,14 @@ dependencies = [ "hyperlight-guest-tracing", "linkme", "log", - "spin 0.10.0", + "spin", "tracing", ] [[package]] name = "hyperlight-guest-macro" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fec4e1154fe4368a5980101d532eede35c622532aaf83a03440d912d55217" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1505,11 +1502,10 @@ dependencies = [ [[package]] name = "hyperlight-guest-tracing" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0f8d1e992b03c778c6c144f94a81060384e823781c84fae6228cba230d449c" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "hyperlight-common", - "spin 0.10.0", + "spin", "tracing", "tracing-core", ] @@ -1517,11 +1513,10 @@ dependencies = [ [[package]] name = "hyperlight-host" version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f81d712e88785e8860be6b33048fc2c9ad6637864aa50fe95d0037cfa50a4fb" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=620339aa95d508e8cbd1d38b4374f09090aade7b#620339aa95d508e8cbd1d38b4374f09090aade7b" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.0", "blake3", "cfg-if", "cfg_aliases", @@ -1544,12 +1539,12 @@ dependencies = [ "mshv-ioctls", "opentelemetry", "page_size", - "rand 0.9.2", + "rand 0.10.0", "rust-embed", "serde_json", "sha256", "termcolor", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "tracing-core", "tracing-log", @@ -1627,7 +1622,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "spin 0.10.0", + "spin", "tempfile", "tracing", ] @@ -1644,7 +1639,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.61.2", ] [[package]] @@ -1908,7 +1903,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "333f77a20344a448f3f70664918135fddeb804e938f28a99d685bd92926e0b19" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -1958,7 +1953,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", ] @@ -2076,7 +2071,7 @@ dependencies = [ "metrics-util", "quanta", "rustls", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -2139,7 +2134,7 @@ checksum = "e52a2a02c4107e08f46ba9dfc4e0f4461dffd44fbeca3e5631b4a047d15376c9" dependencies = [ "libc", "mshv-bindings", - "thiserror 2.0.17", + "thiserror 2.0.18", "vmm-sys-util", ] @@ -2149,7 +2144,7 @@ version = "3.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6944d0bf100571cd6e1a98a316cdca262deb6fccf8d93f5ae1502ca3fc88bd3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "ctor", "futures", "napi-build", @@ -2318,7 +2313,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] @@ -2348,7 +2343,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tonic", ] @@ -2384,7 +2379,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "rand 0.9.2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", ] @@ -2434,7 +2429,7 @@ dependencies = [ "serde_json", "simd-json", "simdutf8", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", "windows 0.62.2", @@ -2789,7 +2784,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -2818,7 +2813,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -2829,7 +2824,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2989,9 +2984,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -3044,11 +3039,11 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3166,7 +3161,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation", "core-foundation-sys", "libc", @@ -3190,7 +3185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3395,15 +3390,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spin" version = "0.10.0" @@ -3478,7 +3464,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3501,11 +3487,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -3521,9 +3507,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -3711,7 +3697,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", "http", @@ -3778,7 +3764,7 @@ dependencies = [ "chrono", "serde", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "tracing-subscriber", @@ -4103,7 +4089,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.15.5", "indexmap", "semver", @@ -4163,7 +4149,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4615,7 +4601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.0", "indexmap", "log", "serde", diff --git a/Cargo.toml b/Cargo.toml index 9016bd9..55b0388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ repository = "https://github.com/hyperlight-dev/hyperlight-js" readme = "README.md" [workspace.dependencies] -hyperlight-common = { version = "0.12", default-features = false } -hyperlight-guest-bin = { version = "0.12" } -hyperlight-guest = { version = "0.12" } -hyperlight-host = { version = "0.12", default-features = false, features = ["executable_heap", "init-paging"] } +hyperlight-common = { git = "https://github.com/hyperlight-dev/hyperlight", rev = "620339aa95d508e8cbd1d38b4374f09090aade7b", default-features = false } +hyperlight-guest-bin = { git = "https://github.com/hyperlight-dev/hyperlight", rev = "620339aa95d508e8cbd1d38b4374f09090aade7b" } +hyperlight-guest = { git = "https://github.com/hyperlight-dev/hyperlight", rev = "620339aa95d508e8cbd1d38b4374f09090aade7b" } +hyperlight-host = { git = "https://github.com/hyperlight-dev/hyperlight", rev = "620339aa95d508e8cbd1d38b4374f09090aade7b", default-features = false, features = ["executable_heap", "init-paging"] } hyperlight-js = { version = "0.1.1", path = "src/hyperlight-js" } hyperlight-js-runtime = { version = "0.1.1", path = "src/hyperlight-js-runtime" } diff --git a/src/hyperlight-js/benches/benchmarks.rs b/src/hyperlight-js/benches/benchmarks.rs index 2e6d8b5..e76172f 100644 --- a/src/hyperlight-js/benches/benchmarks.rs +++ b/src/hyperlight-js/benches/benchmarks.rs @@ -182,7 +182,7 @@ fn handle_events_benchmark(c: &mut Criterion) { let start = Instant::now(); let _ = loaded_js_sandbox.handle_event("function1", event.to_string(), Some(gc)); - loaded_js_sandbox.restore(&snapshot).unwrap(); + loaded_js_sandbox.restore(snapshot.clone()).unwrap(); elapsed += start.elapsed(); } } diff --git a/src/hyperlight-js/examples/interrupt/main.rs b/src/hyperlight-js/examples/interrupt/main.rs index df52cc5..8d96e44 100644 --- a/src/hyperlight-js/examples/interrupt/main.rs +++ b/src/hyperlight-js/examples/interrupt/main.rs @@ -99,7 +99,7 @@ fn main() -> Result<()> { // Demonstrate recovery from poisoned state println!("\nšŸ“ø Restoring sandbox from snapshot..."); - loaded_sandbox.restore(&snapshot)?; + loaded_sandbox.restore(snapshot)?; println!("šŸ”’ Poisoned after restore: {}", loaded_sandbox.poisoned()); assert!( diff --git a/src/hyperlight-js/src/sandbox/js_sandbox.rs b/src/hyperlight-js/src/sandbox/js_sandbox.rs index 2e3ef14..abc9891 100644 --- a/src/hyperlight-js/src/sandbox/js_sandbox.rs +++ b/src/hyperlight-js/src/sandbox/js_sandbox.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::fmt::Debug; +use std::sync::Arc; use hyperlight_host::sandbox::snapshot::Snapshot; use hyperlight_host::{new_error, MultiUseSandbox, Result}; @@ -15,7 +16,7 @@ pub struct JSSandbox { handlers: HashMap, // Snapshot of state before any handlers are added. // This is used to restore state back to a neutral JSSandbox. - snapshot: Snapshot, + snapshot: Arc, // metric drop guard to manage sandbox metric _metric_guard: SandboxMetricsGuard, } @@ -33,8 +34,11 @@ impl JSSandbox { } /// Creates a new `JSSandbox` from a `MultiUseSandbox` and a `Snapshot` of state before any handlers were added. - pub(crate) fn from_loaded(mut loaded: MultiUseSandbox, snapshot: Snapshot) -> Result { - loaded.restore(&snapshot)?; + pub(crate) fn from_loaded( + mut loaded: MultiUseSandbox, + snapshot: Arc, + ) -> Result { + loaded.restore(snapshot.clone())?; Ok(Self { inner: loaded, handlers: HashMap::new(), diff --git a/src/hyperlight-js/src/sandbox/loaded_js_sandbox.rs b/src/hyperlight-js/src/sandbox/loaded_js_sandbox.rs index baed5d8..1574f08 100644 --- a/src/hyperlight-js/src/sandbox/loaded_js_sandbox.rs +++ b/src/hyperlight-js/src/sandbox/loaded_js_sandbox.rs @@ -21,7 +21,7 @@ pub struct LoadedJSSandbox { inner: MultiUseSandbox, // Snapshot of state before the sandbox was loaded and before any handlers were added. // This is used to restore state back to a JSSandbox. - snapshot: Snapshot, + snapshot: Arc, // metric drop guard to manage sandbox metric _metric_guard: SandboxMetricsGuard, } @@ -42,7 +42,7 @@ impl Drop for MonitorTask { impl LoadedJSSandbox { #[instrument(err(Debug), skip_all, level=Level::INFO)] - pub(super) fn new(inner: MultiUseSandbox, snapshot: Snapshot) -> Result { + pub(super) fn new(inner: MultiUseSandbox, snapshot: Arc) -> Result { metrics::counter!(METRIC_SANDBOX_LOADS).increment(1); Ok(LoadedJSSandbox { inner, @@ -92,13 +92,13 @@ impl LoadedJSSandbox { /// Take a snapshot of the the current state of the sandbox. /// This can be used to restore the state of the sandbox later. #[instrument(err(Debug), skip_all, level=Level::DEBUG)] - pub fn snapshot(&mut self) -> Result { + pub fn snapshot(&mut self) -> Result> { self.inner.snapshot() } /// Restore the state of the sandbox to a previous snapshot. #[instrument(err(Debug), skip_all, level=Level::DEBUG)] - pub fn restore(&mut self, snapshot: &Snapshot) -> Result<()> { + pub fn restore(&mut self, snapshot: Arc) -> Result<()> { self.inner.restore(snapshot)?; Ok(()) } @@ -402,7 +402,7 @@ mod tests { assert_eq!(response_json["count"], 3); // Restore the snapshot - loaded_js_sandbox.restore(&snapshot).unwrap(); + loaded_js_sandbox.restore(snapshot.clone()).unwrap(); // Handle the event again, should reset to initial state let result = loaded_js_sandbox @@ -432,7 +432,7 @@ mod tests { .unwrap_err(); // restore to snapshot before unload/reload - reloaded_js_sandbox.restore(&snapshot).unwrap(); + reloaded_js_sandbox.restore(snapshot.clone()).unwrap(); // handler should be available again let result = reloaded_js_sandbox .handle_event("handler", get_static_counter_event(), gc) diff --git a/src/hyperlight-js/src/sandbox/sandbox_builder.rs b/src/hyperlight-js/src/sandbox/sandbox_builder.rs index 8cea862..3f96e40 100644 --- a/src/hyperlight-js/src/sandbox/sandbox_builder.rs +++ b/src/hyperlight-js/src/sandbox/sandbox_builder.rs @@ -1,8 +1,8 @@ #[cfg(target_os = "linux")] use std::time::Duration; -use hyperlight_host::sandbox::{is_hypervisor_present, SandboxConfiguration}; -use hyperlight_host::{GuestBinary, HyperlightError, Result}; +use hyperlight_host::sandbox::SandboxConfiguration; +use hyperlight_host::{is_hypervisor_present, GuestBinary, HyperlightError, Result}; use super::proto_js_sandbox::ProtoJSSandbox; use crate::HostPrintFn; @@ -13,16 +13,35 @@ pub struct SandboxBuilder { host_print_fn: Option, } -const MIN_STACK_SIZE: u64 = 256 * 1024; -// The minimum heap size is 4096KB. +/// The minimum scratch size for the JS runtime sandbox. +/// +/// The scratch region provides writable physical memory for: +/// - I/O buffers (input + output data) +/// - Page table copies (proportional to snapshot size — our ~13 MB guest +/// binary + heap produce ~72 KiB of page tables) +/// - Dynamically allocated pages (GDT/IDT, stack growth, Copy-on-Write +/// resolution during QuickJS initialisation) +/// - Exception stack and metadata (2 pages at the top) +/// +/// Hyperlight's default scratch (288 KiB) is far too small for the JS +/// runtime guest: after fixed overheads there are only ~44 free pages, +/// which are exhausted during init. 1 MiB (0x10_0000) matches +/// hyperlight's own "large guest" test configuration and gives +/// comfortable headroom. +const MIN_SCRATCH_SIZE: usize = 0x10_0000; // 1 MiB + +/// The minimum heap size is 4 MiB. The QuickJS engine needs a +/// reasonable amount of heap during initialisation for builtins, +/// global objects, and the bytecode compiler. This lives in the +/// identity-mapped snapshot region (NOT scratch). const MIN_HEAP_SIZE: u64 = 4096 * 1024; impl SandboxBuilder { /// Create a new SandboxBuilder pub fn new() -> Self { let mut config = SandboxConfiguration::default(); - config.set_stack_size(MIN_STACK_SIZE); config.set_heap_size(MIN_HEAP_SIZE); + config.set_scratch_size(MIN_SCRATCH_SIZE); Self { config, @@ -52,13 +71,14 @@ impl SandboxBuilder { self } - /// Set the guest stack size - /// This is the size of the stack that code executing in the guest can use. - /// If this value is too small then the guest will fail with a stack overflow error - /// The default value (and minimum) is set to the value of the MIN_STACK_SIZE const. - pub fn with_guest_stack_size(mut self, guest_stack_size: u64) -> Self { - if guest_stack_size > MIN_STACK_SIZE { - self.config.set_stack_size(guest_stack_size); + /// Set the guest scratch size in bytes. + /// The scratch region provides writable memory for the guest, including the + /// dynamically-sized stack. Increase this if your guest code needs deep + /// recursion or large local variables. + /// Values smaller than the default (288KiB) are ignored. + pub fn with_guest_scratch_size(mut self, guest_scratch_size: usize) -> Self { + if guest_scratch_size > MIN_SCRATCH_SIZE { + self.config.set_scratch_size(guest_scratch_size); } self } diff --git a/src/hyperlight-js/tests/monitors.rs b/src/hyperlight-js/tests/monitors.rs index 9aec292..f2c37f3 100644 --- a/src/hyperlight-js/tests/monitors.rs +++ b/src/hyperlight-js/tests/monitors.rs @@ -97,7 +97,7 @@ fn wall_clock_monitor_sandbox_recovers_with_restore() { assert!(loaded.poisoned(), "Should be poisoned after kill"); // Restore from snapshot - loaded.restore(&snapshot).unwrap(); + loaded.restore(snapshot.clone()).unwrap(); assert!(!loaded.poisoned(), "Should not be poisoned after restore"); // Should be able to run again @@ -162,7 +162,7 @@ fn cpu_time_monitor_sandbox_recovers_with_restore() { assert!(loaded.poisoned(), "Should be poisoned after kill"); // Restore from snapshot - loaded.restore(&snapshot).unwrap(); + loaded.restore(snapshot.clone()).unwrap(); assert!(!loaded.poisoned(), "Should not be poisoned after restore"); // Should be able to run again @@ -237,7 +237,7 @@ fn tuple_monitor_sandbox_recovers_with_restore() { assert!(loaded.poisoned(), "Should be poisoned after kill"); // Restore and verify recovery - loaded.restore(&snapshot).unwrap(); + loaded.restore(snapshot.clone()).unwrap(); assert!(!loaded.poisoned(), "Should not be poisoned after restore"); let monitor2 = ( diff --git a/src/hyperlight-js/tests/termination.rs b/src/hyperlight-js/tests/termination.rs index f545147..c24632f 100644 --- a/src/hyperlight-js/tests/termination.rs +++ b/src/hyperlight-js/tests/termination.rs @@ -92,7 +92,7 @@ fn handle_termination() -> Result<()> { ); // Restore the sandbox from snapshot - loaded_sandbox.restore(&snapshot)?; + loaded_sandbox.restore(snapshot)?; // Verify sandbox is no longer poisoned after restore assert!( diff --git a/src/js-host-api/README.md b/src/js-host-api/README.md index eb07824..7a96099 100644 --- a/src/js-host-api/README.md +++ b/src/js-host-api/README.md @@ -50,7 +50,7 @@ Creates and configures a new sandbox. **Methods:** - `setHeapSize(bytes: number)` → `this` — Set guest heap size (must be > 0, chainable) -- `setStackSize(bytes: number)` → `this` — Set guest stack size (must be > 0, chainable) +- `setScratchSize(bytes: number)` → `this` — Set guest scratch size, includes stack (must be > 0, chainable) - `setInputBufferSize(bytes: number)` → `this` — Set guest input buffer size (must be > 0, chainable) - `setOutputBufferSize(bytes: number)` → `this` — Set guest output buffer size (must be > 0, chainable) - `build()` → `Promise` — Builds a proto sandbox ready to load the JavaScript runtime @@ -58,7 +58,7 @@ Creates and configures a new sandbox. ```javascript const builder = new SandboxBuilder() .setHeapSize(8 * 1024 * 1024) - .setStackSize(512 * 1024); + .setScratchSize(1024 * 1024); const protoSandbox = await builder.build(); ``` @@ -237,9 +237,8 @@ All errors thrown by the API include a `code` property for programmatic handling |------|---------| | `ERR_INVALID_ARG` | Bad argument (empty handler name, zero timeout, etc.) | | `ERR_CONSUMED` | Object already consumed (e.g., calling `loadRuntime()` twice) | -| `ERR_POISONED` | Sandbox is in an inconsistent state (after timeout kill, guest abort, etc.) — restore from snapshot or unload | +| `ERR_POISONED` | Sandbox is in an inconsistent state (after timeout kill, guest abort, stack overflow, etc.) — restore from snapshot or unload | | `ERR_CANCELLED` | Execution was cancelled (by monitor timeout or manual `kill()`) | -| `ERR_STACK_OVERFLOW` | Guest code caused a stack overflow | | `ERR_GUEST_ABORT` | Guest code aborted | | `ERR_INTERNAL` | Unexpected internal error | @@ -251,8 +250,8 @@ try { case 'ERR_CANCELLED': console.log('Execution was cancelled'); break; - case 'ERR_STACK_OVERFLOW': - console.log('Stack overflow in guest code'); + case 'ERR_POISONED': + console.log('Sandbox is poisoned (e.g. stack overflow, timeout)'); break; default: console.log(`Unexpected error [${error.code}]: ${error.message}`); diff --git a/src/js-host-api/examples/README.md b/src/js-host-api/examples/README.md index 8abd610..b97c6c2 100644 --- a/src/js-host-api/examples/README.md +++ b/src/js-host-api/examples/README.md @@ -126,7 +126,7 @@ const { SandboxBuilder } = require('../lib.js'); async function main() { const builder = new SandboxBuilder(); builder.setHeapSize(8 * 1024 * 1024); // Set heap size - builder.setStackSize(512 * 1024); // Set stack size + builder.setScratchSize(1024 * 1024); // Set scratch size (includes stack) const protoSandbox = await builder.build(); // Build sandbox } main(); diff --git a/src/js-host-api/examples/simple.js b/src/js-host-api/examples/simple.js index 61a812d..4ee80f6 100644 --- a/src/js-host-api/examples/simple.js +++ b/src/js-host-api/examples/simple.js @@ -9,7 +9,7 @@ async function main() { console.log('1. Creating sandbox builder...'); const builder = new SandboxBuilder(); builder.setHeapSize(8 * 1024 * 1024); // 8MB heap - builder.setStackSize(512 * 1024); // 512KB stack + builder.setScratchSize(1024 * 1024); // 1MB scratch (includes stack) console.log(' āœ“ Builder configured\n'); // Step 2: Build the proto sandbox (async — returns a Promise) diff --git a/src/js-host-api/lib.js b/src/js-host-api/lib.js index 84d2336..eb1dff2 100644 --- a/src/js-host-api/lib.js +++ b/src/js-host-api/lib.js @@ -153,7 +153,12 @@ ProtoJSSandbox.prototype.loadRuntime = wrapAsync(ProtoJSSandbox.prototype.loadRu // SandboxBuilder — async build + sync setters SandboxBuilder.prototype.build = wrapAsync(SandboxBuilder.prototype.build); -for (const method of ['setHeapSize', 'setStackSize', 'setInputBufferSize', 'setOutputBufferSize']) { +for (const method of [ + 'setHeapSize', + 'setScratchSize', + 'setInputBufferSize', + 'setOutputBufferSize', +]) { const orig = SandboxBuilder.prototype[method]; if (!orig) throw new Error(`Cannot wrap missing method: SandboxBuilder.${method}`); SandboxBuilder.prototype[method] = wrapSync(orig); diff --git a/src/js-host-api/src/lib.rs b/src/js-host-api/src/lib.rs index 977e567..a0481bc 100644 --- a/src/js-host-api/src/lib.rs +++ b/src/js-host-api/src/lib.rs @@ -80,8 +80,6 @@ enum ErrorCode { Poisoned, /// Execution was cancelled by the host (monitor timeout or manual `kill()`). Cancelled, - /// Guest stack overflow — increase stack size or reduce recursion depth. - StackOverflow, /// Guest abort (trap, panic, or fatal error in guest code). GuestAbort, /// Invalid arguments (bad types, empty names, zero sizes). @@ -98,7 +96,6 @@ impl ErrorCode { match self { Self::Poisoned => "ERR_POISONED", Self::Cancelled => "ERR_CANCELLED", - Self::StackOverflow => "ERR_STACK_OVERFLOW", Self::GuestAbort => "ERR_GUEST_ABORT", Self::InvalidArg => "ERR_INVALID_ARG", Self::Consumed => "ERR_CONSUMED", @@ -144,7 +141,6 @@ fn to_napi_error(err: HyperlightError) -> napi::Error { HyperlightError::PoisonedSandbox => ErrorCode::Poisoned, HyperlightError::ExecutionCanceledByHost() => ErrorCode::Cancelled, HyperlightError::JsonConversionFailure(_) => ErrorCode::InvalidArg, - HyperlightError::StackOverflow() => ErrorCode::StackOverflow, HyperlightError::GuestAborted(_, _) => ErrorCode::GuestAbort, _ => ErrorCode::Internal, }; @@ -211,7 +207,7 @@ pub struct SnapshotWrapper { /// ```js /// const proto = await new SandboxBuilder() /// .setHeapSize(8 * 1024 * 1024) -/// .setStackSize(512 * 1024) +/// .setScratchSize(1024 * 1024) /// .build(); /// ``` #[napi(js_name = "SandboxBuilder")] @@ -296,20 +292,21 @@ impl SandboxBuilderWrapper { self.with_inner(|b| b.with_guest_input_buffer_size(size as usize)) } - /// Set the guest stack size in bytes. + /// Set the guest scratch size in bytes. /// - /// Controls how much stack space is available for guest code execution. - /// Deep recursion or large local variables need a bigger stack. + /// Controls how much scratch space (which includes the stack) is available + /// for guest code execution. Deep recursion or large local variables need + /// a bigger scratch region. /// - /// @param size - Stack size in bytes (must be > 0) + /// @param size - Scratch size in bytes (must be > 0) /// @returns this (for chaining) /// @throws If size is 0 #[napi] - pub fn set_stack_size(&self, size: u32) -> napi::Result<&Self> { + pub fn set_scratch_size(&self, size: u32) -> napi::Result<&Self> { if size == 0 { - return Err(invalid_arg_error("Stack size must be greater than 0")); + return Err(invalid_arg_error("Scratch size must be greater than 0")); } - self.with_inner(|b| b.with_guest_stack_size(size as u64)) + self.with_inner(|b| b.with_guest_scratch_size(size as usize)) } /// Set the guest heap size in bytes. @@ -819,9 +816,7 @@ impl LoadedJSSandboxWrapper { }) .await .map_err(join_error)??; - Ok(SnapshotWrapper { - inner: Arc::new(snapshot), - }) + Ok(SnapshotWrapper { inner: snapshot }) } /// Restore the sandbox to a previously captured snapshot state. @@ -843,7 +838,7 @@ impl LoadedJSSandboxWrapper { let sandbox = guard .as_mut() .ok_or_else(|| consumed_error("LoadedJSSandbox"))?; - let result = sandbox.restore(&snap).map_err(to_napi_error); + let result = sandbox.restore(snap).map_err(to_napi_error); poisoned_flag.store(sandbox.poisoned(), Ordering::Release); result }) diff --git a/src/js-host-api/tests/sandbox.test.js b/src/js-host-api/tests/sandbox.test.js index 6cde135..1505224 100644 --- a/src/js-host-api/tests/sandbox.test.js +++ b/src/js-host-api/tests/sandbox.test.js @@ -22,7 +22,7 @@ describe('SandboxBuilder', () => { const builder = new SandboxBuilder(); const result = builder .setHeapSize(8 * 1024 * 1024) - .setStackSize(512 * 1024) + .setScratchSize(1024 * 1024) .setInputBufferSize(4096) .setOutputBufferSize(4096); expect(result).toBe(builder); @@ -53,9 +53,9 @@ describe('SandboxBuilder', () => { expectThrowsWithCode(() => builder.setHeapSize(0), 'ERR_INVALID_ARG'); }); - it('should reject zero stack size', () => { + it('should reject zero scratch size', () => { const builder = new SandboxBuilder(); - expectThrowsWithCode(() => builder.setStackSize(0), 'ERR_INVALID_ARG'); + expectThrowsWithCode(() => builder.setScratchSize(0), 'ERR_INVALID_ARG'); }); it('should reject zero input buffer size', () => {