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

Re: [PATCH 6/6] hash-object: use fsck for object checks

From
Jeff King <peff@peff.net>
Date
Feb 1, 2023, 12:50 UTC
Message-ID
<Y9pgG10dAoQABGXG@coredump.intra.peff.net>
In-Reply-To
<Y8haHL9xIWntSm0/@coredump.intra.peff.net>
On Wed, Jan 18, 2023 at 03:44:12PM -0500, Jeff King wrote:
Show 18 quoted lines
> @@ -2350,12 +2340,13 @@ static int index_mem(struct index_state *istate,
>  		}
>  	}
>  	if (flags & HASH_FORMAT_CHECK) {
> -		if (type == OBJ_TREE)
> -			check_tree(buf, size);
> -		if (type == OBJ_COMMIT)
> -			check_commit(buf, size);
> -		if (type == OBJ_TAG)
> -			check_tag(buf, size);
> +		struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
> +
> +		opts.strict = 1;
> +		opts.error_func = hash_format_check_report;
> +		if (fsck_buffer(null_oid(), type, buf, size, &opts))
> +			die(_("refusing to create malformed object"));
> +		fsck_finish(&opts);
>  	}

By the way, I wanted to call out one thing here that nobody mentioned during review: we are not checking the return value of fsck_finish().

That is a bit of a weird function. We must call it because it cleans up any resources allocated during the fsck_buffer() call. But it also is the last chance to fsck any special blobs (like those that are found as .gitmodules, etc). We only find out the filenames while looking at the enclosing trees, so we queue them and then check the blobs later.

So if we are hashing a blob, that is mostly fine. We will not have the blob's name queued as anything special, and so the fsck is a noop.

But if we fsck a tree, and it has a .gitmodules entry pointing to blob X, then we would also pull X from the odb and fsck it during this "finish" phase.

Which leads me to two diverging lines of thought:
  1. One of my goals with this series is that one could add objects to
     the repository via "git hash-object -w" and feel confident that no
     fsck rules were violated, because fsck implements some security
     checks. In the past when GitHub rolled out security checks this was
     a major pain, because objects enter repositories not just from
     pushes, but also from web-client activity (e.g., editing a blob on
     the website). And since Git had no way to say "fsck just this
     object", we ended up implementing the fsck checks multiple times,
     in libgit2 and in some of its calling code.
     So I was hoping that just passing the objects to "hash-object"
     would be a viable solution. I'm not sure if it is or not. If you
     just hash a blob, then we'll have no clue it's a .gitmodules file.
     OTOH, you have to get the matching tree which binds the blob to the
     .gitmodules path somehow. So if that tree is fsck'd, and then
     checks the blob during fsck_finish(), that should be enough.
     Assuming that fsck complains when the pointed-to blob cannot be
     accessed, which I think it should (because really, incremental
     pushes face the same problem).
     In which case we really ought to be checking the result of
     fsck_finish() here and complaining.
  2. We're not checking fsck connectivity here, and that's intentional.
     So you can "hash-object" a tree that points to blobs that we don't
     actually have. But if you hash a tree that points a .gitmodules
     entry at a blob that doesn't exist, then that will fail the fsck
     (during the finish step). And respecting the fsck_finish() exit
     code would break that.
     As an addendum, in a regular fsck, many trees might mention the
     same blob as .gitmodules, and we'll queue that blob to be checked
     once. But here, we are potentially running a bunch of individual
     fscks, one per object we hash. So if you had, say, 1000 trees that
     all mentioned the same blob (because other entries were changing),
     and you tried to hash them all with "hash-object --stdin-paths" or
     similar, then we'd fsck that blob 1000 times.
     Which isn't wrong, per se, but seems inefficient. Solving it would
     require keeping track of what has been checked between calls to
     index_mem(). Which is kind of awkward, seeing as how low-level it
     is. It would be a lot more natural if all this checking happened in
     hash-object itself.

So I dunno. The code above is doing (2), albeit with the inefficiency of checking blobs that we might not care about. I kind of think (1) is the right thing, though, and anybody who really wants to make trees that point to bogus .gitmodules can use --literally.

-Peff
Previous: Jeff KingNext: Ævar Arnfjörð Bjarmason
Message 15 of 28 in “hash-object: use fsck to check objects”
  1. 0/6 hash-object: use fsck to check objectsJeff King, Jan 18, 2023
  2. 1/6 t1007: modernize malformed object testsJeff King, Jan 18, 2023
  3. Taylor BlauJan 18, 2023
  4. 2/6 t1006: stop using 0-padded timestampsJeff King, Jan 18, 2023
  5. 3/6 t7030: stop using invalid tag nameJeff King, Jan 18, 2023
  6. 4/6 t: use hash-object --literally when created malformed objectsJeff King, Jan 18, 2023
  7. Taylor BlauJan 18, 2023
  8. Jeff KingJan 19, 2023
  9. 5/6 fsck: provide a function to fsck buffer without object structJeff King, Jan 18, 2023
  10. Taylor BlauJan 18, 2023
  11. Jeff KingJan 19, 2023
  12. 6/6 hash-object: use fsck for object checksJeff King, Jan 18, 2023
  13. Taylor BlauJan 18, 2023
  14. Jeff KingJan 19, 2023
  15. Jeff KingFeb 1, 2023
  16. Ævar Arnfjörð BjarmasonFeb 1, 2023
  17. Junio C HamanoFeb 1, 2023
  18. Jeff KingJan 18, 2023
  19. Junio C HamanoJan 18, 2023
  20. Taylor BlauJan 18, 2023
  21. Jeff KingJan 19, 2023
  22. Jeff KingJan 19, 2023
  23. 7/6 fsck: do not assume NUL-termination of buffersJeff King, Jan 19, 2023
  24. Junio C HamanoJan 19, 2023
  25. René ScharfeJan 21, 2023
  26. Jeff KingJan 22, 2023
  27. René ScharfeJan 22, 2023
  28. Ævar Arnfjörð BjarmasonFeb 1, 2023

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.