Skip to content

component: track and limit ResourceTable host heap usage - #15101

Open
posborne wants to merge 2 commits into
bytecodealliance:mainfrom
posborne:resource-table-host-heap-limiter
Open

posborne wants to merge 2 commits into
bytecodealliance:mainfrom
posborne:resource-table-host-heap-limiter

Conversation

@posborne

Copy link
Copy Markdown
Collaborator

Motivation

Embedders currently lack an aggregate host-side memory boundary for component model resources in ResourceTable. While #12652 added mitigations around handle counts and hostcall fuel, cumulative host heap allocations owned by resources in ResourceTable were previously untracked by any umbrella mechanism.

Over the past several releases, Wasmtime has had to address a recurring class of guest-driven host memory exhaustion advisories with mitigations including GHSA-852m-cvvp-9p4w and GHSA-243v-98vx-264h (#12652), GHSA-c9gc-w9vx-w86p, GHSA-gqmc-89g8-p25r, and GHSA-6fr9-xmw9-5352. While per-resource and per-call limits help, they do not bound the aggregate host heap memory accumulated across entries in a ResourceTable.

Solution

This PR adds host heap usage accounting and optional per-table byte limits to wasmtime::component::ResourceTable. This provides a level of umbrella coverage to protect against host paths that may not have protections currently and to provide a higher level limit that is easier to reason about for embedders.

The way that this is achieved involves the introduction of a HostHeapUsage trait which is required for all resources. This is accompanied by a FixedHostHeapUsage trait as a marker for types which do not contain children with heap storage not accounted for in their own size directly. Methods on the table that push, delete or mutate (update) resources use the traits to account for the heap usage changes associated with the operation. If a limit is set and an operation causes the heap usage to grow beyond the set limit, the operations return an error which will typically result in a guest-side trap.

These new requirements necessitate updates across the wasmtime tree and will be a breaking change for embedders that are pushing their own resources into ResourceTables.

Design Decisions

The code here has gone through a few iterations and updates based on discussion in #12984. What is present here tries to incorporate that feedback and is closer to some of the implementation from servo/firefox/stylo MallocSizeOf trait.

In instrumenting the code to implement the traits, I decided that to ease the burden on embedders and to provide some additional static guarantees, it would be beneficial to introduce proc-macros in order to aid in deriving the traits on compatible structs and enums.

For mutation I went with a closure-based update rather than a guard, because the limit check has to be fallible and a guard could only account in Drop. This makes former get_mut call sites a bit noisier. Since update can only measure after the mutation runs, growth past the limit is reported rather than prevented, and the mutation isn't rolled back because the error is expected to trap. The overshoot is roughly limited to what a single host call can allocate.

Across the code in tree (mostly wasi p2/p3), I tried to provide pragmatic implementations of the trait where reasonable and erred on the side of overcounting bytes in most cases. There are some reference counted cases which are not tracked with complete accuracy but, even in those cases, abuse should be challenging as the fixed size of the resource will be counted even if the amortized cost of the shared data is left unaccounted.

Future Work

The changes here are sprawling, so I wanted to avoid increasing the scope beyond what was required for purpose and reasonable ergonomics (adding derive). Potential future work that I opted to not try to incorporate here include:

  • Allow for the limit to be set at a higher level outside of individual tables, probably on the ResourceLimiter.
  • Hooks to intervene when limits are exceeded or at watermarks prior to the limit.
  • Fast path or conditional compilation to opt out of tracking; even if no limit is set the accounting is done and count is available.

Closes #12984

… macros

Embedders currently lack a mechanism to track host-side heap memory
occupied by component model resources (bytecodealliance#12984).

Following the prior art of Servo's `heapsize` and Firefox's `malloc_size_of`,
this commit introduces two foundational traits:
- `HostHeapUsage`: reports dynamically allocated heap bytes owned by descendants
  (`heap_size_of_children`).
- `FixedHostHeapUsage: HostHeapUsage + Sized`: marker subtrait for types whose
  heap footprint is constant under mutation (scalars, fixed structures, OS
  handles). A blanket impl automatically implements `HostHeapUsage` returning 0.

Two procedural derive macros are provided in `wasmtime-component-macro` and
re-exported from `wasmtime::component`:
- `#[derive(HostHeapUsage)]`: recursively sums `heap_size_of_children` across all
  fields.
- `#[derive(FixedHostHeapUsage)]`: asserts at compile time that all fields are
  fixed-size (via `FixedHostHeapField`, including `Option`, `Result`, arrays,
  and tuples of fixed-size types), preventing accidental additions of unmetered
  buffers.

The proc macros are added in order to provide a safer and more
convenient route for embedders and in-tree implementations to target the
required traits.  Notably, the ability to derive the traits is only
possible if members of the struct/enum already implement the required
trait.  Attempting to, for instance, derive the HostHeapUsage on struct
containing a type which we don't know how to track (e.g. An Arc<T>) will
generate a compile-time failure hinting that the embedder must implement
the trait manually with more careful consideration.
Embedders currently lack an aggregate host-side memory boundary for
component model resources (bytecodealliance#12984). Existing mitigations from PR bytecodealliance#12652
(entry counts and hostcall fuel) bound handle quantities and transfer sizes,
but do not bound cumulative host heap allocation across resources.

This commit integrates the `HostHeapUsage` trait into `ResourceTable`.
The `FixedHostHeapUsage` denotes that a type has fixed size (or is
untracked) and cannot be mutated internally in a way that would change
its metered host heap usage.  With that, these types may be accessed via
`get_mut` in the style prevelant already.

Types only implementing `HostHeapUsage` that aren't fixed must have
their mutations be made fallible, so those mutations go through `update`
instead which provides mutable access within an enclosure, potentially
raising an error if the mutations result in exceeding the heap limit in
place.

All required callsites in tree have been updated to use these new,
required traits.  There are limits and in some cases there are limited
untracked heap allocations that are considered to be safe or which are
amortized in a way that would make accounting using this mechanism not
possible.
@posborne
posborne requested review from a team as code owners October 30, 2026 22:19
@posborne
posborne requested review from alexcrichton and rvolosatovs and removed request for a team October 30, 2026 22:19
@github-actions github-actions Bot added wasi Issues pertaining to WASI wasmtime:c-api Issues pertaining to the C API. labels Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasi Issues pertaining to WASI wasmtime:c-api Issues pertaining to the C API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Limiting of Host Heap Usage when using ResourceTable

1 participant