Skip to content

Commit 34401b7

Browse files
committed
Merge branch 'jk/chunk-bounds-more'
Code clean-up for jk/chunk-bounds topic. * jk/chunk-bounds-more: commit-graph: mark chunk error messages for translation commit-graph: drop verify_commit_graph_lite() commit-graph: check order while reading fanout chunk commit-graph: use fanout value for graph size commit-graph: abort as soon as we see a bogus chunk commit-graph: clarify missing-chunk error messages commit-graph: drop redundant call to "lite" verification midx: check consistency of fanout table commit-graph: handle overflow in chunk_size checks
2 parents b4e6618 + e020391 commit 34401b7

File tree

4 files changed

+67
-77
lines changed

4 files changed

+67
-77
lines changed

commit-graph.c

Lines changed: 33 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -275,59 +275,27 @@ struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
275275
return ret;
276276
}
277277

278-
static int verify_commit_graph_lite(struct commit_graph *g)
278+
static int graph_read_oid_fanout(const unsigned char *chunk_start,
279+
size_t chunk_size, void *data)
279280
{
281+
struct commit_graph *g = data;
280282
int i;
281283

282-
/*
283-
* Basic validation shared between parse_commit_graph()
284-
* which'll be called every time the graph is used, and the
285-
* much more expensive verify_commit_graph() used by
286-
* "commit-graph verify".
287-
*
288-
* There should only be very basic checks here to ensure that
289-
* we don't e.g. segfault in fill_commit_in_graph(), but
290-
* because this is a very hot codepath nothing that e.g. loops
291-
* over g->num_commits, or runs a checksum on the commit-graph
292-
* itself.
293-
*/
294-
if (!g->chunk_oid_fanout) {
295-
error("commit-graph is missing the OID Fanout chunk");
296-
return 1;
297-
}
298-
if (!g->chunk_oid_lookup) {
299-
error("commit-graph is missing the OID Lookup chunk");
300-
return 1;
301-
}
302-
if (!g->chunk_commit_data) {
303-
error("commit-graph is missing the Commit Data chunk");
304-
return 1;
305-
}
284+
if (chunk_size != 256 * sizeof(uint32_t))
285+
return error(_("commit-graph oid fanout chunk is wrong size"));
286+
g->chunk_oid_fanout = (const uint32_t *)chunk_start;
287+
g->num_commits = ntohl(g->chunk_oid_fanout[255]);
306288

307289
for (i = 0; i < 255; i++) {
308290
uint32_t oid_fanout1 = ntohl(g->chunk_oid_fanout[i]);
309291
uint32_t oid_fanout2 = ntohl(g->chunk_oid_fanout[i + 1]);
310292

311293
if (oid_fanout1 > oid_fanout2) {
312-
error("commit-graph fanout values out of order");
294+
error(_("commit-graph fanout values out of order"));
313295
return 1;
314296
}
315297
}
316-
if (ntohl(g->chunk_oid_fanout[255]) != g->num_commits) {
317-
error("commit-graph oid table and fanout disagree on size");
318-
return 1;
319-
}
320-
321-
return 0;
322-
}
323298

324-
static int graph_read_oid_fanout(const unsigned char *chunk_start,
325-
size_t chunk_size, void *data)
326-
{
327-
struct commit_graph *g = data;
328-
if (chunk_size != 256 * sizeof(uint32_t))
329-
return error("commit-graph oid fanout chunk is wrong size");
330-
g->chunk_oid_fanout = (const uint32_t *)chunk_start;
331299
return 0;
332300
}
333301

@@ -336,16 +304,17 @@ static int graph_read_oid_lookup(const unsigned char *chunk_start,
336304
{
337305
struct commit_graph *g = data;
338306
g->chunk_oid_lookup = chunk_start;
339-
g->num_commits = chunk_size / g->hash_len;
307+
if (chunk_size / g->hash_len != g->num_commits)
308+
return error(_("commit-graph OID lookup chunk is the wrong size"));
340309
return 0;
341310
}
342311

343312
static int graph_read_commit_data(const unsigned char *chunk_start,
344313
size_t chunk_size, void *data)
345314
{
346315
struct commit_graph *g = data;
347-
if (chunk_size != g->num_commits * GRAPH_DATA_WIDTH)
348-
return error("commit-graph commit data chunk is wrong size");
316+
if (chunk_size / GRAPH_DATA_WIDTH != g->num_commits)
317+
return error(_("commit-graph commit data chunk is wrong size"));
349318
g->chunk_commit_data = chunk_start;
350319
return 0;
351320
}
@@ -354,8 +323,8 @@ static int graph_read_generation_data(const unsigned char *chunk_start,
354323
size_t chunk_size, void *data)
355324
{
356325
struct commit_graph *g = data;
357-
if (chunk_size != g->num_commits * sizeof(uint32_t))
358-
return error("commit-graph generations chunk is wrong size");
326+
if (chunk_size / sizeof(uint32_t) != g->num_commits)
327+
return error(_("commit-graph generations chunk is wrong size"));
359328
g->chunk_generation_data = chunk_start;
360329
return 0;
361330
}
@@ -364,8 +333,8 @@ static int graph_read_bloom_index(const unsigned char *chunk_start,
364333
size_t chunk_size, void *data)
365334
{
366335
struct commit_graph *g = data;
367-
if (chunk_size != g->num_commits * 4) {
368-
warning("commit-graph changed-path index chunk is too small");
336+
if (chunk_size / 4 != g->num_commits) {
337+
warning(_("commit-graph changed-path index chunk is too small"));
369338
return -1;
370339
}
371340
g->chunk_bloom_indexes = chunk_start;
@@ -379,8 +348,8 @@ static int graph_read_bloom_data(const unsigned char *chunk_start,
379348
uint32_t hash_version;
380349

381350
if (chunk_size < BLOOMDATA_CHUNK_HEADER_SIZE) {
382-
warning("ignoring too-small changed-path chunk"
383-
" (%"PRIuMAX" < %"PRIuMAX") in commit-graph file",
351+
warning(_("ignoring too-small changed-path chunk"
352+
" (%"PRIuMAX" < %"PRIuMAX") in commit-graph file"),
384353
(uintmax_t)chunk_size,
385354
(uintmax_t)BLOOMDATA_CHUNK_HEADER_SIZE);
386355
return -1;
@@ -462,9 +431,19 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
462431
GRAPH_HEADER_SIZE, graph->num_chunks, 1))
463432
goto free_and_return;
464433

465-
read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph);
466-
read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph);
467-
read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph);
434+
if (read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph)) {
435+
error(_("commit-graph required OID fanout chunk missing or corrupted"));
436+
goto free_and_return;
437+
}
438+
if (read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph)) {
439+
error(_("commit-graph required OID lookup chunk missing or corrupted"));
440+
goto free_and_return;
441+
}
442+
if (read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph)) {
443+
error(_("commit-graph required commit data chunk missing or corrupted"));
444+
goto free_and_return;
445+
}
446+
468447
pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges,
469448
&graph->chunk_extra_edges_size);
470449
pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs,
@@ -499,9 +478,6 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
499478

