imazen / imazen/zenzstd

Decoder's 100 MiB window limit is enforced on reset but not on first use

Open
#10 0 comments 0 reactions 1 assignee View on GitHub

@lilith is already working on this.

Since Aug 29, 2026.

Dominant language
Rust
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Summary

MAXIMUM_ALLOWED_WINDOW_SIZE (100 MiB, decoding/frame_decoder.rs:19) is checked in FrameDecoderState::reset but not in FrameDecoderState::new. Whether a frame is accepted therefore depends on whether the FrameDecoder has been used before:

  • First use — FrameDecoder::new() then decode — takes the new path, no limit check, and DecoderScratch::new(window_size) sizes the buffer from the header.
  • Any later use takes reset, which returns WindowSizeTooBig.

Measured

On aarch64-apple-darwin, with the encoder temporarily declaring the uncapped window_log 27 for level 22 (128 MiB):

fresh FrameDecoder:  L22 declared_window=134217728  ours=ok
reused FrameDecoder: L22 declared_window=134217728  ours=WindowSizeTooBig { requested: 134217728 }

Levels 17-21 (8/32/64 MiB) pass on both paths. The divergence is exactly the 100 MiB limit.

Why it matters

  1. Inconsistent contract. The same bytes decode or fail depending on decoder reuse, which is not something a caller can reason about. decoding/streaming_decoder.rs:53 documents "the 100 MiB internal window cap" as if it were unconditional.
  2. The unchecked path is the allocating one. FrameDecoderState::new sizes DecodeBuffer from an attacker-controlled header field with no ceiling below the format's own (3.75 TB, enforced only by MAX_WINDOW_SIZE in frame.rs). This is a resource-management problem, not a memory-safety one, but a fresh decoder is the common shape for one-shot decode of untrusted input.

Found while

Fixing #9. The window cap chosen there (MAX_ENCODER_WINDOW_LOG = 23, 8 MiB) is partly justified by this limit, so the asymmetry is worth resolving before anyone raises it.

Suggested fix

Move the check into FrameDecoderState::new as well — ideally into frame::read_frame_header's caller path so there is one place. Needs a check that the decode corpus and conformance vectors stay green; nothing in decodecorpus_files/ declares anything near 100 MiB, so the expectation is no test movement.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.