git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [DO NOT APPLY PATCH 4/4] index-pack: optionally skip duplicate packfile entries

From
Jeff King <peff@peff.net>
Date
Aug 22, 2013, 00:47 UTC
Message-ID
<20130822004710.GA563@sigill.intra.peff.net>
In-Reply-To
<xmqqob8qskbc.fsf@gitster.dls.corp.google.com>
On Wed, Aug 21, 2013 at 04:20:07PM -0700, Junio C Hamano wrote:
Show 16 quoted lines
> I do not understand the last bit.  If two copies of an object exist
> but you have only one slot for the object in the index, and another
> object names it as its base with ref-delta, then reconstituting it
> should work just fine---whichever representation of the base object
> is recorded in the .idx, that first needs to be reconstituted before
> the delta is applied to it, and both copies should yield identical
> contents for the delta base object, no?
> 
> In any case, ejecting one from the pack .idx would not help in the
> presense of either broken or malicious packer that reuses delta too
> aggressively.  Suppose you have objects A and B and somehow manage
> to create a cycle of deltas, A names B as its delta base and B names
> A as its delta base.  The packer may notice its mistake and then add
> another copy of A as a base object.  The pack contains two copies of
> A (one is delta based on B, the other is full) and B (delta against
> A).
Yes, that is the potentially problematic scenario...
Show 9 quoted lines
> If B refers to the copy of A that is delta against B using ofs-delta,
> fixing the .idx file will have no effect.  read_packed_sha1(B) will
> read the delta data of B, finds the offset to start reading the data
> for A which was excised from the .idx and unless that codepath is
> updated to consult revindex (i.e. you mark one copy of A in the .pack
> as bad, and when B refers to that bad copy of A via ofs-delta, you
> check the offset against revindex to get the object name of A and go
> to the good copy of A), you will never finish reading B because
> reading the bad copy of A will lead you to first reconstitute B.
Yes. There are two levels of brokenness here.

One is that the pack has a delta base offset for B that leads to the "wrong" A, creating a cycle. This is an utterly broken pack and cannot be fixed automatically (you do not even know the name of the cycled A because you cannot constitute it to find its name and realize that it has a duplicate!). But we should notice this during index-pack, because we cannot reconstruct the object.

Another situation is that your delta base points to the "right" A. You can reconstruct either A or B, because although there is a duplicate, there are no cycles in the delta chain (i.e., the chain does not care about object name, only offset, and offsets point only one way).

So we do not have a problem at all with reconstructing the object data. We do have two ways we might access A from the same pack. For regular object lookup, that is probably not a big deal. For operations like repacking that look more closely at the on-disk representation, I do not know. We may mark A as "has a delta" or "does not have a delta" at one point in the code, but then later find the other copy of A which does not match. I did not trace through all of pack-objects to find whether that is possible, or what bugs it might cause. But indexing only one copy as the "master" means that we will always get a consistent view of the object (from that particular pack, at least).

Now consider REF_DELTA. We lookup the base object by name, so we may get a different answer depending on how it is indexed. E.g., index-pack keeps an internal hash table, whereas later callers will use the .idx file. When looking at the .idx file, we may use a regular binary search, or sha1_entry_pos. The exact order of the search may even change from git version to git version.

So you may have a situation where index-pack finds the "right" A and properly reconstructs the file while creating the .idx, and thinks all is well. But later lookups may find the "wrong" A, and fail. In other words, we cannot trust that data that makes it through index-pack is not corrupted (and it is index-pack's output that lets receive-pack commit to the client that we have their objects, so we want to be reasonably sure we have uncorrupted copies at that point).

Choosing a single "master" A to go in the .idx means we will get a consistent view of A once the .idx is generated. But it may not be the right one, and it may not be the one we used to check that we can resolve A and B in the first place.

The right fix for that situation is to keep both entries in the .idx, detect delta cycles, then look up extra copies of A, but being aware that you already found one copy in the .idx and that sha1_entry_pos (or the vanilla binary search) should return any other copies, if they exist. I do not think we detect delta cycles at all (though I didn't check), but certainly we do not have a way to tell sha1_entry_pos any different information so that it will find the "other" A.

So I think it is a recoverable state, but it is a non-trivial bit of code for something that should never happen. Rejecting on push/fetch via index-pack is simple and easy, and we can deal with the fallout if and when it ever actually happens.

> > I think this line of "fixing" should probably be scrapped.
> 
> I tend to agree.

OK. Let's drop the "skip" patch, then. I'll re-roll with a patch to flip the default to "reject".

-Peff
Previous: Junio C HamanoNext: Junio C Hamano
Message 35 of 37 in “duplicate objects in packfile”
  1. Jeff KingAug 14, 2013
  2. Junio C HamanoAug 14, 2013
  3. Junio C HamanoAug 14, 2013
  4. Nicolas PitreAug 14, 2013
  5. Jeff KingAug 16, 2013
  6. 0/4 duplicate objects in packfilesJeff King, Aug 21, 2013
  7. 1/4 sha1-lookup: handle duplicate keys with GIT_USE_LOOKUPJeff King, Aug 21, 2013
  8. 2/4 index-pack: optionally reject packs with duplicate objectsJeff King, Aug 21, 2013
  9. Duy NguyenAug 22, 2013
  10. Jeff KingAug 22, 2013
  11. 0/6 duplicate objects and delta cycles, oh my!Jeff King, Aug 22, 2013
  12. 1/6 test-sha1: add a binary output modeJeff King, Aug 22, 2013
  13. 2/6 sha1-lookup: handle duplicate keys with GIT_USE_LOOKUPJeff King, Aug 22, 2013
  14. Junio C HamanoAug 23, 2013
  15. Jeff KingAug 23, 2013
  16. Nicolas PitreAug 23, 2013
  17. Jeff KingAug 23, 2013
  18. 0/6 duplicate objects and delta cyclesJeff King, Aug 24, 2013
  19. 1/6 test-sha1: add a binary output modeJeff King, Aug 24, 2013
  20. 2/6 sha1-lookup: handle duplicate keys with GIT_USE_LOOKUPJeff King, Aug 24, 2013
  21. 3/6 add tests for indexing packs with delta cyclesJeff King, Aug 24, 2013
  22. 4/6 test index-pack on packs with recoverable delta cyclesJeff King, Aug 24, 2013
  23. 5/6 index-pack: optionally reject packs with duplicate objectsJeff King, Aug 24, 2013
  24. 6/6 default pack.indexDuplicates to falseJeff King, Aug 24, 2013
  25. Johannes SixtAug 23, 2013
  26. Jeff KingAug 23, 2013
  27. 3/6 add tests for indexing packs with delta cyclesJeff King, Aug 22, 2013
  28. 4/6 test index-pack on packs with recoverable delta cyclesJeff King, Aug 22, 2013
  29. 5/6 index-pack: optionally reject packs with duplicate objectsJeff King, Aug 22, 2013
  30. 6/6 default pack.indexDuplicates to falseJeff King, Aug 22, 2013
  31. Nicolas PitreAug 22, 2013
  32. 3/4 reject duplicates when indexing a packfile we createdJeff King, Aug 21, 2013
  33. 4/4 index-pack: optionally skip duplicate packfile entriesJeff King, Aug 21, 2013
  34. Junio C HamanoAug 21, 2013
  35. Jeff KingAug 22, 2013
  36. Junio C HamanoAug 21, 2013
  37. Nicolas PitreAug 14, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.