Skip to content

Commit 879a8d4

Browse files
committed
Merge branch 'jk/detect-truncated-zlib-input'
A regression in Git 2.12 era made "git fsck" fall into an infinite loop while processing truncated loose objects. * jk/detect-truncated-zlib-input: cat-file: handle streaming failures consistently check_stream_sha1(): handle input underflow t1450: check large blob in trailing-garbage test
2 parents fd7761a + 18ad13e commit 879a8d4

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

builtin/cat-file.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ static int filter_object(const char *path, unsigned mode,
5050
return 0;
5151
}
5252

53+
static int stream_blob(const struct object_id *oid)
54+
{
55+
if (stream_blob_to_fd(1, oid, NULL, 0))
56+
die("unable to stream %s to stdout", oid_to_hex(oid));
57+
return 0;
58+
}
59+
5360
static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
5461
int unknown_type)
5562
{
@@ -132,7 +139,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
132139
}
133140

134141
if (type == OBJ_BLOB)
135-
return stream_blob_to_fd(1, &oid, NULL, 0);
142+
return stream_blob(&oid);
136143
buf = read_object_file(&oid, &type, &size);
137144
if (!buf)
138145
die("Cannot read object %s", obj_name);
@@ -155,7 +162,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
155162
oidcpy(&blob_oid, &oid);
156163

157164
if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
158-
return stream_blob_to_fd(1, &blob_oid, NULL, 0);
165+
return stream_blob(&blob_oid);
159166
/*
160167
* we attempted to dereference a tag to a blob
161168
* and failed; there may be new dereference
@@ -319,8 +326,9 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
319326
BUG("invalid cmdmode: %c", opt->cmdmode);
320327
batch_write(opt, contents, size);
321328
free(contents);
322-
} else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
323-
die("unable to stream %s to stdout", oid_to_hex(oid));
329+
} else {
330+
stream_blob(oid);
331+
}
324332
}
325333
else {
326334
enum object_type type;

sha1-file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2199,7 +2199,8 @@ static int check_stream_sha1(git_zstream *stream,
21992199
* see the comment in unpack_sha1_rest for details.
22002200
*/
22012201
while (total_read <= size &&
2202-
(status == Z_OK || status == Z_BUF_ERROR)) {
2202+
(status == Z_OK ||
2203+
(status == Z_BUF_ERROR && !stream->avail_out))) {
22032204
stream->next_out = buf;
22042205
stream->avail_out = sizeof(buf);
22052206
if (size - total_read < stream->avail_out)

t/t1450-fsck.sh

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,16 +673,35 @@ test_expect_success 'fsck detects trailing loose garbage (commit)' '
673673
test_i18ngrep "garbage.*$commit" out
674674
'
675675

676-
test_expect_success 'fsck detects trailing loose garbage (blob)' '
676+
test_expect_success 'fsck detects trailing loose garbage (large blob)' '
677677
blob=$(echo trailing | git hash-object -w --stdin) &&
678678
file=$(sha1_file $blob) &&
679679
test_when_finished "remove_object $blob" &&
680680
chmod +w "$file" &&
681681
echo garbage >>"$file" &&
682-
test_must_fail git fsck 2>out &&
682+
test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
683683
test_i18ngrep "garbage.*$blob" out
684684
'
685685

686+
test_expect_success 'fsck detects truncated loose object' '
687+
# make it big enough that we know we will truncate in the data
688+
# portion, not the header
689+
test-tool genrandom truncate 4096 >file &&
690+
blob=$(git hash-object -w file) &&
691+
file=$(sha1_file $blob) &&
692+
test_when_finished "remove_object $blob" &&
693+
test_copy_bytes 1024 <"$file" >tmp &&
694+
rm "$file" &&
695+
mv -f tmp "$file" &&
696+
697+
# check both regular and streaming code paths
698+
test_must_fail git fsck 2>out &&
699+
test_i18ngrep corrupt.*$blob out &&
700+
701+
test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
702+
test_i18ngrep corrupt.*$blob out
703+
'
704+
686705
# for each of type, we have one version which is referenced by another object
687706
# (and so while unreachable, not dangling), and another variant which really is
688707
# dangling.

0 commit comments

Comments
 (0)