зеркало из https://github.com/microsoft/git.git
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
This commit is contained in:
Коммит
19a504d92b
|
@ -1,3 +1,4 @@
|
|||
/fuzz-commit-graph
|
||||
/fuzz_corpora
|
||||
/fuzz-pack-headers
|
||||
/fuzz-pack-idx
|
||||
|
|
3
Makefile
3
Makefile
|
@ -690,6 +690,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
|
|||
|
||||
ETAGS_TARGET = TAGS
|
||||
|
||||
FUZZ_OBJS += fuzz-commit-graph.o
|
||||
FUZZ_OBJS += fuzz-pack-headers.o
|
||||
FUZZ_OBJS += fuzz-pack-idx.o
|
||||
|
||||
|
@ -3125,7 +3126,7 @@ cover_db_html: cover_db
|
|||
# An example command to build against libFuzzer from LLVM 4.0.0:
|
||||
#
|
||||
# make CC=clang CXX=clang++ \
|
||||
# FUZZ_CXXFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
|
||||
# CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
|
||||
# LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
|
||||
# fuzz-all
|
||||
#
|
||||
|
|
|
@ -83,16 +83,10 @@ static int commit_graph_compatible(struct repository *r)
|
|||
struct commit_graph *load_commit_graph_one(const char *graph_file)
|
||||
{
|
||||
void *graph_map;
|
||||
const unsigned char *data, *chunk_lookup;
|
||||
size_t graph_size;
|
||||
struct stat st;
|
||||
uint32_t i;
|
||||
struct commit_graph *graph;
|
||||
struct commit_graph *ret;
|
||||
int fd = git_open(graph_file);
|
||||
uint64_t last_chunk_offset;
|
||||
uint32_t last_chunk_id;
|
||||
uint32_t graph_signature;
|
||||
unsigned char graph_version, hash_version;
|
||||
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
@ -107,27 +101,55 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
|
|||
die(_("graph file %s is too small"), graph_file);
|
||||
}
|
||||
graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
ret = parse_commit_graph(graph_map, fd, graph_size);
|
||||
|
||||
if (!ret) {
|
||||
munmap(graph_map, graph_size);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
|
||||
size_t graph_size)
|
||||
{
|
||||
const unsigned char *data, *chunk_lookup;
|
||||
uint32_t i;
|
||||
struct commit_graph *graph;
|
||||
uint64_t last_chunk_offset;
|
||||
uint32_t last_chunk_id;
|
||||
uint32_t graph_signature;
|
||||
unsigned char graph_version, hash_version;
|
||||
|
||||
if (!graph_map)
|
||||
return NULL;
|
||||
|
||||
if (graph_size < GRAPH_MIN_SIZE)
|
||||
return NULL;
|
||||
|
||||
data = (const unsigned char *)graph_map;
|
||||
|
||||
graph_signature = get_be32(data);
|
||||
if (graph_signature != GRAPH_SIGNATURE) {
|
||||
error(_("graph signature %X does not match signature %X"),
|
||||
graph_signature, GRAPH_SIGNATURE);
|
||||
goto cleanup_fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
graph_version = *(unsigned char*)(data + 4);
|
||||
if (graph_version != GRAPH_VERSION) {
|
||||
error(_("graph version %X does not match version %X"),
|
||||
graph_version, GRAPH_VERSION);
|
||||
goto cleanup_fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash_version = *(unsigned char*)(data + 5);
|
||||
if (hash_version != oid_version()) {
|
||||
error(_("hash version %X does not match version %X"),
|
||||
hash_version, oid_version());
|
||||
goto cleanup_fail;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
graph = alloc_commit_graph();
|
||||
|
@ -142,16 +164,27 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
|
|||
last_chunk_offset = 8;
|
||||
chunk_lookup = data + 8;
|
||||
for (i = 0; i < graph->num_chunks; i++) {
|
||||
uint32_t chunk_id = get_be32(chunk_lookup + 0);
|
||||
uint64_t chunk_offset = get_be64(chunk_lookup + 4);
|
||||
uint32_t chunk_id;
|
||||
uint64_t chunk_offset;
|
||||
int chunk_repeated = 0;
|
||||
|
||||
if (data + graph_size - chunk_lookup <
|
||||
GRAPH_CHUNKLOOKUP_WIDTH) {
|
||||
error(_("chunk lookup table entry missing; graph file may be incomplete"));
|
||||
free(graph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chunk_id = get_be32(chunk_lookup + 0);
|
||||
chunk_offset = get_be64(chunk_lookup + 4);
|
||||
|
||||
chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
|
||||
|
||||
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
|
||||
error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
|
||||
(uint32_t)chunk_offset);
|
||||
goto cleanup_fail;
|
||||
free(graph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (chunk_id) {
|
||||
|
@ -186,7 +219,8 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
|
|||
|
||||
if (chunk_repeated) {
|
||||
error(_("chunk id %08x appears multiple times"), chunk_id);
|
||||
goto cleanup_fail;
|
||||
free(graph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
|
||||
|
@ -200,11 +234,6 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
|
|||
}
|
||||
|
||||
return graph;
|
||||
|
||||
cleanup_fail:
|
||||
munmap(graph_map, graph_size);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
|
||||
|
|
|
@ -54,6 +54,9 @@ struct commit_graph {
|
|||
|
||||
struct commit_graph *load_commit_graph_one(const char *graph_file);
|
||||
|
||||
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
|
||||
size_t graph_size);
|
||||
|
||||
/*
|
||||
* Return 1 if and only if the repository has a commit-graph
|
||||
* file and generation numbers are computed in that file.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#include "commit-graph.h"
|
||||
|
||||
struct commit_graph *parse_commit_graph(void *graph_map, int fd,
|
||||
size_t graph_size);
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
struct commit_graph *g;
|
||||
|
||||
g = parse_commit_graph((void *)data, -1, size);
|
||||
free(g);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -366,9 +366,10 @@ GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
|
|||
GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
|
||||
GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
|
||||
|
||||
# usage: corrupt_graph_and_verify <position> <data> <string>
|
||||
# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
|
||||
# Manipulates the commit-graph file at the position
|
||||
# by inserting the data, then runs 'git commit-graph verify'
|
||||
# by inserting the data, optionally zeroing the file
|
||||
# starting at <zero_pos>, then runs 'git commit-graph verify'
|
||||
# and places the output in the file 'err'. Test 'err' for
|
||||
# the given string.
|
||||
corrupt_graph_and_verify() {
|
||||
|
@ -376,11 +377,15 @@ corrupt_graph_and_verify() {
|
|||
data="${2:-\0}"
|
||||
grepstr=$3
|
||||
cd "$TRASH_DIRECTORY/full" &&
|
||||
orig_size=$(wc -c < $objdir/info/commit-graph) &&
|
||||
zero_pos=${4:-${orig_size}} &&
|
||||
test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
|
||||
cp $objdir/info/commit-graph commit-graph-backup &&
|
||||
printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
|
||||
dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=0 &&
|
||||
dd if=/dev/zero of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" count=$(($orig_size - $zero_pos)) &&
|
||||
test_must_fail git commit-graph verify 2>test_err &&
|
||||
grep -v "^+" test_err >err
|
||||
grep -v "^+" test_err >err &&
|
||||
test_i18ngrep "$grepstr" err
|
||||
}
|
||||
|
||||
|
@ -484,6 +489,11 @@ test_expect_success 'detect invalid checksum hash' '
|
|||
"incorrect checksum"
|
||||
'
|
||||
|
||||
test_expect_success 'detect incorrect chunk count' '
|
||||
corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
|
||||
"chunk lookup table entry missing" $GRAPH_CHUNK_LOOKUP_OFFSET
|
||||
'
|
||||
|
||||
test_expect_success 'git fsck (checks commit-graph)' '
|
||||
cd "$TRASH_DIRECTORY/full" &&
|
||||
git fsck &&
|
||||
|
|
Загрузка…
Ссылка в новой задаче