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-graphmaint
						commit
						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 && | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano