Skip to content

Commit 19a504d

Browse files
committed
Merge branch 'js/commit-graph-chunk-table-fix'
The codepath to read from the commit-graph file attempted to read past the end of it when the file's table-of-contents was corrupt. * js/commit-graph-chunk-table-fix: Makefile: correct example fuzz build commit-graph: fix buffer read-overflow commit-graph, fuzz: add fuzzer for commit-graph
2 parents 40b8ba2 + 8b7c2ee commit 19a504d

File tree

6 files changed

+83
-23
lines changed

6 files changed

+83
-23
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/fuzz-commit-graph
12
/fuzz_corpora
23
/fuzz-pack-headers
34
/fuzz-pack-idx

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
690690

691691
ETAGS_TARGET = TAGS
692692

693+
FUZZ_OBJS += fuzz-commit-graph.o
693694
FUZZ_OBJS += fuzz-pack-headers.o
694695
FUZZ_OBJS += fuzz-pack-idx.o
695696

@@ -3125,7 +3126,7 @@ cover_db_html: cover_db
31253126
# An example command to build against libFuzzer from LLVM 4.0.0:
31263127
#
31273128
# make CC=clang CXX=clang++ \
3128-
# FUZZ_CXXFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
3129+
# CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
31293130
# LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
31303131
# fuzz-all
31313132
#

commit-graph.c

+48-19
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,10 @@ static int commit_graph_compatible(struct repository *r)
8383
struct commit_graph *load_commit_graph_one(const char *graph_file)
8484
{
8585
void *graph_map;
86-
const unsigned char *data, *chunk_lookup;
8786
size_t graph_size;
8887
struct stat st;
89-
uint32_t i;
90-
struct commit_graph *graph;
88+
struct commit_graph *ret;
9189
int fd = git_open(graph_file);
92-
uint64_t last_chunk_offset;
93-
uint32_t last_chunk_id;
94-
uint32_t graph_signature;
95-
unsigned char graph_version, hash_version;
9690

9791
if (fd < 0)
9892
return NULL;
@@ -107,27 +101,55 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
107101
die(_("graph file %s is too small"), graph_file);
108102
}
109103
graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
104+
ret = parse_commit_graph(graph_map, fd, graph_size);
105+
106+
if (!ret) {
107+
munmap(graph_map, graph_size);
108+
close(fd);
109+
exit(1);
110+
}
111+
112+
return ret;
113+
}
114+
115+
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
116+
size_t graph_size)
117+
{
118+
const unsigned char *data, *chunk_lookup;
119+
uint32_t i;
120+
struct commit_graph *graph;
121+
uint64_t last_chunk_offset;
122+
uint32_t last_chunk_id;
123+
uint32_t graph_signature;
124+
unsigned char graph_version, hash_version;
125+
126+
if (!graph_map)
127+
return NULL;
128+
129+
if (graph_size < GRAPH_MIN_SIZE)
130+
return NULL;
131+
110132
data = (const unsigned char *)graph_map;
111133

112134
graph_signature = get_be32(data);
113135
if (graph_signature != GRAPH_SIGNATURE) {
114136
error(_("graph signature %X does not match signature %X"),
115137
graph_signature, GRAPH_SIGNATURE);
116-
goto cleanup_fail;
138+
return NULL;
117139
}
118140

119141
graph_version = *(unsigned char*)(data + 4);
120142
if (graph_version != GRAPH_VERSION) {
121143
error(_("graph version %X does not match version %X"),
122144
graph_version, GRAPH_VERSION);
123-
goto cleanup_fail;
145+
return NULL;
124146
}
125147

126148
hash_version = *(unsigned char*)(data + 5);
127149
if (hash_version != oid_version()) {
128150
error(_("hash version %X does not match version %X"),
129151
hash_version, oid_version());
130-
goto cleanup_fail;
152+
return NULL;
131153
}
132154

133155
graph = alloc_commit_graph();
@@ -142,16 +164,27 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
142164
last_chunk_offset = 8;
143165
chunk_lookup = data + 8;
144166
for (i = 0; i < graph->num_chunks; i++) {
145-
uint32_t chunk_id = get_be32(chunk_lookup + 0);
146-
uint64_t chunk_offset = get_be64(chunk_lookup + 4);
167+
uint32_t chunk_id;
168+
uint64_t chunk_offset;
147169
int chunk_repeated = 0;
148170

171+
if (data + graph_size - chunk_lookup <
172+
GRAPH_CHUNKLOOKUP_WIDTH) {
173+
error(_("chunk lookup table entry missing; graph file may be incomplete"));
174+
free(graph);
175+
return NULL;
176+
}
177+
178+
chunk_id = get_be32(chunk_lookup + 0);
179+
chunk_offset = get_be64(chunk_lookup + 4);
180+
149181
chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
150182

151183
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
152184
error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
153185
(uint32_t)chunk_offset);
154-
goto cleanup_fail;
186+
free(graph);
187+
return NULL;
155188
}
156189