500479
oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
501480

502-
if (verify_commit_graph_lite(graph))
503-
goto free_and_return;
504-
505481
free_chunkfile(cf);
506482
return graph;
507483

@@ -629,7 +605,7 @@ int open_commit_graph_chain(const char *chain_file,
629605
/* treat empty files the same as missing */
630606
errno = ENOENT;
631607
} else {
632-
warning("commit-graph chain file too small");
608+
warning(_("commit-graph chain file too small"));
633609
errno = EINVAL;
634610
}
635611
return 0;
@@ -970,7 +946,7 @@ static int fill_commit_in_graph(struct repository *r,
970946
parent_data_pos = edge_value & GRAPH_EDGE_LAST_MASK;
971947
do {
972948
if (g->chunk_extra_edges_size / sizeof(uint32_t) <= parent_data_pos) {
973-
error("commit-graph extra-edges pointer out of bounds");
949+
error(_("commit-graph extra-edges pointer out of bounds"));
974950
free_commit_list(item->parents);
975951
item->parents = NULL;
976952
item->object.parsed = 0;
@@ -2690,10 +2666,6 @@ static int verify_one_commit_graph(struct repository *r,
26902666
struct commit *seen_gen_zero = NULL;
26912667
struct commit *seen_gen_non_zero = NULL;
26922668

2693-
verify_commit_graph_error = verify_commit_graph_lite(g);
2694-
if (verify_commit_graph_error)
2695-
return verify_commit_graph_error;
2696-
26972669
if (!commit_graph_checksum_valid(g)) {
26982670
graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
26992671
verify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH;

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/t5318-commit-graph.sh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -540,17 +540,17 @@ test_expect_success 'detect low chunk count' '
540540

541541
test_expect_success 'detect missing OID fanout chunk' '
542542
corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
543-
"missing the OID Fanout chunk"
543+
"commit-graph required OID fanout chunk missing or corrupted"
544544
'
545545

546546
test_expect_success 'detect missing OID lookup chunk' '
547547
corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
548-
"missing the OID Lookup chunk"
548+
"commit-graph required OID lookup chunk missing or corrupted"
549549
'
550550

551551
test_expect_success 'detect missing commit data chunk' '
552552
corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
553-
"missing the Commit Data chunk"
553+
"commit-graph required commit data chunk missing or corrupted"
554554
'
555555

556556
test_expect_success 'detect incorrect fanout' '
@@ -560,7 +560,7 @@ test_expect_success 'detect incorrect fanout' '
560560

561561
test_expect_success 'detect incorrect fanout final value' '
562562
corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
563-
"oid table and fanout disagree on size"
563+
"OID lookup chunk is the wrong size"
564564
'
565565

566566
test_expect_success 'detect incorrect OID order' '
@@ -842,15 +842,16 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
842842
check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
843843
cat >expect.err <<-\EOF &&
844844
error: commit-graph oid fanout chunk is wrong size
845-
error: commit-graph is missing the OID Fanout chunk
845+
error: commit-graph required OID fanout chunk missing or corrupted
846846
EOF
847847
test_cmp expect.err err
848848
'
849849

850850
test_expect_success 'reader notices fanout/lookup table mismatch' '
851851
check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
852852
cat >expect.err <<-\EOF &&
853-
error: commit-graph oid table and fanout disagree on size
853+
error: commit-graph OID lookup chunk is the wrong size
854+
error: commit-graph required OID lookup chunk missing or corrupted
854855
EOF
855856
test_cmp expect.err err
856857
'
@@ -866,6 +867,7 @@ test_expect_success 'reader notices out-of-bounds fanout' '
866867
check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
867868
cat >expect.err <<-\EOF &&
868869
error: commit-graph fanout values out of order
870+
error: commit-graph required OID fanout chunk missing or corrupted
869871
EOF
870872
test_cmp expect.err err
871873
'
@@ -874,7 +876,7 @@ test_expect_success 'reader notices too-small commit data chunk' '
874876
check_corrupt_chunk CDAT clear 00000000 &&
875877
cat >expect.err <<-\EOF &&
876878
error: commit-graph commit data chunk is wrong size
877-
error: commit-graph is missing the Commit Data chunk
879+
error: commit-graph required commit data chunk missing or corrupted
878880
EOF
879881
test_cmp expect.err err
880882
'

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)