Repository navigation
A fixed length string could overflow its declared field length - #16
Merged
Merged
Conversation
The padding loop appended whole fill characters until the field was full, with no upper bound. A fill character that costs more than one byte, which is any character under the unicode encoder, then overshoots whenever the declared length is not a multiple of that width. An eleven byte unicode field padded with '\0' was written as twelve bytes. The buffer it went into was a List<byte> that could grow, so nothing noticed: the frame came out one byte too long, every field behind the string moved along by one, and the converter could not read its own output back. Both branches now truncate to the declared length, which is what the existing truncation branch already did for values that were too long. A fixed length field is exactly its declared length, always. The size check added with the caller supplied buffer is what surfaced this: the first new test with an odd declared length failed with "19 bytes were calculated but the writer produced 20" rather than silently emitting a broken frame. TestFixedLengthStringWriting pins the whole matrix around it: padding, truncation, empty and null values, an exactly fitting value, both encoders, an odd declared length in both the padding and the truncating branch, a surrogate pair cut through the middle, and agreement between the two write overloads for every value length. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The bug
The padding loop appended whole fill characters until the field was full, with no upper bound:
A fill character that costs more than one byte, which is any character under the unicode encoder, overshoots whenever the declared length is not a multiple of that width.
An eleven byte unicode field padded with
'\0'was written as twelve bytes. The buffer it went into was aList<byte>that could grow, so nothing noticed: the frame came out one byte too long, every field behind the string moved along by one, and the converter could not read its own output back.Measured, not inferred: for a POCO of
int+ 11 byte unicode string +int, the writer produces 20 bytes for a 19 byte protocol.How it surfaced
The size check added in #15 caught it on the very first new test. Instead of silently emitting a broken frame:
That check went in as a guard against something that "could only ever mean a bug in this library". It found one immediately.
The fix
Both branches truncate to the declared length, which is what the existing truncation branch already did for values that were too long. A fixed length field is exactly its declared length, always.
Tests
TestFixedLengthStringWritingpins the whole matrix, because the golden vectors pin one frame per protocol and cannot enumerate the interesting values of a field:ConvertToByteArrayandTryConvertToByteArrayfor every value length130/130 green.
Who is affected
Only protocols whose fill character is wider than one byte and whose declared length is not a multiple of that width. A one byte fill character, or a declared length that divides evenly, was always correct. Frames of an affected protocol written by 3.x are malformed and will not match what 4.0 writes, which is noted in the upgrade section of the README.
🤖 Generated with Claude Code