plumbing: format/packfile, Tighten delta validation#2089
Merged
Conversation
f4c3439 to
9e3c92b
Compare
Each delta operation's declared size is validated against the size still remaining in the target buffer, not the total target size. The previous check against the total accepted a sequence of operations whose cumulative output would exceed the declared target, leaving the unsigned remaining counter unable to reach zero and the loop free to keep writing past the declared target until the delta payload was consumed. Three sibling code paths shared the flaw and are updated together: `patchDelta` (used by `PatchDelta` and `ApplyDelta`), `ReaderFromDelta`, and `patchDeltaWriter` (used by the packfile parser). The loop structure is rewritten as `for remainingTargetSz > 0` so the invariant is explicit, and a post-loop check rejects deltas that leave bytes unconsumed -- matching the `data != top` sanity check in `patch-delta.c`[1]. Empty-target deltas (header-only, no operations) are now accepted, also matching upstream. Several incidental defects on the same call paths are fixed: `patchDelta`'s copy-from-src failure used `break`, which only exited the switch and let the loop keep consuming delta bytes; `patchDeltaWriter` returned `(0, ZeroHash, nil)` on invalid input, silently reporting success; and `ReaderFromDelta`'s copy-from-src failure closed the writer without an error, silently truncating the consumer stream. Each now propagates `ErrInvalidDelta` (or `ErrDeltaCmd`) consistently. `packfile.getMemoryObject` was re-inflating delta payloads into the buffer the scanner had already populated, appending a duplicate copy that previously went unnoticed because the loop silently ignored anything past the target. The fix matches the cached-content pattern already used in `Scanner.WriteObject`. [1]: https://github.com/git/git/blob/c2c5f6b1e479f2c38e0e01345350620944e3527f/patch-delta.c Assisted-by: Claude Opus 4.7 Signed-off-by: Hidde Beydals <[email protected]>
9e3c92b to
f5526de
Compare
hiddeco
added a commit
to hiddeco/go-git
that referenced
this pull request
May 8, 2026
Each delta operation's declared size is validated against the size still remaining in the target buffer, not the total target size. The previous check against the total accepted a sequence of operations whose cumulative output would exceed the declared target, leaving the unsigned remaining counter unable to reach zero and the loop free to keep writing past the declared target until the delta payload was consumed. Three sibling code paths shared the flaw and are updated together: `patchDelta` (used by `PatchDelta` and `ApplyDelta`), `ReaderFromDelta`, and `patchDeltaWriter` (used by the packfile parser). The loop structure is rewritten as `for remainingTargetSz > 0` so the invariant is explicit, and a post-loop check rejects deltas that leave bytes unconsumed -- matching the `data != top` sanity check in `patch-delta.c`[1]. Empty-target deltas (header-only, no operations) are now accepted, also matching upstream. Several incidental defects on the same call paths are fixed: `patchDelta`'s copy-from-src failure used `break`, which only exited the switch and let the loop keep consuming delta bytes; `patchDeltaWriter` returned `(0, ZeroHash, nil)` on invalid input, silently reporting success; and `ReaderFromDelta`'s copy-from-src failure closed the writer without an error, silently truncating the consumer stream. Each now propagates `ErrInvalidDelta` (or `ErrDeltaCmd`) consistently. [1]: https://github.com/git/git/blob/c2c5f6b1e479f2c38e0e01345350620944e3527f/patch-delta.c Backport of go-git#2089. Assisted-by: Claude Opus 4.7 Signed-off-by: Hidde Beydals <[email protected]>
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.
Each delta operation's declared size is validated against the size still remaining in the target buffer, not the total target size. The previous check against the total accepted a sequence of operations whose cumulative output would exceed the declared target, leaving the unsigned remaining counter unable to reach zero and the loop free to keep writing past the declared target until the delta payload was consumed.
Three sibling code paths shared the flaw and are updated together:
patchDelta(used byPatchDeltaandApplyDelta),ReaderFromDelta, andpatchDeltaWriter(used by the packfile parser).The loop structure is rewritten as
for remainingTargetSz > 0so the invariant is explicit, and a post-loop check rejects deltas that leave bytes unconsumed -- matching thedata != topsanity check inpatch-delta.c1. Empty-target deltas (header-only, no operations) are now accepted, also matching upstream.Several incidental defects on the same call paths are fixed:
patchDelta's copy-from-src failure usedbreak, which only exited the switch and let the loop keep consuming delta bytes;patchDeltaWriterreturned(0, ZeroHash, nil)on invalid input, silently reporting success; andReaderFromDelta's copy-from-src failure closed the writer without an error, silently truncating the consumer stream. Each now propagatesErrInvalidDelta(orErrDeltaCmd) consistently.packfile.getMemoryObjectwas re-inflating delta payloads into the buffer the scanner had already populated, appending a duplicate copy that previously went unnoticed because the loop silently ignored anything past the target. The fix matches the cached-content pattern already used inScanner.WriteObject.