Skip to content

Document implementation-defined resource limits in Canonical ABI#616

Open
ricochet wants to merge 1 commit intoWebAssembly:mainfrom
ricochet:resource-constraints
Open

Document implementation-defined resource limits in Canonical ABI#616
ricochet wants to merge 1 commit intoWebAssembly:mainfrom
ricochet:resource-constraints

Conversation

@ricochet
Copy link
Contributor

@ricochet ricochet commented Mar 3, 2026

Allow implementations to non-deterministically trap on cross-component
calls when resource limits are exceeded, covering handle table capacity
and value transfer size.

This aligns with how memory.grow works in Core WebAssembly.

See WebAssembly/WASI#890 for context.

Allow implementations to non-deterministically trap on cross-component
calls when resource limits are exceeded, covering handle table capacity
and value transfer size.

This aligns with how memory.grow works in Core WebAssembly.

See WebAssembly/WASI#890 for context.
@ricochet ricochet requested a review from fitzgen March 3, 2026 14:58
this issue, allowing guest wasm code to handle failure by eagerly returning
some value of the declared return type to indicate failure.

### Implementation-Defined Limits
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure if we should place this here or in the Explainer.md

Comment on lines +120 to +125
* An implementation may trap when the total size of values being transferred
across a component boundary (via lifting or lowering) exceeds an
implementation-defined limit. For example, lifting a `list<u8>` of length N
from guest linear memory requires an intermediate allocation of at least N
bytes on the host, and implementations may refuse to perform such an
allocation when N is very large.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why we need to allow traps with the current ABI.

If/when we have lazy lowering, can this trap condition be removed?
And maybe even go the opposite way: with lazy lowering, the callee should never trap if it can not process it all at once.

There is no way for a guest to find out what the host considers "very large". Microcontrollers might already find 1MB very large. We shouldn't end up in a situation where guests need to play a game of "guess the host's limit" and be penalized with a trap in case they guess wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Even with our current eager ABI, because hosts have host powers, it seems like, as long as the WIT function signature allows some sort of graceful failure return value, a host should ideally return a failure value on too-large-parameter instead of trapping. (Adding lazy lowering will just add the equivalent expressive power to wasm-implemented functions.)

Now I can imagine that implementing graceful failure would take time and effort and so trapping might be the right choice in the short term. However, because a host runtime is always allowed to unilaterally abort execution (think: host crash, timeout), and because trap = "abort execution" in component-land, trapping is just the host exercising its freedom and it's more of a quality of implementation issue.

I think what both these arguments mean is that we don't need this bullet.

Comment on lines +114 to +119
* An implementation may trap when the handles table has reached an
implementation-defined capacity that is at or below `Table.MAX_LENGTH`. This
allows implementations to bound the host-side memory used to track resources,
waitables and other table elements. This is analogous to how `memory.grow`
may non-deterministically return `-1` in Core WebAssembly when the
implementation cannot satisfy the allocation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is analogous to how memory.grow may non-deterministically return -1

One difference is that memory.grow gracefully returns control flow back to the guest. A trap doesn't.

Reaching a max table capacity is similar to OOM and many languages don't handle that. Even Rust doesn't support that on stable yet. Because of that, I think trapping is indeed a reasonable default for the C/M.

OTOH, there are also environments where the guest is fully prepared to handle resource exhaustion and would much rather prefer missing out on a single resource over trapping the entire process.
If trapping becomes the default, is there a way for a guest to opt-out of this via e.g. a canonopt? Turning every resource handle into an option of a resource handle at the ABI layer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great points. For host-implemented functions being passed handles from guest-to-host, I think the same reasoning as above applies (the host should ideally fail gracefully or, because it's always allowed, trap; too-many-host-handles seems like it'd be pretty rare in non-pathological scenarios so trapping seems Fine).

When the guest is lowering a handle with the current eager ABI, indeed there is already a trap_if(i > Table.MAX_LENGTH) in the add function in the Table section of the ABI, so nothing new needs to be said for this case. But for the Lazy ABI, that's a good point @badeend that we could consider making this fallible.

But overall, I think this bullet might not be needed either.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants