Repository navigation
Stop dropping the invalid byte 0xc1 in skip_nil and the header readers - #412
Merged
Merged
Conversation
HEAD_BYTE_REQUIRED, the "no head byte buffered" sentinel, was 0xc1, the one byte MessagePack leaves undefined. A real 0xc1 in the input therefore looked like "nothing buffered", so skip_nil, read_array_header and read_map_header let the next call read past it and the following read returned the next object instead of raising MalformedFormatError. Move the sentinel to 0x100, outside the range a byte can take. Co-authored-by: Claude Mythos 5.1 <noreply@anthropic.com>
byroot
approved these changes
Oct 31, 2026
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.
Since 078020e,
HEAD_BYTE_REQUIRED, thehead_bytevalue for "nothing cached", is0xc1, the one first byte the spec marks "(never used)". A0xc1read from the input is cached as the same value, so the nextget_head_bytethinks the cache is empty and reads past it.skip_nilreturns false and leaves the0xc1cached.read_array_headerandread_map_headerraiseUnexpectedTypeErrorand leave it cached too. The nextreadthen skips the invalid byte:readandskiparen't affected;read_primitiverejects0xc1first.Fix
HEAD_BYTE_REQUIREDis now0x100.head_byteis anunsigned intand only ever receives0..255from the input, so the sentinel can't collide. It is only assigned in init, reset andreset_head_byte, and only compared inget_head_byte. JRuby has its own unpacker.The JRuby decoder already behaves this way:
read_array_headerandread_map_headerrestore the buffer position on error, andskip_nilis not implemented there.The new specs call
skip_nil,read_array_headerorread_map_headeron"\xc1\x01"and check that the nextreadraisesMalformedFormatError. Without the change they fail withexpected MessagePack::MalformedFormatError but nothing was raised.