Merge branch 'js/fuzzer'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Oct 2018 05:22:13 +0000 (14:22 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Oct 2018 05:22:14 +0000 (14:22 +0900)
An experiment to fuzz test a few areas, hopefully we can gain more
coverage to various areas.

* js/fuzzer:
fuzz: add fuzz testing for packfile indices.
fuzz: add basic fuzz testing target.

.gitignore
Makefile
fuzz-pack-headers.c [new file with mode: 0644]
fuzz-pack-idx.c [new file with mode: 0644]
packfile.c
packfile.h
index 9d1363a1ebce8432c15f610aa7af9520e3e2bb12..64b3377d40c35a68a10d5f9e3aa6f3a6fa6aece2 100644 (file)
@@ -1,3 +1,6 @@
+/fuzz_corpora
+/fuzz-pack-headers
+/fuzz-pack-idx
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
 /GIT-LDFLAGS
index d18ab0fe78d7a9ffdfa4068bf9ffbe5d79a56434..b08d5ea258c69a78745dfa73fe698c11d021858a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -590,6 +590,8 @@ XDIFF_OBJS =
 VCSSVN_OBJS =
 GENERATED_H =
 EXTRA_CPPFLAGS =
+FUZZ_OBJS =
+FUZZ_PROGRAMS =
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
@@ -682,6 +684,14 @@ SCRIPTS = $(SCRIPT_SH_INS) \
 
 ETAGS_TARGET = TAGS
 
+FUZZ_OBJS += fuzz-pack-headers.o
+FUZZ_OBJS += fuzz-pack-idx.o
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+
 # Empty...
 EXTRA_PROGRAMS =
 
@@ -2253,6 +2263,7 @@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
+       $(FUZZ_OBJS) \
        common-main.o \
        git.o
 ifndef NO_CURL
@@ -2951,6 +2962,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+       $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
@@ -3075,3 +3087,24 @@ cover_db: coverage-report
 cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
 
+
+### Fuzz testing
+#
+# Building fuzz targets generally requires a special set of compiler flags that
+# are not necessarily appropriate for general builds, and that vary greatly
+# depending on the compiler version used.
+#
+# An example command to build against libFuzzer from LLVM 4.0.0:
+#
+# make CC=clang CXX=clang++ \
+#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+#      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
+#      fuzz-all
+#
+.PHONY: fuzz-all
+
+$(FUZZ_PROGRAMS): all
+       $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
+               $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+
+fuzz-all: $(FUZZ_PROGRAMS)
diff --git a/fuzz-pack-headers.c b/fuzz-pack-headers.c
new file mode 100644 (file)
index 0000000..99da1d0
--- /dev/null
@@ -0,0 +1,14 @@
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       enum object_type type;
+       unsigned long len;
+
+       unpack_object_header_buffer((const unsigned char *)data,
+                                   (unsigned long)size, &type, &len);
+
+       return 0;
+}
diff --git a/fuzz-pack-idx.c b/fuzz-pack-idx.c
new file mode 100644 (file)
index 0000000..0c3d777
--- /dev/null
@@ -0,0 +1,13 @@
+#include "object-store.h"
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct packed_git p;
+
+       load_idx("fuzz-input", GIT_SHA1_RAWSZ, (void *)data, size, &p);
+
+       return 0;
+}
index 841b36182fcd938da5ee0a4b065e0717ef4a18ed..86074a76e965087ed826e3170b867e995fd9e9fe 100644 (file)
@@ -80,10 +80,8 @@ void pack_report(void)
 static int check_packed_git_idx(const char *path, struct packed_git *p)
 {
        void *idx_map;
-       struct pack_idx_header *hdr;
        size_t idx_size;
-       uint32_t version, nr, i, *index;
-       int fd = git_open(path);
+       int fd = git_open(path), ret;
        struct stat st;
        const unsigned int hashsz = the_hash_algo->rawsz;
 
@@ -101,16 +99,32 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
        idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
 
-       hdr = idx_map;
+       ret = load_idx(path, hashsz, idx_map, idx_size, p);
+
+       if (ret)
+               munmap(idx_map, idx_size);
+
+       return ret;
+}
+
+int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+            size_t idx_size, struct packed_git *p)
+{
+       struct pack_idx_header *hdr = idx_map;
+       uint32_t version, nr, i, *index;
+
+       if (idx_size < 4 * 256 + hashsz + hashsz)
+               return error("index file %s is too small", path);
+       if (idx_map == NULL)
+               return error("empty data");
+
        if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
                version = ntohl(hdr->idx_version);
-               if (version < 2 || version > 2) {
-                       munmap(idx_map, idx_size);
+               if (version < 2 || version > 2)
                        return error("index file %s is version %"PRIu32
                                     " and is not supported by this binary"
                                     " (try upgrading GIT to a newer version)",
                                     path, version);
-               }
        } else
                version = 1;
 
@@ -120,10 +134,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                index += 2;  /* skip index header */
        for (i = 0; i < 256; i++) {
                uint32_t n = ntohl(index[i]);
-               if (n < nr) {
-                       munmap(idx_map, idx_size);
+               if (n < nr)
                        return error("non-monotonic index %s", path);
-               }
                nr = n;
        }
 
@@ -135,10 +147,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                 *  - hash of the packfile
                 *  - file checksum
                 */
-               if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
-                       munmap(idx_map, idx_size);
+               if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
                        return error("wrong index v1 file size in %s", path);
-               }
        } else if (version == 2) {
                /*
                 * Minimum size:
@@ -157,20 +167,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
                unsigned long max_size = min_size;
                if (nr)
                        max_size += (nr - 1)*8;
-               if (idx_size < min_size || idx_size > max_size) {
-                       munmap(idx_map, idx_size);
+               if (idx_size < min_size || idx_size > max_size)
                        return error("wrong index v2 file size in %s", path);
-               }
                if (idx_size != min_size &&
                    /*
                     * make sure we can deal with large pack offsets.
                     * 31-bit signed offset won't be enough, neither
                     * 32-bit unsigned one will be.
                     */
-                   (sizeof(off_t) <= 4)) {
-                       munmap(idx_map, idx_size);
+                   (sizeof(off_t) <= 4))
                        return error("pack too large for current definition of off_t in %s", path);
-               }
        }
 
        p->index_version = version;
index 442625723dea4b0c8f22b57d52e760d4b810540e..6c4037605d0dfee59a084c440506f1af11708d63 100644 (file)
@@ -164,4 +164,17 @@ extern int has_pack_index(const unsigned char *sha1);
  */
 extern int is_promisor_object(const struct object_id *oid);
 
+/*
+ * Expose a function for fuzz testing.
+ *
+ * load_idx() parses a block of memory as a packfile index and puts the results
+ * into a struct packed_git.
+ *
+ * This function should not be used directly. It is exposed here only so that we
+ * have a convenient entry-point for fuzz testing. For real uses, you should
+ * probably use open_pack_index() or parse_pack_index() instead.
+ */
+extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+                   size_t idx_size, struct packed_git *p);
+
 #endif