commit-graph, fuzz: add fuzzer for commit-graph

Break load_commit_graph_one() into a new function, parse_commit_graph(). The latter function operates on arbitrary buffers, which makes it suitable as a fuzzing target. Since parse_commit_graph() is only called by load_commit_graph_one() (and the fuzzer described below), we omit error messages that would be duplicated by the caller. Adds fuzz-commit-graph.c, which provides a fuzzing entry point compatible with libFuzzer (and possibly other fuzzing engines). Signed-off-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Josh Steadmon committed Jan 15, 2019 at 14:25 UTC aa658574bfcbe03f5703458ac10be1ef3f5f5472
5 files changed +57 -17
.gitignore
+1
@@ -1,3 +1,4 @@
1 +/fuzz-commit-graph
2 /fuzz_corpora
3 /fuzz-pack-headers
4 /fuzz-pack-idx
Makefile
+1
@@ -684,6 +684,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
684
685 ETAGS_TARGET = TAGS
686
687 +FUZZ_OBJS += fuzz-commit-graph.o
688 FUZZ_OBJS += fuzz-pack-headers.o
689 FUZZ_OBJS += fuzz-pack-idx.o
690
commit-graph.c
+36 -17
@@ -84,16 +84,10 @@ static int commit_graph_compatible(struct repository *r)
84 struct commit_graph *load_commit_graph_one(const char *graph_file)
85 {
86 void *graph_map;
87 - const unsigned char *data, *chunk_lookup;
87 size_t graph_size;
88 struct stat st;
90 - uint32_t i;
91 - struct commit_graph *graph;
89 + struct commit_graph *ret;
90 int fd = git_open(graph_file);
93 - uint64_t last_chunk_offset;
94 - uint32_t last_chunk_id;
95 - uint32_t graph_signature;
96 - unsigned char graph_version, hash_version;
91
92 if (fd < 0)
93 return NULL;
@@ -108,27 +102,55 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
102 die(_("graph file %s is too small"), graph_file);
103 }
104 graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
105 + ret = parse_commit_graph(graph_map, fd, graph_size);
106 +
107 + if (!ret) {
108 + munmap(graph_map, graph_size);
109 + close(fd);
110 + exit(1);
111 + }
112 +
113 + return ret;
114 +}
115 +
116 +struct commit_graph *parse_commit_graph(void *graph_map, int fd,
117 + size_t graph_size)
118 +{
119 + const unsigned char *data, *chunk_lookup;
120 + uint32_t i;
121 + struct commit_graph *graph;
122 + uint64_t last_chunk_offset;
123 + uint32_t last_chunk_id;
124 + uint32_t graph_signature;
125 + unsigned char graph_version, hash_version;
126 +
127 + if (!graph_map)
128 + return NULL;
129 +
130 + if (graph_size < GRAPH_MIN_SIZE)
131 + return NULL;
132 +
133 data = (const unsigned char *)graph_map;
134
135 graph_signature = get_be32(data);
136 if (graph_signature != GRAPH_SIGNATURE) {
137 error(_("graph signature %X does not match signature %X"),
138 graph_signature, GRAPH_SIGNATURE);
117 - goto cleanup_fail;
139 + return NULL;
140 }
141
142 graph_version = *(unsigned char*)(data + 4);
143 if (graph_version != GRAPH_VERSION) {
144 error(_("graph version %X does not match version %X"),
145 graph_version, GRAPH_VERSION);
124 - goto cleanup_fail;
146 + return NULL;
147 }
148
149 hash_version = *(unsigned char*)(data + 5);
150 if (hash_version != GRAPH_OID_VERSION) {
151 error(_("hash version %X does not match version %X"),
152 hash_version, GRAPH_OID_VERSION);
131 - goto cleanup_fail;
153 + return NULL;
154 }
155
156 graph = alloc_commit_graph();
@@ -152,7 +174,8 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
174 if (chunk_offset > graph_size - GIT_MAX_RAWSZ) {
175 error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
176 (uint32_t)chunk_offset);
155 - goto cleanup_fail;
177 + free(graph);
178 + return NULL;
179 }
180
181 switch (chunk_id) {
@@ -187,7 +210,8 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
210
211 if (chunk_repeated) {
212 error(_("chunk id %08x appears multiple times"), chunk_id);
190 - goto cleanup_fail;
213 + free(graph);
214 + return NULL;
215 }
216
217 if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
@@ -201,11 +225,6 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
225 }
226
227 return graph;
204 -
205 -cleanup_fail:
206 - munmap(graph_map, graph_size);
207 - close(fd);
208 - exit(1);
228 }
229
230 static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
commit-graph.h
+3
@@ -54,6 +54,9 @@ struct commit_graph {
54
55 struct commit_graph *load_commit_graph_one(const char *graph_file);
56
57 +struct commit_graph *parse_commit_graph(void *graph_map, int fd,
58 + size_t graph_size);
59 +
60 /*
61 * Return 1 if and only if the repository has a commit-graph
62 * file and generation numbers are computed in that file.
fuzz-commit-graph.c new
+16
@@ -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 +}