fuzz: add fuzz testing for packfile indices.

Breaks the majority of check_packed_git_idx() into a separate function, load_idx(). The latter function operates on arbitrary buffers, which makes it suitable as a fuzzing test target. Signed-off-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Josh Steadmon committed Oct 12, 2018 at 17:58 UTC 1127a98ccea2a911d2043dcc9ce301e080a3cc39
5 files changed +53 -19
.gitignore
+1
@@ -1,5 +1,6 @@
1 /fuzz_corpora
2 /fuzz-pack-headers
3 +/fuzz-pack-idx
4 /GIT-BUILD-OPTIONS
5 /GIT-CFLAGS
6 /GIT-LDFLAGS
Makefile
+1
@@ -685,6 +685,7 @@ SCRIPTS = $(SCRIPT_SH_INS) \
685 ETAGS_TARGET = TAGS
686
687 FUZZ_OBJS += fuzz-pack-headers.o
688 +FUZZ_OBJS += fuzz-pack-idx.o
689
690 # Always build fuzz objects even if not testing, to prevent bit-rot.
691 all:: $(FUZZ_OBJS)
fuzz-pack-idx.c new
+13
@@ -0,0 +1,13 @@
1 +#include "object-store.h"
2 +#include "packfile.h"
3 +
4 +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
5 +
6 +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
7 +{
8 + struct packed_git p;
9 +
10 + load_idx("fuzz-input", GIT_SHA1_RAWSZ, (void *)data, size, &p);
11 +
12 + return 0;
13 +}
packfile.c
+25 -19
@@ -80,10 +80,8 @@ void pack_report(void)
80 static int check_packed_git_idx(const char *path, struct packed_git *p)
81 {
82 void *idx_map;
83 - struct pack_idx_header *hdr;
83 size_t idx_size;
85 - uint32_t version, nr, i, *index;
86 - int fd = git_open(path);
84 + int fd = git_open(path), ret;
85 struct stat st;
86 const unsigned int hashsz = the_hash_algo->rawsz;
87
@@ -101,16 +99,32 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
99 idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
100 close(fd);
101
104 - hdr = idx_map;
102 + ret = load_idx(path, hashsz, idx_map, idx_size, p);
103 +
104 + if (ret)
105 + munmap(idx_map, idx_size);
106 +
107 + return ret;
108 +}
109 +
110 +int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
111 + size_t idx_size, struct packed_git *p)
112 +{
113 + struct pack_idx_header *hdr = idx_map;
114 + uint32_t version, nr, i, *index;
115 +
116 + if (idx_size < 4 * 256 + hashsz + hashsz)
117 + return error("index file %s is too small", path);
118 + if (idx_map == NULL)
119 + return error("empty data");
120 +
121 if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
122 version = ntohl(hdr->idx_version);
107 - if (version < 2 || version > 2) {
108 - munmap(idx_map, idx_size);
123 + if (version < 2 || version > 2)
124 return error("index file %s is version %"PRIu32
125 " and is not supported by this binary"
126 " (try upgrading GIT to a newer version)",
127 path, version);
113 - }
128 } else
129 version = 1;
130
@@ -120,10 +134,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
134 index += 2; /* skip index header */
135 for (i = 0; i < 256; i++) {
136 uint32_t n = ntohl(index[i]);
123 - if (n < nr) {
124 - munmap(idx_map, idx_size);
137 + if (n < nr)
138 return error("non-monotonic index %s", path);
126 - }
139 nr = n;
140 }
141
@@ -135,10 +147,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
147 * - hash of the packfile
148 * - file checksum
149 */
138 - if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
139 - munmap(idx_map, idx_size);
150 + if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
151 return error("wrong index v1 file size in %s", path);
141 - }
152 } else if (version == 2) {
153 /*
154 * Minimum size:
@@ -157,20 +167,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
167 unsigned long max_size = min_size;
168 if (nr)
169 max_size += (nr - 1)*8;
160 - if (idx_size < min_size || idx_size > max_size) {
161 - munmap(idx_map, idx_size);
170 + if (idx_size < min_size || idx_size > max_size)
171 return error("wrong index v2 file size in %s", path);
163 - }
172 if (idx_size != min_size &&
173 /*
174 * make sure we can deal with large pack offsets.
175 * 31-bit signed offset won't be enough, neither
176 * 32-bit unsigned one will be.
177 */
170 - (sizeof(off_t) <= 4)) {
171 - munmap(idx_map, idx_size);
178 + (sizeof(off_t) <= 4))
179 return error("pack too large for current definition of off_t in %s", path);
173 - }
180 }
181
182 p->index_version = version;
packfile.h
+13
@@ -164,4 +164,17 @@ extern int has_pack_index(const unsigned char *sha1);
164 */
165 extern int is_promisor_object(const struct object_id *oid);
166
167 +/*
168 + * Expose a function for fuzz testing.
169 + *
170 + * load_idx() parses a block of memory as a packfile index and puts the results
171 + * into a struct packed_git.
172 + *
173 + * This function should not be used directly. It is exposed here only so that we
174 + * have a convenient entry-point for fuzz testing. For real uses, you should
175 + * probably use open_pack_index() or parse_pack_index() instead.
176 + */
177 +extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
178 + size_t idx_size, struct packed_git *p);
179 +
180 #endif