Validate chunk size in IndexPos read path to prevent panic/hang#350
Merged
Conversation
IndexPos.Read assumes len(curChunk) equals the size the index declares for the chunk, but loadChunk never verified it. An index that declares a chunk larger than the data actually stored for that chunk ID could drive curChunkOffset past len(curChunk), making the slice in Read panic with "slice bounds out of range". When the declared size is larger but the offset lands exactly at the real end of a non-last chunk, Read instead copies zero bytes and Seek(0) makes no progress, spinning forever. Both are reachable with an untrusted .caibx via 'desync cat' and the 'desync mount-index' FUSE read handler. Verify after loading that the actual chunk length matches the declared index size and return an error otherwise, the same check AssembleFile already performs. Covers the null-chunk shortcut too.
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.
Problem
IndexPos(used bydesync catand thedesync mount-indexFUSE read handler) assumes the invariantlen(curChunk) == Index.Chunks[curChunkIdx].Size, but nothing enforced it:findOffsetboundscurChunkOffsetagainst the declared size from the index, not the actual chunk data.loadChunkstored the chunk bytes from the store without comparing their length to the declared size.Size > ChunkSizeMax, so a declared size like 1000 against a real 4‑byte chunk (default max 256 KiB) passes validation.A
.caibxthat references a real chunk ID present in the store but over-declares that chunk'sSizetherefore triggers:Seek(200)setscurChunkOffset = 200(within the declared 1000), thenReadloads the real 4‑byte chunk and evaluatescurChunk[200:4]→slice bounds out of rangeatreadseeker.go:148. Withmount-indexthis crashes the daemon and orphans the mount.Readcopies 0 bytes andSeek(0, SeekCurrent)makes no progress, hanging the caller forever.Fix
After loading a chunk, verify its actual length matches the size the index declares for it and return an error on mismatch — the same check
AssembleFilealready performs (assemble.go:73). The null-chunk shortcut path is covered too (a legitimate null chunk always declaresSize == ChunkSizeMax). This restores the invariant the rest of the read path relies on, eliminating both the panic and the hang; callers receive an"unexpected size for chunk"error through the normalio.Readerpath instead.Tests
Added
readseeker_test.go:TestIndexReadSeekerSizeMismatchPanic— over-declared chunk + seek/read returns an error instead of panicking.TestIndexReadSeekerSizeMismatchNoLoop— short non-last chunk returns an error promptly instead of spinning (goroutine + 5s timeout guard).TestIndexReadSeekerValid— a well-formed multi-chunk index (including a memory-served null chunk) still reads back correctly, with and without seeking.Reverting the fix makes the first test panic at
readseeker.go:148and the second time out; the valid-path test passes either way. Full library suite andcmd/desyncbuild pass.