Repository navigation
harden decoders - #1515
Merged
Merged
harden decoders#1515
Conversation
`read_value` accepted any non-zero byte as `option::some` and silently treated any byte other than 0 as `result::err`. Use the same `read_option_status` and `read_result_status` helpers as the `wasmtime` codec so that malformed status bytes fail with `InvalidData`, and read at least one byte for `flags` like the `wasmtime` codec does. Assisted-by: claude:claude-fable-5-1
rvolosatovs
commented
Oct 6, 2026
rvolosatovs
force-pushed
the
fix/decoder
branch
from
October 6, 2026 18:45
f61986e to
fda5c70
Compare
rvolosatovs
commented
Oct 6, 2026
Look up variant and enum discriminants and flag bits with the same helpers and width-specific branches as the `wasmtime` codec, and encode list, record and tuple elements by reference rather than cloning them first. Encoding a `result` or `variant` payload whose presence does not match the type now fails like the `wasmtime` codec does, instead of silently producing undecodable output. Drop `with_type`, which was `WaveEncoder::new` under another name, skip the `dst` buffer in the trace span and make the doc examples run. Assisted-by: claude:claude-fable-5-1
The `wasmtime` and `wave` `read_value` implementations and the transport `ListDecoder` all reserved capacity for the list length read off the wire before reading any element, so a few bytes claiming `u32::MAX` elements forced a multi-gigabyte allocation. Let the vectors grow as elements are actually decoded instead. Assisted-by: claude:claude-fable-5-1
Assisted-by: claude:claude-fable-5-1
`ingress` reserved capacity for the path length and zero-filled a buffer for the data length read off the wire before reading either, so a few bytes claiming `u32::MAX` forced a multi-gigabyte allocation. Grow the path as elements are read and read the data through `take`, failing with `UnexpectedEof` if the stream ends early. Assisted-by: claude:claude-opus-5-5
Pulls in the fix avoiding preallocation from untrusted `core:name` and `core:vec` lengths. Assisted-by: claude:claude-opus-5-5
Spell out the target type at the conversion site instead of relying on `try_into()` inference or `as` casts. Assisted-by: claude:claude-opus-5-5
The remote resource handle arm of `read_value` preallocated the handle length read off the wire and then read to the end of the stream, ignoring that length and swallowing any data following the handle. Read exactly the declared number of bytes instead. Assisted-by: claude:claude-opus-5-5
The Go runtime list, byte list and frame readers as well as the generated string, byte list, list, stream chunk and resource handle readers allocated the length read off the wire up front, so a few bytes claiming `u32::MAX` elements forced a multi-gigabyte allocation. Grow the buffers as data is actually read instead. This also fixes string and handle readers relying on a single `Read` call filling the buffer. Assisted-by: claude:claude-opus-5-5
Assisted-by: claude:claude-opus-5-5
Generated `f32`, `f64` and flags readers used a single `Read` call, which the `io.Reader` contract allows to return short with a `nil` error, silently zero-padding the value and desynchronizing the decoder on the following bytes. Use `io.ReadFull` instead. Assisted-by: claude:claude-fable-5-1
rvolosatovs
force-pushed
the
fix/decoder
branch
from
October 7, 2026 10:27
fda5c70 to
19e94f0
Compare
Path buffers in the frame decoder were preallocated from the wire-provided path length, bounded only by the configurable maximum depth. Cap the initial allocation at `MAX_INITIAL_PATH_CAPACITY`, the same way frame data is capped at `MAX_INITIAL_DATA_CAPACITY`, and preallocate the ingress path up to the same cap. Assisted-by: claude:claude-fable-5-1
wasm-tokio 0.7.0 caps decoder preallocation at a const-generic `MAX_INITIAL_CAPACITY` (1 MiB by default), so list and byte decoders no longer reserve memory based solely on an untrusted length prefix. Assisted-by: claude:claude-fable-5-1
Assisted-by: claude:claude-fable-5-1
rvolosatovs
force-pushed
the
fix/decoder
branch
from
October 7, 2026 11:10
02cb929 to
0e2d9f3
Compare
rvolosatovs
marked this pull request as ready for review
October 7, 2026 11:13
Reject paths deeper than the decoder's default maximum depth and forward frame data in chunks of at most `DEFAULT_MAX_INITIAL_CAPACITY` bytes instead of buffering whole frames of up to 4 GiB. Also drop the redundant path reservation in the frame decoder and reuse `wasm_tokio::DEFAULT_MAX_INITIAL_CAPACITY`. Assisted-by: claude:claude-opus-5-5
Reset `ListDecoder` state when an element fails to decode, matching `wasm_tokio::CoreVecDecoder`, and preallocate list buffers up to `DEFAULT_MAX_INITIAL_CAPACITY` bytes rather than not at all. Assisted-by: claude:claude-opus-5-5
Add `ReadBytes`, which preallocates at most 1 MiB and reads the rest in chunks via `io.ReadFull`, and `NewSlice`, which returns a non-nil slice with capped capacity. Use them in the library and generated bindings, restoring non-nil empty lists and frame paths. Assisted-by: claude:claude-opus-5-5
Compare untrusted `uint32` lengths before converting to `int`, which would go negative on 32-bit targets and make `make` panic. Assisted-by: claude:claude-opus-5-5
Clear buffered elements and deferred handlers when a list element fails to decode, so a subsequent empty list does not hand out stale deferred handlers via `take_deferred`. Assisted-by: claude:claude-opus-5-5
The spec defines no maximum path depth and neither the Go nor JS implementations enforce one, so cap the path preallocation instead of rejecting deep paths. Also read data chunks into uninitialized buffers rather than zero-filling them first. Assisted-by: claude:claude-opus-5-5
Forward frame payloads to sub-streams chunk by chunk instead of buffering whole frames, avoiding quadratic copying of large frames arriving over many transport chunks. Assisted-by: claude:claude-opus-5-5
`WaveEncoder::with_type` was removed, which is a breaking change. Assisted-by: claude:claude-opus-5-5
Collapse the Go `ReadBytes`/`NewSlice` tests into a single case that still crosses the 1 MiB chunk boundary, and merge the two JS framing tests into one that only passes when frame data is routed before the frame is complete. Assisted-by: claude:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.