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

[PATCH 7/9] upload-pack: always turn off save_commit_buffer

From
Jeff King <peff@peff.net>
Date
Feb 28, 2024, 22:39 UTC
Message-ID
<20240228223900.GG1158131@coredump.intra.peff.net>
In-Reply-To
<20240228223700.GA1157826@coredump.intra.peff.net>

When the client sends us "want $oid" lines, we call parse_object($oid) to get an object struct. It's important to parse the commits because we need to traverse them in the negotiation phase. But of course we don't need to hold on to the commit messages for each one.

We've turned off the save_commit_buffer flag in get_common_commits() for a long time, since f0243f26f6 (git-upload-pack: More efficient usage of the has_sha1 array, 2005-10-28). That helps with the commits we see while actually traversing. But:

  1. That function is only used by the v0 protocol. I think the v2
     protocol's code path leaves the flag on (and thus pays the extra
     memory penalty), though I didn't measure it specifically.
  2. If the client sends us a bunch of "want" lines, that happens before
     the negotiation phase. So we'll hold on to all of those commit
     messages. Generally the number of "want" lines scales with the
     refs, not with the number of objects in the repo. But a malicious
     client could send a lot in order to waste memory.

As an example of (2), if I generate a request to fetch all commits in git.git like this:

  pktline() {
    local msg="$*"
    printf "%04x%s\n" $((1+4+${#msg})) "$msg"
  }
  want_commits() {
    pktline command=fetch
    printf 0001
    git cat-file --batch-all-objects --batch-check='%(objectname) %(objecttype)' |
      while read oid type; do
        test "$type" = "commit" || continue
        pktline want $oid
      done
      pktline done
      printf 0000
  }
  want_commits | GIT_PROTOCOL=version=2 valgrind --tool=massif git-upload-pack . >/dev/null

before this patch upload-pack peaks at ~125MB, and after at ~35MB. The difference is not coincidentally about the same as the sum of all commit object sizes as computed by:

  git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectsize)' |
  perl -alne '$v += $F[1] if $F[0] eq "commit"; END { print $v }'
In a larger repository like linux.git, that number is ~1GB.

In a repository with a full commit-graph file this will have no impact (and the commit graph would save us from parsing at all, so is a much better solution!). But it's easy to do, might help a little in real-world cases (where even if you have a commit graph it might not be fully up to date), and helps a lot for a worst-case malicious request.

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin/upload-pack.c | 2 ++
 upload-pack.c         | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 9b021ef026..15afb97260 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -8,6 +8,7 @@
 #include "replace-object.h"
 #include "upload-pack.h"
 #include "serve.h"
+#include "commit.h"
 
 static const char * const upload_pack_usage[] = {
 	N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -37,6 +38,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 
 	packet_trace_identity("upload-pack");
 	disable_replace_refs();
+	save_commit_buffer = 0;
 
 	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
diff --git a/upload-pack.c b/upload-pack.c
index 2a5c52666e..3970bb9b30 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -526,8 +526,6 @@ static int get_common_commits(struct upload_pack_data *data,
 	int got_other = 0;
 	int sent_ready = 0;
 
-	save_commit_buffer = 0;
-
 	for (;;) {
 		const char *arg;
 
-- 
2.44.0.rc2.424.gbdbf4d014b
Previous: Taylor BlauNext: Jeff King
Message 11 of 17 in “bound upload-pack memory allocations”
  1. 0/9 bound upload-pack memory allocationsJeff King, Feb 28, 2024
  2. 1/9 upload-pack: drop separate v2 "haves" arrayJeff King, Feb 28, 2024
  3. 2/9 upload-pack: switch deepen-not list to an oid_arrayJeff King, Feb 28, 2024
  4. 3/9 upload-pack: use oidset for deepen_not listJeff King, Feb 28, 2024
  5. 4/9 upload-pack: use a strmap for want-ref linesJeff King, Feb 28, 2024
  6. 5/9 upload-pack: accept only a single packfile-uri lineJeff King, Feb 28, 2024
  7. 6/9 upload-pack: disallow object-info capability by defaultJeff King, Feb 28, 2024
  8. Patrick SteinhardtMar 4, 2024
  9. Jeff KingMar 4, 2024
  10. Taylor BlauApr 29, 2024
  11. 7/9 upload-pack: always turn off save_commit_bufferJeff King, Feb 28, 2024
  12. 8/9 upload-pack: use PARSE_OBJECT_SKIP_HASH_CHECK in more placesJeff King, Feb 28, 2024
  13. 9/9 upload-pack: free tree buffers after parsingJeff King, Feb 28, 2024
  14. Patrick SteinhardtMar 4, 2024
  15. Jeff KingMar 4, 2024
  16. Patrick SteinhardtMar 4, 2024
  17. Junio C HamanoFeb 28, 2024

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.