Skip to content

Commit 60cdb0b

Browse files
author
Zachary Whitley
committed
feat(per-actor): PerActorTvmHost wrapper for per-store budget enforcement
Adds a per-store budget wrapper around `SharedTvmHost` so embedders can host *untrusted* actors on a shared substrate without one being able to exhaust the directory for the others. Designed and requested by the girder runtime — its E1 (host-side enforcement for untrusted tvm actors) was previously blocked on a tvm-wasm hook; this commit is that hook. - `PerActorTvmHost { inner: SharedTvmHost, budget, state }`: per-store outstanding-bytes accounting + overrun flag. `inner` is a regular `SharedTvmHost` clone — two `PerActorTvmHost` instances built from `shared.clone()` see the same regions but track budgets independently. - `TvmBudget::{unlimited, outstanding_bytes(u64)}`: 0 = unlimited (the fast path delegates with no accounting overhead — drop-in replacement for `SharedTvmHost` at zero perf cost when unused). - All four host traits (`types::Host`, `manager::Host`, `bytes::Host`, `diagnostics::Host`) implemented: * `alloc`: budget-check → delegate → record (handle→size in a private HashMap keyed by `(region_id, generation, offset)`). * `dealloc`: delegate → release the recorded size. * Everything else: pure delegation to the inner `SharedTvmHost`. - Overrun surfaces as `TvmError::AllocationFailed` (existing variant) + a host-side `budget_overrun()` flag the embedder polls. No WIT change; guests see identical behaviour to substrate-wide exhaustion. The embedder reclassifies fatal-vs-graceful: substrate-wide exhaustion is still graceful (a restart can't free shared bytes); per-actor overrun is fatal-and-restartable (the new incarnation releases this actor's regions, so a restart actually remediates). - `add_per_actor_to_linker<T: AsMut<PerActorTvmHost>>` mirrors `add_shared_to_linker` for the per-store wrapper. - 5 unit tests in `per_actor::tests` cover: unlimited fast-path, budget-deny + overrun flag, dealloc-releases, reset_overrun, and the headline two-actors-share-substrate-independent-budgets case. Pre-existing clippy warnings in `tvm-core` (introduced by recent Rust 1.96 lints) are *not* addressed here — out of scope, untouched files. Existing tvm-wasm test suite stays fully green (87 tests across 20 binaries).
1 parent fb33d3b commit 60cdb0b

3 files changed

Lines changed: 429 additions & 1 deletion

File tree

crates/tvm-wasmtime/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub mod imported;
4747
pub mod linker;
4848
pub mod macros;
4949
pub mod memory_factory;
50+
pub mod per_actor;
5051
pub mod prelude;
5152
pub mod raw_linker;
5253
pub mod region_view;
@@ -61,9 +62,11 @@ pub use imported::{
6162
build_imported_setup, build_imported_setup_with_data, create_imported_in_store, ImportedRegion,
6263
};
6364
pub use linker::{
64-
add_concurrent_to_linker, add_shared_to_linker, add_to_linker, add_to_linker_with,
65+
add_concurrent_to_linker, add_per_actor_to_linker, add_shared_to_linker, add_to_linker,
66+
add_to_linker_with,
6567
};
6668
pub use memory_factory::{RuntimeMemoryRegion, WasmtimeMemoryRegion, WASM_PAGE_SIZE};
69+
pub use per_actor::{PerActorTvmHost, TvmBudget};
6770
pub use raw_linker::{
6871
add_raw_imports, add_raw_imports_with_memory_name, add_raw_shared,
6972
add_raw_shared_with_memory_name,

crates/tvm-wasmtime/src/linker.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::bindings::tvm::memory::{bytes, diagnostics, manager, types};
44
use crate::bindings::TvmGuest;
55
use crate::concurrent_host::ConcurrentTvmHost;
66
use crate::host::TvmHost;
7+
use crate::per_actor::PerActorTvmHost;
78
use crate::shared_host::SharedTvmHost;
89

910
/// Convenience wrapper for stores whose data implements `AsMut<TvmHost>`.
@@ -42,6 +43,21 @@ pub fn add_concurrent_to_linker<T: AsMut<ConcurrentTvmHost> + Send + 'static>(
4243
TvmGuest::add_to_linker::<T, HasSelf<ConcurrentTvmHost>>(linker, project::<T>)
4344
}
4445

46+
/// Convenience wrapper for stores whose data implements
47+
/// `AsMut<PerActorTvmHost>`. Each store gets its own outstanding-bytes
48+
/// accounting + overrun flag, while the *inner* `SharedTvmHost` they
49+
/// each wrap stays a shared substrate — the right shape for embedders
50+
/// that host *untrusted* actors on a shared directory and want one to
51+
/// be unable to exhaust the substrate for the others.
52+
pub fn add_per_actor_to_linker<T: AsMut<PerActorTvmHost> + Send + 'static>(
53+
linker: &mut Linker<T>,
54+
) -> wasmtime::Result<()> {
55+
fn project<T: AsMut<PerActorTvmHost>>(state: &mut T) -> &mut PerActorTvmHost {
56+
state.as_mut()
57+
}
58+
TvmGuest::add_to_linker::<T, HasSelf<PerActorTvmHost>>(linker, project::<T>)
59+
}
60+
4561
/// Generic helper. Use when your store data isn't `AsMut<H>` directly — for
4662
/// instance when `T` is an application struct that owns the host alongside
4763
/// other state. Pass a `fn` pointer that projects from `&mut T` to `&mut H`.

0 commit comments

Comments
 (0)