Repository navigation
Conversation
The decoder read a length or count from the file and then allocated a buffer of that size before it read the data. A file of 22 bytes could declare a string of 2^40 bytes. The Go runtime then stopped with "fatal error: out of memory". Parse cannot recover from that error. Strings and LZF input up to 1 MiB are read as before. A longer declared length grows the buffer as the data arrives. A short input still returns the same EOF error as before. lzf.Decompress rejects an output length that is more than 88 times the input length, because valid data never exceeds that ratio. Counts that come from a buffer in memory (intset cardinality and stream listpack counts) are now checked against the buffer size. Counts that come from the file only set a capped initial capacity. The slice grows with append, and the loops still run for the real count. The Valkey slot import branch now uses append as well. Fixes HDT3213#75 Signed-off-by: Zsolt Gaspar <17578237+zsltg@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 of 8 tasks
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.
Summary
Fixes #75.
The decoder read a length or a count from the file and allocated a buffer of that size before it read the data. A malformed file of 22 bytes could declare a 2^40-byte string. The Go runtime then stopped the process with
fatal error: out of memory, andDecoder.Parsecannot recover from that error. With this change, memory follows the data that actually arrives, and a malformed file returns an error.Changes
readStringandreadLZFread through a new helper,readBytes. A length up to 1 MiB is allocated at once, the same as before. For a longer length, the buffer starts at 1 MiB and doubles only as data arrives. A truncated value returns the sameio.EOForio.ErrUnexpectedEOFas before.lzf.Decompressrejects an output length that is more than 88 times the input length. One back reference of 3 bytes produces at most 264 bytes, and a literal run produces fewer bytes than it uses, so valid data never exceeds that ratio.capHint.appendgrows the slice, and the loops still run for the real count. This applies to the list, set, zset, stream consumer group, and Valkey slot import readers. The slot import branch now usesappendinstead of indexed writes.Tests
TestParseDeclaredLengthExceedsDatabuilds small malformed files for the string, LZF, list, set, zset, and intset types. Each file must return an error fromParse.TestParseValkeySlotImportHugeRangesandTestReadStreamEntryContentHugeCountcover the slot import branch and the stream counts.TestReadBytesAboveChunkcovers a value longer than 1 MiB: a complete value, a value that is cut short, and a value that ends exactly at the 1 MiB boundary.TestDecompressRejectsImpossibleOutLenandTestDecompressEmptyInputcover the LZF bound and the empty input.TestCheckCountandTestCapHintcover the two helpers.Before the fix, the string and LZF tests stopped with
fatal error: out of memorywhen run underulimit -v 8000000. After the fix, all new tests pass.Validation
go test -coverpkg=./core,./ ./...passes on Go 1.18.10 and Go 1.27.cases/makes the same number of allocations before and after the change (70,749). The time difference is within measurement noise.GOARCH=386 go test ./lzfpasses.TestToJsonWithRegexandTestFlameGraphinhelpercan fail now and then. The cause is in the tests, not in this change.ToJsonswrites lines from several workers in the order that they finish, but the test expects a fixed order.d3flame.Webreturns before the server listens, and the test sends a request at once. This change does not touch either test. If one of them fails in CI, run the job again.Generated by Claude Code