summaryrefslogtreecommitdiff
path: root/object-file.c
diff options
context:
space:
mode:
authorJeff King <peff@peff.net>2025-02-25 01:29:58 -0500
committerJunio C Hamano <gitster@pobox.com>2025-02-25 10:24:55 -0800
commitb748ddb7a470b952b8a5596649f7433278d7f2c4 (patch)
treecc483d6d1bec14e1819f13129f7594a23ee0afbb /object-file.c
parente7ac344d7018d4537eda29d5a09c047a35f27364 (diff)
unpack_loose_header(): fix infinite loop on broken zlib input
When reading a loose object, we first try to expand the first 32 bytes to read the type+size header. This is enough for any of the normal Git types. But since 46f034483e (sha1_file: support reading from a loose object of unknown type, 2015-05-03), the caller can also ask us to parse any unknown names, which can be much longer. In this case we keep inflating until we find the NUL at the end of the header, or hit Z_STREAM_END. But what if zlib can't make forward progress? For example, if the loose object file is truncated, we'll have no more data to feed it. It will return Z_BUF_ERROR, and we'll just loop infinitely, calling git_inflate() over and over but never seeing new bytes nor an end-of-stream marker. We can fix this by only looping when we think we can make forward progress. This will always be Z_OK in this case. In other code we might also be able to continue on Z_BUF_ERROR, but: - We will never see Z_BUF_ERROR because the output buffer is full; we always feed a fresh 32-byte buffer on each call to git_inflate(). - We may see Z_BUF_ERROR if we run out of input. But since we've fed the whole mmap'd buffer to zlib, if it runs out of input there is nothing more we can do. So if we don't see Z_OK (and didn't see the end-of-header NUL, otherwise we'd have broken out of the loop), then we should stop looping and return an error. The test case shows an example where the input is truncated (which gives us the input Z_BUF_ERROR case above). Although we do operate on objects we might get from an untrusted remote, I don't think the security implications of this bug are too great. It can only trigger if both of these are true: - You're reading a loose object whose on-disk representation was written by an attacker. So fetching an object (or receiving a push) are mostly OK, because even with unpack-objects it is our local, trusted code that writes out the object file. The exception may be fetching from an untrusted local repo, or using dumb-http, which copies objects verbatim. But... - The only code path which triggers the inflate loop is cat-file's --allow-unknown-type option. This is unlikely to be called at all outside of debugging. But I also suspect that objects with non-standard types (or that are truncated) would not survive the usual fetch/receive checks in the first place. So I think it would be quite hard to trick somebody into running the infinite loop, and we can just fix the bug. Co-authored-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'object-file.c')
-rw-r--r--object-file.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/object-file.c b/object-file.c
index b1c33dbb63..5086633e21 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1307,7 +1307,7 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
return 0;
- } while (status != Z_STREAM_END);
+ } while (status == Z_OK);
return ULHR_BAD;
}