Context
PR #245 introduced FrameDecoder::decode_to_slice with
UserSliceBackend writing directly into the caller's slice. The
backend's write methods (extend, extend_and_fill,
extend_from_within_unchecked) bounds-check via debug_assert! and
a release-mode assert!. Per-block produced tracking guards FCS
overflow AFTER each block completes.
Problem
Within a single Compressed block, the burst's per-symbol writes
hit the backend write methods in a tight loop. A malformed frame
that declares a small frame_content_size AND a Compressed block
whose payload expands past the declared size will cause the burst
to write past slice.len() and panic (assert! or slice
indexing). This is a DoS surface for adversarial input — should be
a structured FrameDecoderError instead.
Proposed fix
Extend BufferBackend to support fallible writes that return
Result<(), BackendOverflow>. The UserSliceBackend impl checks
remaining capacity on every write and returns an error instead of
panicking. FlatBuf / RingBuffer keep their current panic-free
behaviour (their Vec::reserve path handles growth).
Open design questions:
- Do we add a parallel fallible API or migrate all backends?
- Cost of per-write capacity check in the HUF burst hot path?
- Could a coarser per-block pre-flight check work instead
(e.g., refuse to enter direct path when produced + MAX_BLOCK_SIZE > content_size + WILDCOPY)?
Acceptance criteria
Workaround until fix
Callers handling untrusted input must use decode_all (which
routes through FlatBuf / RingBuffer and never panics on size
mismatches).
Part of the #244 perf roadmap.
Context
PR #245 introduced
FrameDecoder::decode_to_slicewithUserSliceBackendwriting directly into the caller's slice. Thebackend's write methods (
extend,extend_and_fill,extend_from_within_unchecked) bounds-check viadebug_assert!anda release-mode
assert!. Per-blockproducedtracking guards FCSoverflow AFTER each block completes.
Problem
Within a single Compressed block, the burst's per-symbol writes
hit the backend write methods in a tight loop. A malformed frame
that declares a small
frame_content_sizeAND a Compressed blockwhose payload expands past the declared size will cause the burst
to write past
slice.len()and panic (assert!or sliceindexing). This is a DoS surface for adversarial input — should be
a structured
FrameDecoderErrorinstead.Proposed fix
Extend
BufferBackendto support fallible writes that returnResult<(), BackendOverflow>. TheUserSliceBackendimpl checksremaining capacity on every write and returns an error instead of
panicking.
FlatBuf/RingBufferkeep their current panic-freebehaviour (their
Vec::reservepath handles growth).Open design questions:
(e.g., refuse to enter direct path when
produced + MAX_BLOCK_SIZE > content_size + WILDCOPY)?Acceptance criteria
payload returns
FrameDecoderError::FrameContentSizeMismatchfrom
decode_to_sliceinstead of panicking.Workaround until fix
Callers handling untrusted input must use
decode_all(whichroutes through
FlatBuf/RingBufferand never panics on sizemismatches).
Part of the #244 perf roadmap.