Skip to content

Commit 9d78fb0

Browse files
peffgitster
authored andcommitted
midx: check consistency of fanout table
The commit-graph, midx, and pack idx on-disk formats all have oid fanout tables which are fed to bsearch_hash(). If these tables do not increase monotonically, then the binary search may not only produce bogus values, it may cause out of bounds reads. We fixed this for commit graphs in 4169d89 (commit-graph: check consistency of fanout table, 2023-10-09). That commit argued that we did not need to do the same for midx and pack idx files, because they already did this check. However, that is wrong. We _do_ check the fanout table for pack idx files when we load them, but we only do so for midx files when running "git multi-pack-index verify". So it is possible to get an out-of-bounds read by running a normal command with a specially crafted midx file. Let's fix this using the same solution (and roughly the same test) we did for the commit-graph in 4169d89. This replaces the same check from "multi-pack-index verify", because verify uses the same read routines, we'd bail on reading the midx much sooner now. So let's make sure to copy its verbose error message. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4bc6d43 commit 9d78fb0

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

midx.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,24 @@ void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
6464
static int midx_read_oid_fanout(const unsigned char *chunk_start,
6565
size_t chunk_size, void *data)
6666
{
67+
int i;
6768
struct multi_pack_index *m = data;
6869
m->chunk_oid_fanout = (uint32_t *)chunk_start;
6970

7071
if (chunk_size != 4 * 256) {
7172
error(_("multi-pack-index OID fanout is of the wrong size"));
7273
return 1;
7374
}
75+
for (i = 0; i < 255; i++) {
76+
uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
77+
uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i+1]);
78+
79+
if (oid_fanout1 > oid_fanout2) {
80+
error(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
81+
i, oid_fanout1, oid_fanout2, i + 1);
82+
return 1;
83+
}
84+
}
7485
m->num_objects = ntohl(m->chunk_oid_fanout[255]);
7586
return 0;
7687
}
@@ -1782,15 +1793,6 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
17821793
}
17831794
stop_progress(&progress);
17841795

1785-
for (i = 0; i < 255; i++) {
1786-
uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
1787-
uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i + 1]);
1788-
1789-
if (oid_fanout1 > oid_fanout2)
1790-
midx_report(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
1791-
i, oid_fanout1, oid_fanout2, i + 1);
1792-
}
1793-
17941796
if (m->num_objects == 0) {
17951797
midx_report(_("the midx contains no oid"));
17961798
/*

t/t5319-multi-pack-index.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,4 +1157,18 @@ test_expect_success 'reader notices too-small revindex chunk' '
11571157
test_cmp expect.err err
11581158
'
11591159

1160+
test_expect_success 'reader notices out-of-bounds fanout' '
1161+
# This is similar to the out-of-bounds fanout test in t5318. The values
1162+
# in adjacent entries should be large but not identical (they
1163+
# are used as hi/lo starts for a binary search, which would then abort
1164+
# immediately).
1165+
corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
1166+
test_must_fail git log 2>err &&
1167+
cat >expect <<-\EOF &&
1168+
error: oid fanout out of order: fanout[254] = fe000000 > 5c = fanout[255]
1169+
fatal: multi-pack-index required OID fanout chunk missing or corrupted
1170+
EOF
1171+
test_cmp expect err
1172+
'
1173+
11601174
test_done

0 commit comments

Comments
 (0)