Skip to content

Fix empty sub-block terminator in extension parsing#228

Merged
197g merged 1 commit intoimage-rs:masterfrom
davidalecrim1:fix-empty-extension-sub-block
Apr 9, 2026
Merged

Fix empty sub-block terminator in extension parsing#228
197g merged 1 commit intoimage-rs:masterfrom
davidalecrim1:fix-empty-extension-sub-block

Conversation

@davidalecrim1
Copy link
Copy Markdown
Contributor

Problem

A GIF with an empty comment extension (21 fe 00) immediately before its image descriptor fails to decode. The decoder returns zero frames or errors with UnexpectedEof, despite the file being valid per the GIF89a spec.

This was caught while investigating zed-industries/zed#53426, where several valid GIFs rendered fine in VS Code but failed silently in Zed.

Root cause

ExtensionDataSubBlockStart(sub_block_len) always transitions to ExtensionDataSubBlock(sub_block_len) via goto!(0, ...), which re-presents the current byte b without consuming it. When sub_block_len == 0 (a block terminator per GIF89a §23), the state machine falls into ExtensionDataSubBlock(0) and checks if b == 0 against whatever byte comes next — which is the first byte of the next record (e.g. 0x2c, the image descriptor intro). Since that byte is not zero, it's misinterpreted as a new sub-block of that many bytes, swallowing image data and causing UnexpectedEof or missing frames.

Fix

Short-circuit in ExtensionDataSubBlockStart when sub_block_len == 0, going directly to ExtensionBlockEnd the same way ExtensionDataSubBlock does when it sees the actual terminator byte.

Tests

  • empty_comment_extension_decodes_one_frame: minimal hand-crafted GIF bytes with 21 fe 00 before the image descriptor — verifies the parser handles the spec-compliant case
  • empty_comment_extension_real_world_gif: real-world 128x128 GIF file that triggered the bug report
  • Both tests fail on the current code and pass with this fix
  • All existing tests continue to pass

A zero-length sub-block (size byte = 0x00) is a block terminator per
the GIF89a spec (§23). When ExtensionDataSubBlockStart(0) transitioned
to ExtensionDataSubBlock(0) via goto!(0, ...), the current byte `b` was
re-presented rather than consumed. ExtensionDataSubBlock(0) then checked
`if b == 0` against the *next* record byte (e.g. 0x2c, the image
descriptor intro), misinterpreting it as a new sub-block of that many
bytes. This swallowed image data and resulted in UnexpectedEof or zero
frames being decoded.

Fix by short-circuiting directly to ExtensionBlockEnd when sub_block_len
is 0, matching the existing block-terminator path in ExtensionDataSubBlock.

Adds two regression tests and the real-world GIF file that triggered the
bug report (a 128x128 single-frame GIF with an empty comment extension
immediately before its image descriptor).
@davidalecrim1 davidalecrim1 force-pushed the fix-empty-extension-sub-block branch from f7304d3 to f0f52cb Compare April 9, 2026 14:47
Copy link
Copy Markdown
Member

@197g 197g left a comment

Choose a reason for hiding this comment

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

That is indeed on weird edge case.

@197g 197g merged commit 5116dcd into image-rs:master Apr 9, 2026
13 checks passed
@davidalecrim1
Copy link
Copy Markdown
Contributor Author

Thanks @197g ! Can you create a tag for this?

@197g
Copy link
Copy Markdown
Member

197g commented Apr 9, 2026

A crates.io release or just a tag? Will probably do both, just the tag is quicker.

@davidalecrim1
Copy link
Copy Markdown
Contributor Author

I would need both to open a PR for https://github.com/image-rs/image with the bump. After that one is released as well I should have all I need for Zed.

@197g
Copy link
Copy Markdown
Member

197g commented Apr 9, 2026

You don't need to open a PR against image, the version requirement is gif = ^0.14. We can release 0.14.2, I think. So it'll just be:

cargo update -p gif --precise 0.14.2

@davidalecrim1
Copy link
Copy Markdown
Contributor Author

Nice to know. I will keep that in mind.

davidalecrim1 added a commit to davidalecrim1/zed that referenced this pull request Apr 9, 2026
The fix for GIFs with empty comment extensions was merged upstream in
image-rs/image-gif#228 and released as gif 0.14.2. Bump the dependency
so those GIFs now render correctly in the markdown preview.
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.

2 participants