157190
switch (chunk_id) {
@@ -186,7 +219,8 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
186219

187220
if (chunk_repeated) {
188221
error(_("chunk id %08x appears multiple times"), chunk_id);
189-
goto cleanup_fail;
222+
free(graph);
223+
return NULL;
190224
}
191225

192226
if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
@@ -200,11 +234,6 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
200234
}
201235

202236
return graph;
203-
204-
cleanup_fail:
205-
munmap(graph_map, graph_size);
206-
close(fd);
207-
exit(1);
208237
}
209238

210239
static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)

commit-graph.h

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ struct commit_graph {
5454

5555
struct commit_graph *load_commit_graph_one(const char *graph_file);
5656

57+
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
58+
size_t graph_size);
59+
5760
/*
5861
* Return 1 if and only if the repository has a commit-graph
5962
* file and generation numbers are computed in that file.

fuzz-commit-graph.c

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "commit-graph.h"
2+
3+
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
4+
size_t graph_size);
5+
6+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
7+
8+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
9+
{
10+
struct commit_graph *g;
11+
12+
g = parse_commit_graph((void *)data, -1, size);
13+
free(g);
14+
15+
return 0;
16+
}

t/t5318-commit-graph.sh

+13-3
Original file line numberDiff line numberDiff line change
@@ -366,21 +366,26 @@ GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
366366
GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
367367
GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
368368

369-
# usage: corrupt_graph_and_verify <position> <data> <string>
369+
# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
370370
# Manipulates the commit-graph file at the position
371-
# by inserting the data, then runs 'git commit-graph verify'
371+
# by inserting the data, optionally zeroing the file
372+
# starting at <zero_pos>, then runs 'git commit-graph verify'
372373
# and places the output in the file 'err'. Test 'err' for
373374
# the given string.
374375
corrupt_graph_and_verify() {
375376
pos=$1
376377
data="${2:-\0}"
377378
grepstr=$3
378379
cd "$TRASH_DIRECTORY/full" &&
380+
orig_size=$(wc -c < $objdir/info/commit-graph) &&
381+
zero_pos=${4:-${orig_size}} &&
379382
test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
380383
cp $objdir/info/commit-graph commit-graph-backup &&
381384
printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
385+
dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=0 &&
386+
dd if=/dev/zero of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=$(($orig_size - $zero_pos)) &&
382387
test_must_fail git commit-graph verify 2>test_err &&
383-
grep -v "^+" test_err >err
388+
grep -v "^+" test_err >err &&
384389
test_i18ngrep "$grepstr" err
385390
}
386391

@@ -484,6 +489,11 @@ test_expect_success 'detect invalid checksum hash' '
484489
"incorrect checksum"
485490
'
486491

492+
test_expect_success 'detect incorrect chunk count' '
493+
corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
494+
"chunk lookup table entry missing" $GRAPH_CHUNK_LOOKUP_OFFSET
495+
'
496+
487497
test_expect_success 'git fsck (checks commit-graph)' '
488498
cd "$TRASH_DIRECTORY/full" &&
489499
git fsck &&

0 commit comments

Comments
 (0)