From 5acb89b885552787e48815091e843dfa191c35d4 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:50:02 -0700 Subject: [PATCH 1/2] Use talc as the guest heap allocator buddy_system_allocator writes a free-list pointer at the start of every power-of-two heap block during guest init. talc writes only the start and end of the heap. The host populates snapshot memory on first guest touch, so fewer written pages make sandbox creation faster. Criterion sandboxes/create_initialized: * MSHV: 25% to 37% faster for small to large heaps * KVM: 9% to 11% faster for small and large heaps Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --- CHANGELOG.md | 2 ++ Cargo.lock | 39 ++++++++++++++------------- src/hyperlight_guest_bin/Cargo.toml | 2 +- src/hyperlight_guest_bin/src/lib.rs | 17 +++++++----- src/tests/rust_guests/Cargo.lock | 41 +++++++++++++++-------------- 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa70855d70..c095f6de15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). * A new sandbox writes only the guest code, PEB and init data into its memory. The host populates other pages on first guest touch, which speeds up sandbox creation. +* Use `talc` as the guest heap allocator. Guest init writes only the start and + end of the heap, which speeds up sandbox creation. * **Breaking:** The transport arena sits one unmapped guard page below the reserved scratch-top pages, at an address independent of the scratch size, and the page tables at the scratch base. The guest's page allocator stops at the arena GPA the host publishes in the scratch-top metadata, so guests must be rebuilt. `hyperlight_common::layout::scratch_allocator_limit_gpa` is renamed `scratch_reserved_top_gpa`, the start of the reserved pages. An allocator that stops there runs into the arena. The minimum scratch size grows by that guard page. Snapshot ABI 7: existing snapshots must be regenerated. diff --git a/Cargo.lock b/Cargo.lock index f53387d634..474e4096f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,12 @@ dependencies = [ "cc", ] +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "android_system_properties" version = "0.1.6" @@ -305,15 +311,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.1", -] - [[package]] name = "built" version = "0.8.1" @@ -1708,7 +1705,7 @@ dependencies = [ "quickcheck", "rand 0.10.3", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing", "tracing-core", @@ -1770,7 +1767,6 @@ dependencies = [ name = "hyperlight-guest-bin" version = "0.18.0" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -1779,7 +1775,8 @@ dependencies = [ "hyperlight-libc", "linkme", "log", - "spin 0.12.3", + "spin", + "talc", "tracing", ] @@ -1798,7 +1795,7 @@ name = "hyperlight-guest-tracing" version = "0.18.0" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] @@ -3767,12 +3764,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" - [[package]] name = "spin" version = "0.12.3" @@ -3876,6 +3867,16 @@ dependencies = [ "version-compare", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2", + "lock_api", +] + [[package]] name = "target-lexicon" version = "0.13.5" diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index 6c6f86efe1..0b9fe21966 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -25,7 +25,7 @@ hyperlight-common = { workspace = true, default-features = false } hyperlight-guest-tracing = { workspace = true, default-features = false } hyperlight-guest-macro = { workspace = true, default-features = false, optional = true } hyperlight-libc = { workspace = true, default-features = false, optional = true } -buddy_system_allocator = "0.13.0" +talc = { version = "5.1.1", default-features = false } log = { version = "0.4", default-features = false } linkme = { version = "0.3.36", optional = true } spin = "0.12.0" diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 7df64cefc6..3cf85417ea 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -8,7 +8,6 @@ extern crate alloc; use core::fmt::Write; use arch::dispatch::dispatch_function; -use buddy_system_allocator::LockedHeap; use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; @@ -18,6 +17,8 @@ use hyperlight_common::mem::HyperlightPEB; use hyperlight_common::outb::OutBAction; use hyperlight_guest::exit::write_abort; use hyperlight_guest::guest_handle::handle::GuestHandle; +use talc::TalcLock; +use talc::source::Manual; // === Modules === #[cfg_attr(target_arch = "x86_64", path = "arch/amd64/mod.rs")] @@ -50,10 +51,12 @@ mod init; pub use hyperlight_libc as libc; // Globals +type GuestHeap = TalcLock, Manual>; + #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] -struct ProfiledLockedHeap(LockedHeap); +struct ProfiledLockedHeap(GuestHeap); #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] -unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap { +unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { let addr = unsafe { self.0.alloc(layout) }; unsafe { @@ -107,11 +110,10 @@ unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap // === Globals === #[cfg(not(all(feature = "mem_profile", target_arch = "x86_64")))] #[global_allocator] -pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty(); +pub(crate) static HEAP_ALLOCATOR: GuestHeap = GuestHeap::new(Manual); #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] #[global_allocator] -pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> = - ProfiledLockedHeap(LockedHeap::<32>::empty()); +pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap = ProfiledLockedHeap(GuestHeap::new(Manual)); pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new(); pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister = @@ -271,7 +273,8 @@ pub(crate) extern "C" fn generic_init( heap_allocator .try_lock() .expect("Failed to access HEAP_ALLOCATOR") - .init(heap_start, heap_size); + .claim(heap_start as *mut u8, heap_size) + .expect("Failed to claim guest heap"); peb_ptr }; diff --git a/src/tests/rust_guests/Cargo.lock b/src/tests/rust_guests/Cargo.lock index 14c0698272..4d2223cb74 100644 --- a/src/tests/rust_guests/Cargo.lock +++ b/src/tests/rust_guests/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "anstream" version = "1.0.0" @@ -79,15 +85,6 @@ version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.1", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -245,7 +242,7 @@ dependencies = [ "flatbuffers", "log", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing-core", ] @@ -296,7 +293,6 @@ dependencies = [ name = "hyperlight-guest-bin" version = "0.18.0" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -304,7 +300,8 @@ dependencies = [ "hyperlight-guest-tracing", "linkme", "log", - "spin 0.12.3", + "spin", + "talc", "tracing", ] @@ -323,7 +320,7 @@ name = "hyperlight-guest-tracing" version = "0.18.0" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] @@ -630,12 +627,6 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" -[[package]] -name = "spin" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" - [[package]] name = "spin" version = "0.12.3" @@ -667,6 +658,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2", + "lock_api", +] + [[package]] name = "thiserror" version = "2.0.20" @@ -915,7 +916,7 @@ dependencies = [ "hyperlight-component-macro", "hyperlight-guest", "hyperlight-guest-bin", - "spin 0.12.3", + "spin", ] [[package]] From a8850fcb3f12f4b51c65ccc1a0dfbfdaafdd2dd0 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:50:57 -0700 Subject: [PATCH 2/2] PR feedback Add a SAFETY comment to the guest heap claim. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --- src/hyperlight_guest_bin/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 3cf85417ea..c253fac20b 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -270,6 +270,8 @@ pub(crate) extern "C" fn generic_init( let heap_allocator = &HEAP_ALLOCATOR; #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] let heap_allocator = &HEAP_ALLOCATOR.0; + // SAFETY: the host reserves the PEB heap range for the guest heap, and + // init claims it once. Only this allocator writes to it. heap_allocator .try_lock() .expect("Failed to access HEAP_ALLOCATOR")