Stream: git-wasmtime

Topic: wasmtime / PR #14617 Bounds-check string transcoder buffe...


view this post on Zulip Wasmtime GitHub notifications bot (Oct 08 2026 at 20:21):

ilyas-mallah opened PR #14617 from ilyas-mallah:transcode-bounds-checks to bytecodealliance:main:

The host string transcoders in vm/component/libcalls.rs build raw slices from the pointers they get, and only the adapter module bounds-checks those pointers. This path has had three advisories this year (GHSA-394w-hwhg-8vgm, GHSA-hx6p-xpx3-jvvv, GHSA-jxhv-7h78-9775), each a gap in the adapter's checks that the host trusted.

This adds a second check in the transcoder trampoline, like #13027 and #14484 did elsewhere: before the libcall it loads each memory's current_length and traps unless both buffers are in bounds and aligned, using the adapter's own trap codes. The cost is a couple of loads and compares per transcode call.

A new disas test shows the checks, and the component-model wast tests pass, including big-strings.wast and memory64.wast, also on Pulley. With FACT's validate_guest_pointer disabled locally, the 6 component_model::strings tests still trap as expected. Without this change, ptr_overflow and realloc_oob crash the host with SIGSEGV.

Open questions:

view this post on Zulip Wasmtime GitHub notifications bot (Oct 08 2026 at 20:21):

ilyas-mallah requested alexcrichton for a review on PR #14617.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 08 2026 at 20:21):

ilyas-mallah requested wasmtime-core-reviewers for a review on PR #14617.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 08 2026 at 20:21):

ilyas-mallah requested wasmtime-compiler-reviewers for a review on PR #14617.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 15:09):

:memo: alexcrichton submitted PR review:

Thanks for this! Out of curiosity, would you be interested in helping to try something with a slightly different tact instead? Bounds-checks are notoriously tricky and hard to get right, so instead of that one possibility would be to use a normal wasm load/store to determine if the string is valid. For example if before calling transcoding the adapter could perform a wasm load of the last byte/16-bit codepoint in the string, and if that succeeds then everything is guaranteed to be in-bounds. That'd keep the translation and handling on the wasm-side as well which I think would be nice to keep the Cranelift side smaller

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 15:13):

ilyas-mallah commented on PR #14617:

Sure, happy to try that. I'll rework it so the adapter does a wasm load of the last byte (or the last 16-bit unit for UTF-16) of each buffer before calling the transcoder, and drop the Cranelift side. For zero-length strings I'd skip the load, since the host only builds an empty slice there. Does that match what you had in mind?

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 15:23):

alexcrichton commented on PR #14617:

Yeah that's what I was thinking too, zero-length is just skipped

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 19:59):

ilyas-mallah updated PR #14617.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 20:00):

ilyas-mallah edited PR #14617:

The host string transcoders in vm/component/libcalls.rs build raw slices from the pointers they get, and only the adapter's earlier checks keep those pointers in bounds. This path had three advisories this year (GHSA-394w-hwhg-8vgm, GHSA-hx6p-xpx3-jvvv, GHSA-jxhv-7h78-9775), each a gap in those checks.

This adds a second check in the adapter right before every transcoder call: a plain wasm load of the last byte or 16-bit unit of each buffer the host will access, in that buffer's memory. Empty buffers skip the load. Before it, the adapter traps on a length over the maximum string size or an address that wraps, and 16-bit buffers keep the alignment check, now a helper shared with validate_guest_pointer.

If one of these loads fails, the trap is a regular out-of-bounds trap instead of StringOutOfBounds. That only happens when an earlier check has a bug.

Tested with a new disas test and the component-model wast tests on Cranelift, Winch and Pulley. With validate_guest_pointer disabled locally, the out-of-bounds cases in component_model::strings still trap, and without the new loads as well they crash the test process.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 09 2026 at 20:01):

ilyas-mallah commented on PR #14617:

Yeah that's what I was thinking too, zero-length is just skipped

Just reworked and pushed it as a new commit that replaces the Cranelift version entirely. The full PR diff is easier to read than the commit on its own. Two things that might be worth a look: the 16-bit alignment check moved out of validate_guest_pointer into a helper so both can use it, and a failed load is now a plain out-of-bounds trap instead of StringOutOfBounds. I also swapped the disas test for one showing the loads, can drop it if it's not useful.


Last updated: Oct 11 2026 at 02:20 UTC