]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
integration of oss-fuzz in make test #204 (#206)
authorsebpop <sebpop@gmail.com>
Wed, 7 Nov 2018 09:05:20 +0000 (03:05 -0600)
committerHans Kristian Rosbach <hk-github@circlestorm.org>
Wed, 7 Nov 2018 09:05:20 +0000 (10:05 +0100)
The requirements for an ideal integration of a project in oss-fuzz are:
https://github.com/google/oss-fuzz/blob/master/docs/ideal_integration.md
- Is maintained by code owners in their RCS (Git, SVN, etc).
- Is built with the rest of the tests - no bit rot!
- Has a seed corpus with good code coverage.
- Is continuously tested on the seed corpus with ASan/UBSan/MSan
- Is fast and has no OOMs
- Has a fuzzing dictionary, if applicable

13 files changed:
.gitignore
CMakeLists.txt
Makefile.in
configure
test/Makefile.in
test/fuzz/checksum_fuzzer.c [new file with mode: 0644]
test/fuzz/compress_fuzzer.c [new file with mode: 0644]
test/fuzz/example_dict_fuzzer.c [new file with mode: 0644]
test/fuzz/example_flush_fuzzer.c [new file with mode: 0644]
test/fuzz/example_large_fuzzer.c [new file with mode: 0644]
test/fuzz/example_small_fuzzer.c [new file with mode: 0644]
test/fuzz/minigzip_fuzzer.c [new file with mode: 0644]
test/fuzz/standalone_fuzz_target_runner.c [new file with mode: 0644]

index 257e4df27d9c0f1892ab69443cec664d9a3401fc..fd9985eda275a36e23709004a597dc20ccc9ae14 100644 (file)
@@ -26,6 +26,7 @@
 /CVE-2003-0107
 
 .DS_Store
+*_fuzzer
 *.obj
 *.exe
 *.pdb
index dc416e0068818995f4a5104e973d880715ccb15a..3f5cd005d921ab4e1ca6685219f9128cc1e439d2 100644 (file)
@@ -73,6 +73,9 @@ if (WITH_GZFILEOP)
     add_definitions(-DWITH_GZFILEOP)
 endif (WITH_GZFILEOP)
 
+option(WITH_SANITIZERS "Build with address sanitizer and all supported sanitizers other than memory sanitizer" OFF)
+option(WITH_MSAN "Build with memory sanitizer" OFF)
+option(WITH_FUZZERS "Build test/fuzz" OFF)
 option(WITH_OPTIM "Build with optimisation" ON)
 option(WITH_NEW_STRATEGIES "Use new strategies" ON)
 option(WITH_NATIVE_INSTRUCTIONS
@@ -177,6 +180,9 @@ endif()
 add_feature_info(ZLIB_COMPAT ZLIB_COMPAT "Provide a zlib-compatible API")
 add_feature_info(WITH_GZFILEOP WITH_GZFILEOP "Compile with support for gzFile-related functions")
 add_feature_info(WITH_OPTIM WITH_OPTIM "Build with optimisation")
+add_feature_info(WITH_SANITIZERS WITH_SANITIZERS "Build with address sanitizer and all supported sanitizers other than memory sanitizer")
+add_feature_info(WITH_MSAN WITH_MSAN "Build with memory sanitizer")
+add_feature_info(WITH_FUZZERS WITH_FUZZERS "Build test/fuzz")
 add_feature_info(WITH_NEW_STRATEGIES WITH_NEW_STRATEGIES "Use new strategies")
 if("${ARCH}" MATCHES "arm" OR "${ARCH}" MATCHES "aarch64")
     add_feature_info(WITH_ACLE WITH_ACLE "Build with ACLE CRC")
@@ -230,6 +236,54 @@ endif()
 check_include_file(unistd.h Z_HAVE_UNISTD_H)
 check_include_file(stdarg.h Z_HAVE_STDARG_H)
 
+if(WITH_SANITIZERS AND WITH_MSAN)
+    message(FATAL_ERROR "Memory sanitizer is incompatible with address sanitizer")
+endif()
+
+if(WITH_MSAN)
+    set(CMAKE_REQUIRED_FLAGS "-fsanitize=memory")
+    check_c_source_compiles("int main() { return 0; }" HAS_MSAN FAIL_REGEX "not supported")
+    if(${HAS_MSAN})
+        set(SANITIZERS_FLAGS "-fsanitize=memory")
+        message(STATUS "Adding memory sanitizer flag: ${SANITIZERS_FLAGS}")
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZERS_FLAGS}")
+    endif()
+endif()
+
+if(WITH_SANITIZERS)
+    set(_sanitize_flags
+        bool
+        address
+        array-bounds
+        float-divide-by-zero
+        function
+        integer-divide-by-zero
+        return
+        shift
+        signed-integer-overflow
+        undefined
+        unsigned-integer-overflow
+        vla-bound
+        vptr
+       )
+    set(SANITIZERS_FLAGS "")
+    foreach(_flag ${_sanitize_flags})
+        set(CMAKE_REQUIRED_FLAGS "-fsanitize=${_flag}")
+        check_c_source_compiles("int main() { return 0; }"
+          HAS_SANITIZER_${_flag} FAIL_REGEX "not supported")
+        if(${HAS_SANITIZER_${_flag}})
+            if("${SANITIZERS_FLAGS}" STREQUAL "")
+                set(SANITIZERS_FLAGS "-fsanitize=${_flag}")
+            else()
+                set(SANITIZERS_FLAGS "${SANITIZERS_FLAGS},${_flag}")
+            endif()
+        endif()
+        set(CMAKE_REQUIRED_FLAGS)
+    endforeach()
+    message(STATUS "Adding sanitizers flags: ${SANITIZERS_FLAGS}")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZERS_FLAGS}")
+endif()
+
 #
 # Check if we can hide zlib internal symbols that are linked between separate source files using hidden
 #
@@ -703,6 +757,33 @@ if (ZLIB_ENABLE_TESTS)
         target_link_libraries(minigzip64 zlib)
         set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
     endif()
+
+    if(WITH_FUZZERS)
+        add_executable(checksum_fuzzer test/fuzz/checksum_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(compress_fuzzer test/fuzz/compress_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(example_small_fuzzer test/fuzz/example_small_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(example_large_fuzzer test/fuzz/example_large_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(example_flush_fuzzer test/fuzz/example_flush_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(example_dict_fuzzer test/fuzz/example_dict_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+        add_executable(minigzip_fuzzer test/fuzz/minigzip_fuzzer.c test/fuzz/standalone_fuzz_target_runner.c)
+
+        target_link_libraries(checksum_fuzzer zlib)
+        target_link_libraries(compress_fuzzer zlib)
+        target_link_libraries(example_small_fuzzer zlib)
+        target_link_libraries(example_large_fuzzer zlib)
+        target_link_libraries(example_flush_fuzzer zlib)
+        target_link_libraries(example_dict_fuzzer zlib)
+        target_link_libraries(minigzip_fuzzer zlib)
+
+        file(GLOB ALL_SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*")
+        add_test(checksum_fuzzer checksum_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(compress_fuzzer compress_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(example_small_fuzzer example_small_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(example_large_fuzzer example_large_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(example_flush_fuzzer example_flush_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(example_dict_fuzzer example_dict_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+        add_test(minigzip_fuzzer minigzip_fuzzer${CMAKE_EXECUTABLE_SUFFIX} ${ALL_SRC_FILES})
+    endif()
 endif()
 
 FEATURE_SUMMARY(WHAT ALL INCLUDE_QUIET_PACKAGES)
index 2ee049a0b872c49a6569b1f6e5e0ac9f7bcbbb73..b5dda1b162636309beb3eedf9c94195783b0e085 100644 (file)
@@ -20,6 +20,12 @@ CFLAGS=-O
 #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
 #           -Wstrict-prototypes -Wmissing-prototypes
 
+WITH_SANITIZERS=
+WITH_MSAN=
+WITH_FUZZERS=
+SANITIZERS_CFLAGS=
+MSAN_CFLAGS=
+
 SFLAGS=-O
 LDFLAGS=-L.
 LIBNAME1=libz-ng
@@ -87,7 +93,7 @@ PIC_OBJS = $(PIC_OBJC)
 
 all: static shared
 
-static: example$(EXE) minigzip$(EXE)
+static: example$(EXE) minigzip$(EXE) fuzzers
 
 shared: examplesh$(EXE) minigzipsh$(EXE)
 
@@ -112,6 +118,65 @@ $(ARCHDIR)/%.lo: $(SRCDIR)/$(ARCHDIR)/%.c
 test: all
        $(MAKE) -C test
 
+# By default, use our own standalone_fuzz_target_runner.
+# This runner does no fuzzing, but simply executes the inputs
+# provided via parameters.
+# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
+# to link the fuzzer(s) against a real fuzzing engine.
+ifneq (,$(LIB_FUZZING_ENGINE))
+  # OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
+  WITH_FUZZERS=1
+else
+  LIB_FUZZING_ENGINE = standalone_fuzz_target_runner.o
+  ifeq (1,$(WITH_SANITIZERS))
+  CFLAGS := $(CFLAGS) $(SANITIZERS_CFLAGS)
+  LDFLAGS := $(LDFLAGS) $(SANITIZERS_CFLAGS)
+  else
+    ifeq (1,$(WITH_MSAN))
+    CFLAGS := $(CFLAGS) $(MSAN_CFLAGS)
+    LDFLAGS := $(LDFLAGS) $(MSAN_CFLAGS)
+    endif
+  endif
+endif
+
+ifeq (1,$(WITH_FUZZERS))
+fuzzers: checksum_fuzzer$(EXE) compress_fuzzer$(EXE) example_small_fuzzer$(EXE) example_large_fuzzer$(EXE) example_flush_fuzzer$(EXE) example_dict_fuzzer$(EXE) minigzip_fuzzer$(EXE)
+else
+fuzzers:
+endif
+
+# The standalone fuzz target runner.
+standalone_fuzz_target_runner.o: $(SRCDIR)/test/fuzz/standalone_fuzz_target_runner.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+checksum_fuzzer.o: $(SRCDIR)/test/fuzz/checksum_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+compress_fuzzer.o: $(SRCDIR)/test/fuzz/compress_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+example_small_fuzzer.o: $(SRCDIR)/test/fuzz/example_small_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+example_large_fuzzer.o: $(SRCDIR)/test/fuzz/example_large_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+example_flush_fuzzer.o: $(SRCDIR)/test/fuzz/example_flush_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+example_dict_fuzzer.o: $(SRCDIR)/test/fuzz/example_dict_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+minigzip_fuzzer.o: $(SRCDIR)/test/fuzz/minigzip_fuzzer.c
+       $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+checksum_fuzzer$(EXE): checksum_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) checksum_fuzzer.o $(STATICLIB) -lpthread
+compress_fuzzer$(EXE): compress_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) compress_fuzzer.o $(STATICLIB) -lpthread
+example_small_fuzzer$(EXE): example_small_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) example_small_fuzzer.o $(STATICLIB) -lpthread
+example_large_fuzzer$(EXE): example_large_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) example_large_fuzzer.o $(STATICLIB) -lpthread
+example_flush_fuzzer$(EXE): example_flush_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) example_flush_fuzzer.o $(STATICLIB) -lpthread
+example_dict_fuzzer$(EXE): example_dict_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) example_dict_fuzzer.o $(STATICLIB) -lpthread
+minigzip_fuzzer$(EXE): minigzip_fuzzer.o standalone_fuzz_target_runner.o $(STATICLIB)
+       $(CC) $(LDFLAGS) -o $@ $(LIB_FUZZING_ENGINE) minigzip_fuzzer.o $(STATICLIB) -lpthread
+
 infcover.o: $(SRCDIR)/test/infcover.c $(SRCDIR)/zlib$(SUFFIX).h zconf$(SUFFIX).h
        $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $(SRCDIR)/test/infcover.c
 
@@ -274,6 +339,8 @@ clean:
        rm -f *.o *.lo *~ \
           example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \
           example64$(EXE) minigzip64$(EXE) \
+          checksum_fuzzer$(EXE) compress_fuzzer$(EXE) example_small_fuzzer$(EXE) example_large_fuzzer$(EXE) \
+          example_flush_fuzzer$(EXE) example_dict_fuzzer$(EXE) minigzip_fuzzer$(EXE) \
           infcover \
           $(STATICLIB) $(IMPORTLIB) $(SHAREDLIB) $(SHAREDLIBV) $(SHAREDLIBM) \
           foo.gz so_locations \
index 61c6282c69c78a145734e1c724bb5d355f4e6cf1..cb7e27d6b84f97eb12ed2fb1c1c1e58492cbda33 100755 (executable)
--- a/configure
+++ b/configure
@@ -98,6 +98,9 @@ build32=0
 build64=0
 buildacle=0
 buildneon=0
+with_sanitizers=0
+with_msan=0
+with_fuzzers=0
 floatabi=
 native=0
 sse2flag="-msse2"
@@ -147,6 +150,9 @@ case "$1" in
       echo '    [--without-optimizations]   Compiles without support for optional instruction sets' | tee -a configure.log
       echo '    [--without-new-strategies]  Compiles without using new additional deflate strategies' | tee -a configure.log
       echo '    [--acle] [--neon]           Compiles with additional instruction set enabled' | tee -a configure.log
+      echo '    [--with-sanitizers]         Build with address sanitizer and all supported sanitizers other than memory sanitizer (disabled by default)' | tee -a configure.log
+      echo '    [--with-msan]               Build with memory sanitizer (disabled by default)' | tee -a configure.log
+      echo '    [--with-fuzzers]            Build test/fuzz (disabled by default)' | tee -a configure.log
         exit 0 ;;
     -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;;
     -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/.*=//'`; shift ;;
@@ -175,6 +181,9 @@ case "$1" in
     -oldstrat | --without-new-strategies) without_new_strategies=1; shift;;
     -w* | --warn) warn=1; shift ;;
     -d* | --debug) debug=1; shift ;;
+    --with-sanitizers) with_sanitizers=1; shift ;;
+    --with-msan) with_msan=1; shift ;;
+    --with-fuzzers) with_fuzzers=1; shift ;;
     *)
       echo "unknown option: $1" | tee -a configure.log
       echo "$0 --help for help" | tee -a configure.log
@@ -259,7 +268,7 @@ if test "$gcc" -eq 1 && ($cc $CFLAGS -c $test.c) >> configure.log 2>&1; then
   CC="$cc"
   CFLAGS="${CFLAGS} -std=c99"
   # Re-check arch if gcc is a cross-compiler
-  GCC_ARCH=`$CC -dumpmachine | sed 's/-.*//g'`
+  GCC_ARCH=`$CC $CFLAGS -dumpmachine | sed 's/-.*//g'`
   case $GCC_ARCH in
     i386 | i486 | i586 | i686)
       # Honor user choice if gcc is multilib and 64-bit is requested
@@ -534,6 +543,49 @@ fi
 
 echo >> configure.log
 
+if test $with_sanitizers -eq 1; then
+    if test $with_msan -eq 1; then
+        echo "Error: --with-sanitizers and --with-msan cannot be used together"
+        exit 1
+    fi
+    echo -n "Checking for sanitizers ASan/UBSan... " | tee -a configure.log
+    sanitizers=""
+    for san in bool address array-bounds float-divide-by-zero function integer-divide-by-zero return shift signed-integer-overflow undefined unsigned-integer-overflow vla-bound vptr; do
+        if try $CC -c $CFLAGS $test.c -fsanitize=$san ; then
+            if test -n "$sanitizers"; then
+                sanitizers="$sanitizers,$san"
+            else
+                sanitizers="$san"
+            fi
+        fi
+    done
+
+    SANITIZERS_CFLAGS=""
+    if test -n "$sanitizers"; then
+        echo "-fsanitize=$sanitizers" | tee -a configure.log
+        SANITIZERS_CFLAGS="-fsanitize=$sanitizers"
+    else
+        echo No | tee -a configure.log
+        with_sanitizers=0
+    fi
+
+    echo >> configure.log
+fi
+
+if test $with_msan -eq 1; then
+    echo -n "Checking for MSan... " | tee -a configure.log
+    if try $CC -c $CFLAGS $test.c -fsanitize=memory ; then
+        echo "-fsanitize=memory" | tee -a configure.log
+        MSAN_CFLAGS="-fsanitize=memory"
+    else
+        echo No | tee -a configure.log
+        MSAN_CFLAGS=""
+        with_msan=0
+    fi
+
+    echo >> configure.log
+fi
+
 # see if shared library build supported
 cat > $test.c <<EOF
 extern int getchar();
@@ -1164,6 +1216,11 @@ if [ "$SRCDIR" != "$BUILDDIR" ]; then INCLUDES="-I$BUILDDIR ${INCLUDES}"; fi
 sed < $SRCDIR/Makefile.in "
 /^CC *=/s#=.*#=$CC#
 /^CFLAGS *=/s#=.*#=$CFLAGS#
+/^WITH_SANITIZERS *=/s#=.*#=$with_sanitizers#
+/^WITH_MSAN *=/s#=.*#=$with_msan#
+/^WITH_FUZZERS *=/s#=.*#=$with_fuzzers#
+/^SANITIZERS_CFLAGS *=/s#=.*#=$SANITIZERS_CFLAGS#
+/^MSAN_CFLAGS *=/s#=.*#=$MSAN_CFLAGS#
 /^SFLAGS *=/s#=.*#=$SFLAGS#
 /^LDFLAGS *=/s#=.*#=$LDFLAGS#
 /^LDSHARED *=/s#=.*#=$LDSHARED#
@@ -1247,6 +1304,7 @@ sed < $SRCDIR/test/Makefile.in "
 /^SRCTOP *=/s#=.*#=$SRCDIR#
 /^COMPATTESTS *=/s#=.*#=$COMPATTESTS#
 /^QEMU_RUN *=/s#=.*#=$QEMU_RUN#
+/^WITH_FUZZERS *=/s#=.*#=$with_fuzzers#
 " > test/Makefile
 
 # create zlib.pc with the configure results
index a78f4daf4b84d3e4c5f2d5eb1d426e8a738ee4c3..772f8e81afe0ddc7d3339e3cad347e0949513b25 100644 (file)
@@ -9,12 +9,13 @@ SRCDIR=
 SRCTOP=
 INCLUDES=
 TEST_LDFLAGS=-L.. ../libz.a
+WITH_FUZZERS=
 
 COMPATTESTS =
 QEMU_RUN=
 QEMU_VER:=$(shell command -v $(QEMU_RUN) --version 2> /dev/null)
 
-all: oldtests cvetests $(COMPATTESTS)
+all: oldtests cvetests $(COMPATTESTS) fuzzer
 
 oldtests: #set by ../configure
 check_cross_dep:
@@ -24,6 +25,26 @@ ifeq (,$(QEMU_VER))
 endif
 endif
 
+ALL_SRC_FILES := $(wildcard ../*)
+
+# Only check the fuzzer when it is a stand-alone executable.
+ifneq (,$(LIB_FUZZING_ENGINE))
+fuzzer:
+else
+ ifeq (0,$(WITH_FUZZERS))
+fuzzer:
+ else
+fuzzer:
+       @${QEMU_RUN} ../checksum_fuzzer$(EXE) $(ALL_SRC_FILES) ; \
+       ${QEMU_RUN} ../compress_fuzzer$(EXE) $(ALL_SRC_FILES) ; \
+       ${QEMU_RUN} ../example_small_fuzzer$(EXE) $(ALL_SRC_FILES) ; \
+       ${QEMU_RUN} ../example_large_fuzzer$(EXE) $(ALL_SRC_FILES) ; \
+       ${QEMU_RUN} ../example_flush_fuzzer$(EXE) $(ALL_SRC_FILES) ; \
+       ${QEMU_RUN} ../example_dict_fuzzer$(EXE) $(ALL_SRC_FILES); \
+       ${QEMU_RUN} ../minigzip_fuzzer$(EXE) $(ALL_SRC_FILES)
+ endif
+endif
+
 teststatic: check_cross_dep
        @TMPST=tmpst_$$; \
        if echo hello world | ${QEMU_RUN} ../minigzip$(EXE) | ${QEMU_RUN} ../minigzip$(EXE) -d && ${QEMU_RUN} ../example$(EXE) $$TMPST ; then \
diff --git a/test/fuzz/checksum_fuzzer.c b/test/fuzz/checksum_fuzzer.c
new file mode 100644 (file)
index 0000000..e7003dd
--- /dev/null
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) {
+  uint32_t crc1 = PREFIX(crc32)(0L, NULL, 0);
+  uint32_t crc2 = PREFIX(crc32)(0L, NULL, 0);
+  uint32_t adler1 = PREFIX(adler32)(0L, NULL, 0);
+  uint32_t adler2 = PREFIX(adler32)(0L, NULL, 0);;
+  /* Checksum with a buffer of size equal to the first byte in the input. */
+  uint32_t buffSize = data[0];
+  uint32_t offset = 0;
+
+  /* Discard inputs larger than 1Mb. */
+  static size_t kMaxSize = 1024 * 1024;
+  if (dataLen < 1 || dataLen > kMaxSize)
+    return 0;
+
+  /* Make sure the buffer has at least a byte. */
+  if (buffSize == 0)
+    ++buffSize;
+
+  /* CRC32 */
+  for (offset = 0; offset + buffSize <= dataLen; offset += buffSize)
+    crc1 = PREFIX(crc32_z)(crc1, data + offset, buffSize);
+  crc1 = PREFIX(crc32_z)(crc1, data + offset, dataLen % buffSize);
+
+  crc2 = PREFIX(crc32_z)(crc2, data, dataLen);
+
+  assert(crc1 == crc2);
+  assert(PREFIX(crc32_combine)(crc1, crc2, dataLen) ==
+         PREFIX(crc32_combine)(crc1, crc1, dataLen));
+
+  /* Adler32 */
+  for (offset = 0; offset + buffSize <= dataLen; offset += buffSize)
+    adler1 = PREFIX(adler32_z)(adler1, data + offset, buffSize);
+  adler1 = PREFIX(adler32_z)(adler1, data + offset, dataLen % buffSize);
+
+  adler2 = PREFIX(adler32_z)(adler2, data, dataLen);
+
+  assert(adler1 == adler2);
+  assert(PREFIX(adler32_combine)(adler1, adler2, dataLen) ==
+         PREFIX(adler32_combine)(adler1, adler1, dataLen));
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/compress_fuzzer.c b/test/fuzz/compress_fuzzer.c
new file mode 100644 (file)
index 0000000..e2ccf71
--- /dev/null
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+static const uint8_t *data;
+static size_t dataLen;
+
+static void check_compress_level(uint8_t *compr, size_t comprLen,
+                                 uint8_t *uncompr, size_t uncomprLen,
+                                 int level) {
+  PREFIX(compress2)(compr, &comprLen, data, dataLen, level);
+  PREFIX(uncompress)(uncompr, &uncomprLen, compr, comprLen);
+
+  /* Make sure compress + uncompress gives back the input data. */
+  assert(dataLen == uncomprLen);
+  assert(0 == memcmp(data, uncompr, dataLen));
+}
+
+#define put_byte(s, i, c) {s[i] = (unsigned char)(c);}
+
+static void write_zlib_header(uint8_t *s) {
+  unsigned level_flags = 0; /* compression level (0..3) */
+  unsigned w_bits = 8; /* window size log2(w_size)  (8..16) */
+  unsigned int header = (Z_DEFLATED + ((w_bits-8)<<4)) << 8;
+  header |= (level_flags << 6);
+
+  header += 31 - (header % 31);
+
+  /* s is guaranteed to be longer than 2 bytes. */
+  put_byte(s, 0, (unsigned char)(header >> 8));
+  put_byte(s, 1, (unsigned char)(header & 0xff));
+}
+
+static void check_decompress(uint8_t *compr, size_t comprLen) {
+  /* We need to write a valid zlib header of size two bytes. Copy the input data
+     in a larger buffer. Do not modify the input data to avoid libFuzzer error:
+     fuzz target overwrites its const input. */
+  size_t copyLen = dataLen + 2;
+  uint8_t *copy = (uint8_t *)malloc(copyLen);
+  memcpy(copy + 2, data, dataLen);
+  write_zlib_header(copy);
+
+  PREFIX(uncompress)(compr, &comprLen, copy, copyLen);
+  free(copy);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *d, size_t size) {
+  /* compressBound does not provide enough space for low compression levels. */
+  size_t comprLen = 100 + 2 * PREFIX(compressBound)(size);
+  size_t uncomprLen = size;
+  uint8_t *compr, *uncompr;
+
+  /* Discard inputs larger than 1Mb. */
+  static size_t kMaxSize = 1024 * 1024;
+
+  if (size < 1 || size > kMaxSize)
+    return 0;
+
+  data = d;
+  dataLen = size;
+  compr = (uint8_t *)calloc(1, comprLen);
+  uncompr = (uint8_t *)calloc(1, uncomprLen);
+
+  check_compress_level(compr, comprLen, uncompr, uncomprLen, 1);
+  check_compress_level(compr, comprLen, uncompr, uncomprLen, 3);
+  check_compress_level(compr, comprLen, uncompr, uncomprLen, 6);
+  check_compress_level(compr, comprLen, uncompr, uncomprLen, 7);
+
+  check_decompress(compr, comprLen);
+
+  free(compr);
+  free(uncompr);
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/example_dict_fuzzer.c b/test/fuzz/example_dict_fuzzer.c
new file mode 100644 (file)
index 0000000..b191011
--- /dev/null
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+static const uint8_t *data;
+static size_t dataLen;
+static alloc_func zalloc = NULL;
+static free_func zfree = NULL;
+static size_t dictionaryLen = 0;
+static unsigned long dictId; /* Adler32 value of the dictionary */
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(unsigned char **compr, size_t *comprLen)
+{
+    PREFIX3(stream) c_stream; /* compression stream */
+    int err;
+    int level = data[0] % 11 - 1; /* [-1..9]
+      compression levels
+      #define Z_NO_COMPRESSION         0
+      #define Z_BEST_SPEED             1
+      #define Z_BEST_COMPRESSION       9
+      #define Z_DEFAULT_COMPRESSION  (-1) */
+
+    int method = Z_DEFLATED; /* The deflate compression method (the only one
+                                supported in this version) */
+    int windowBits = 8 + data[0] % 8; /* The windowBits parameter is the base
+      two logarithm of the window size (the size of the history buffer).  It
+      should be in the range 8..15 for this version of the library. */
+    int memLevel = 1 + data[0] % 9;   /* memLevel=1 uses minimum memory but is
+      slow and reduces compression ratio; memLevel=9 uses maximum memory for
+      optimal speed. */
+    int strategy = data[0] % 5;       /* [0..4]
+      #define Z_FILTERED            1
+      #define Z_HUFFMAN_ONLY        2
+      #define Z_RLE                 3
+      #define Z_FIXED               4
+      #define Z_DEFAULT_STRATEGY    0 */
+
+    /* deflate would fail for no-compression or for speed levels. */
+    if (level == 0 || level == 1)
+      level = -1;
+
+    c_stream.zalloc = zalloc;
+    c_stream.zfree = zfree;
+    c_stream.opaque = (void *)0;
+
+    err = PREFIX(deflateInit2)(&c_stream, level, method, windowBits, memLevel,
+                               strategy);
+    CHECK_ERR(err, "deflateInit");
+
+    err = PREFIX(deflateSetDictionary)(
+        &c_stream, (const unsigned char *)data, dictionaryLen);
+    CHECK_ERR(err, "deflateSetDictionary");
+
+    /* deflateBound does not provide enough space for low compression levels. */
+    *comprLen = 100 + 2 * PREFIX(deflateBound)(&c_stream, dataLen);
+    *compr = (uint8_t *)calloc(1, *comprLen);
+
+    dictId = c_stream.adler;
+    c_stream.next_out = *compr;
+    c_stream.avail_out = (unsigned int)(*comprLen);
+
+    c_stream.next_in = data;
+    c_stream.avail_in = dataLen;
+
+    err = PREFIX(deflate)(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        fprintf(stderr, "deflate dict should report Z_STREAM_END\n");
+        exit(1);
+    }
+    err = PREFIX(deflateEnd)(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(unsigned char *compr, size_t comprLen) {
+  int err;
+  PREFIX3(stream) d_stream; /* decompression stream */
+  unsigned char *uncompr;
+
+  d_stream.zalloc = zalloc;
+  d_stream.zfree = zfree;
+  d_stream.opaque = (void *)0;
+
+  d_stream.next_in = compr;
+  d_stream.avail_in = (unsigned int)comprLen;
+
+  err = PREFIX(inflateInit)(&d_stream);
+  CHECK_ERR(err, "inflateInit");
+
+  uncompr = (uint8_t *)calloc(1, dataLen);
+  d_stream.next_out = uncompr;
+  d_stream.avail_out = (unsigned int)dataLen;
+
+  for (;;) {
+    err = PREFIX(inflate)(&d_stream, Z_NO_FLUSH);
+    if (err == Z_STREAM_END)
+      break;
+    if (err == Z_NEED_DICT) {
+      if (d_stream.adler != dictId) {
+        fprintf(stderr, "unexpected dictionary");
+        exit(1);
+      }
+      err = PREFIX(inflateSetDictionary)(
+          &d_stream, (const unsigned char *)data, dictionaryLen);
+    }
+    CHECK_ERR(err, "inflate with dict");
+  }
+
+  err = PREFIX(inflateEnd)(&d_stream);
+  CHECK_ERR(err, "inflateEnd");
+
+  if (memcmp(uncompr, data, dataLen)) {
+    fprintf(stderr, "bad inflate with dict\n");
+    exit(1);
+  }
+
+  free(uncompr);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *d, size_t size) {
+  size_t comprLen = 0;
+  uint8_t *compr;
+
+  /* Discard inputs larger than 100Kb. */
+  static size_t kMaxSize = 100 * 1024;
+
+  if (size < 1 || size > kMaxSize)
+    return 0;
+
+  data = d;
+  dataLen = size;
+
+  /* Set up the contents of the dictionary.  The size of the dictionary is
+     intentionally selected to be of unusual size.  To help cover more corner
+     cases, the size of the dictionary is read from the input data.  */
+  dictionaryLen = data[0];
+  if (dictionaryLen > dataLen)
+    dictionaryLen = dataLen;
+
+  test_dict_deflate(&compr, &comprLen);
+  test_dict_inflate(compr, comprLen);
+
+  free(compr);
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/example_flush_fuzzer.c b/test/fuzz/example_flush_fuzzer.c
new file mode 100644 (file)
index 0000000..f9f2d36
--- /dev/null
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+static const uint8_t *data;
+static size_t dataLen;
+static alloc_func zalloc = NULL;
+static free_func zfree = NULL;
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(unsigned char *compr, z_size_t *comprLen) {
+  PREFIX3(stream) c_stream; /* compression stream */
+  int err;
+  unsigned int len = dataLen;
+
+  c_stream.zalloc = zalloc;
+  c_stream.zfree = zfree;
+  c_stream.opaque = (void *)0;
+
+  err = PREFIX(deflateInit)(&c_stream, Z_DEFAULT_COMPRESSION);
+  CHECK_ERR(err, "deflateInit");
+
+  c_stream.next_in = (const unsigned char *)data;
+  c_stream.next_out = compr;
+  c_stream.avail_in = 3;
+  c_stream.avail_out = (unsigned int)*comprLen;
+  err = PREFIX(deflate)(&c_stream, Z_FULL_FLUSH);
+  CHECK_ERR(err, "deflate flush 1");
+
+  compr[3]++; /* force an error in first compressed block */
+  c_stream.avail_in = len - 3;
+
+  err = PREFIX(deflate)(&c_stream, Z_FINISH);
+  if (err != Z_STREAM_END) {
+    CHECK_ERR(err, "deflate flush 2");
+  }
+  err = PREFIX(deflateEnd)(&c_stream);
+  CHECK_ERR(err, "deflateEnd");
+
+  *comprLen = (z_size_t)c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(unsigned char *compr, size_t comprLen, unsigned char *uncompr,
+               size_t uncomprLen) {
+  int err;
+  PREFIX3(stream) d_stream; /* decompression stream */
+
+  d_stream.zalloc = zalloc;
+  d_stream.zfree = zfree;
+  d_stream.opaque = (void *)0;
+
+  d_stream.next_in = compr;
+  d_stream.avail_in = 2; /* just read the zlib header */
+
+  err = PREFIX(inflateInit)(&d_stream);
+  CHECK_ERR(err, "inflateInit");
+
+  d_stream.next_out = uncompr;
+  d_stream.avail_out = (unsigned int)uncomprLen;
+
+  err = PREFIX(inflate)(&d_stream, Z_NO_FLUSH);
+  CHECK_ERR(err, "inflate");
+
+  d_stream.avail_in = (unsigned int)comprLen - 2; /* read all compressed data */
+  err = PREFIX(inflateSync)(&d_stream); /* but skip the damaged part */
+  CHECK_ERR(err, "inflateSync");
+
+  err = PREFIX(inflate)(&d_stream, Z_FINISH);
+  if (err != Z_DATA_ERROR) {
+    fprintf(stderr, "inflate should report DATA_ERROR\n");
+    /* Because of incorrect adler32 */
+    exit(1);
+  }
+  err = PREFIX(inflateEnd)(&d_stream);
+  CHECK_ERR(err, "inflateEnd");
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *d, size_t size) {
+  size_t comprLen = 100 + 2 * PREFIX(compressBound)(size);
+  size_t uncomprLen = size;
+  uint8_t *compr, *uncompr;
+
+  /* Discard inputs larger than 1Mb. */
+  static size_t kMaxSize = 1024 * 1024;
+
+  // This test requires at least 3 bytes of input data.
+  if (size <= 3 || size > kMaxSize)
+    return 0;
+
+  data = d;
+  dataLen = size;
+  compr = (uint8_t *)calloc(1, comprLen);
+  uncompr = (uint8_t *)calloc(1, uncomprLen);
+
+  test_flush(compr, &comprLen);
+  test_sync(compr, comprLen, uncompr, uncomprLen);
+
+  free(compr);
+  free(uncompr);
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/example_large_fuzzer.c b/test/fuzz/example_large_fuzzer.c
new file mode 100644 (file)
index 0000000..1e8fedc
--- /dev/null
@@ -0,0 +1,143 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+static const uint8_t *data;
+static size_t dataLen;
+static alloc_func zalloc = NULL;
+static free_func zfree = NULL;
+static unsigned int diff;
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(unsigned char *compr, size_t comprLen,
+                        unsigned char *uncompr, size_t uncomprLen) {
+  PREFIX3(stream) c_stream; /* compression stream */
+  int err;
+
+  c_stream.zalloc = zalloc;
+  c_stream.zfree = zfree;
+  c_stream.opaque = (void *)0;
+
+  err = PREFIX(deflateInit)(&c_stream, Z_BEST_COMPRESSION);
+  CHECK_ERR(err, "deflateInit");
+
+  c_stream.next_out = compr;
+  c_stream.avail_out = (unsigned int)comprLen;
+
+  /* At this point, uncompr is still mostly zeroes, so it should compress
+   * very well:
+   */
+  c_stream.next_in = uncompr;
+  c_stream.avail_in = (unsigned int)uncomprLen;
+  err = PREFIX(deflate)(&c_stream, Z_NO_FLUSH);
+  CHECK_ERR(err, "deflate large 1");
+  if (c_stream.avail_in != 0) {
+    fprintf(stderr, "deflate not greedy\n");
+    exit(1);
+  }
+
+  /* Feed in already compressed data and switch to no compression: */
+  PREFIX(deflateParams)(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+  c_stream.next_in = compr;
+  diff = (unsigned int)(c_stream.next_out - compr);
+  c_stream.avail_in = diff;
+  err = PREFIX(deflate)(&c_stream, Z_NO_FLUSH);
+  CHECK_ERR(err, "deflate large 2");
+
+  /* Switch back to compressing mode: */
+  PREFIX(deflateParams)(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+  c_stream.next_in = uncompr;
+  c_stream.avail_in = (unsigned int)uncomprLen;
+  err = PREFIX(deflate)(&c_stream, Z_NO_FLUSH);
+  CHECK_ERR(err, "deflate large 3");
+
+  err = PREFIX(deflate)(&c_stream, Z_FINISH);
+  if (err != Z_STREAM_END) {
+    fprintf(stderr, "deflate large should report Z_STREAM_END\n");
+    exit(1);
+  }
+  err = PREFIX(deflateEnd)(&c_stream);
+  CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(unsigned char *compr, size_t comprLen,
+                        unsigned char *uncompr, size_t uncomprLen) {
+  int err;
+  PREFIX3(stream) d_stream; /* decompression stream */
+
+  d_stream.zalloc = zalloc;
+  d_stream.zfree = zfree;
+  d_stream.opaque = (void *)0;
+
+  d_stream.next_in = compr;
+  d_stream.avail_in = (unsigned int)comprLen;
+
+  err = PREFIX(inflateInit)(&d_stream);
+  CHECK_ERR(err, "inflateInit");
+
+  for (;;) {
+    d_stream.next_out = uncompr; /* discard the output */
+    d_stream.avail_out = (unsigned int)uncomprLen;
+    err = PREFIX(inflate)(&d_stream, Z_NO_FLUSH);
+    if (err == Z_STREAM_END)
+      break;
+    CHECK_ERR(err, "large inflate");
+  }
+
+  err = PREFIX(inflateEnd)(&d_stream);
+  CHECK_ERR(err, "inflateEnd");
+
+  if (d_stream.total_out != 2 * uncomprLen + diff) {
+    fprintf(stderr, "bad large inflate: %zu\n", d_stream.total_out);
+    exit(1);
+  }
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *d, size_t size) {
+  size_t comprLen = 100 + 3 * size;
+  size_t uncomprLen = comprLen;
+  uint8_t *compr, *uncompr;
+
+  /* Discard inputs larger than 512Kb. */
+  static size_t kMaxSize = 512 * 1024;
+
+  if (size < 1 || size > kMaxSize)
+    return 0;
+
+  data = d;
+  dataLen = size;
+  compr = (uint8_t *)calloc(1, comprLen);
+  uncompr = (uint8_t *)calloc(1, uncomprLen);
+
+  test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+  test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+  free(compr);
+  free(uncompr);
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/example_small_fuzzer.c b/test/fuzz/example_small_fuzzer.c
new file mode 100644 (file)
index 0000000..d965c2d
--- /dev/null
@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+#  include "zlib.h"
+#else
+#  include "zlib-ng.h"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+static const uint8_t *data;
+static size_t dataLen;
+static alloc_func zalloc = NULL;
+static free_func zfree = NULL;
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(unsigned char *compr, size_t comprLen) {
+  PREFIX3(stream) c_stream; /* compression stream */
+  int err;
+  unsigned long len = dataLen;
+
+  c_stream.zalloc = zalloc;
+  c_stream.zfree = zfree;
+  c_stream.opaque = (void *)0;
+
+  err = PREFIX(deflateInit)(&c_stream, Z_DEFAULT_COMPRESSION);
+  CHECK_ERR(err, "deflateInit");
+
+  c_stream.next_in = (const unsigned char *)data;
+  c_stream.next_out = compr;
+
+  while (c_stream.total_in != len && c_stream.total_out < comprLen) {
+    c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+    err = PREFIX(deflate)(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate small 1");
+  }
+  /* Finish the stream, still forcing small buffers: */
+  for (;;) {
+    c_stream.avail_out = 1;
+    err = PREFIX(deflate)(&c_stream, Z_FINISH);
+    if (err == Z_STREAM_END)
+      break;
+    CHECK_ERR(err, "deflate small 2");
+  }
+
+  err = PREFIX(deflateEnd)(&c_stream);
+  CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(unsigned char *compr, size_t comprLen, unsigned char *uncompr,
+                  size_t uncomprLen) {
+  int err;
+  PREFIX3(stream) d_stream; /* decompression stream */
+
+  d_stream.zalloc = zalloc;
+  d_stream.zfree = zfree;
+  d_stream.opaque = (void *)0;
+
+  d_stream.next_in = compr;
+  d_stream.avail_in = 0;
+  d_stream.next_out = uncompr;
+
+  err = PREFIX(inflateInit)(&d_stream);
+  CHECK_ERR(err, "inflateInit");
+
+  while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+    d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+    err = PREFIX(inflate)(&d_stream, Z_NO_FLUSH);
+    if (err == Z_STREAM_END)
+      break;
+    CHECK_ERR(err, "inflate");
+  }
+
+  err = PREFIX(inflateEnd)(&d_stream);
+  CHECK_ERR(err, "inflateEnd");
+
+  if (memcmp(uncompr, data, dataLen)) {
+    fprintf(stderr, "bad inflate\n");
+    exit(1);
+  }
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *d, size_t size) {
+  size_t comprLen = PREFIX(compressBound)(size);
+  size_t uncomprLen = size;
+  uint8_t *compr, *uncompr;
+
+  /* Discard inputs larger than 1Mb. */
+  static size_t kMaxSize = 1024 * 1024;
+
+  if (size < 1 || size > kMaxSize)
+    return 0;
+
+  data = d;
+  dataLen = size;
+  compr = (uint8_t *)calloc(1, comprLen);
+  uncompr = (uint8_t *)calloc(1, uncomprLen);
+
+  test_deflate(compr, comprLen);
+  test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+  free(compr);
+  free(uncompr);
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/minigzip_fuzzer.c b/test/fuzz/minigzip_fuzzer.c
new file mode 100644 (file)
index 0000000..375bab5
--- /dev/null
@@ -0,0 +1,518 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing.
+ */
+
+/* @(#) $Id$ */
+
+#include "zbuild.h"
+#ifdef ZLIB_COMPAT
+# include "zlib.h"
+#else
+# include "zlib-ng.h"
+#endif
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef USE_MMAP
+#  include <sys/types.h>
+#  include <sys/mman.h>
+#  include <sys/stat.h>
+#endif
+
+#ifndef UNALIGNED_OK
+#  include <malloc.h>
+#endif
+
+#if defined(WIN32) || defined(__CYGWIN__)
+#  include <fcntl.h>
+#  include <io.h>
+#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+#  define SET_BINARY_MODE(file)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#  define snprintf _snprintf
+#endif
+
+#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+  extern int unlink (const char *);
+#endif
+#endif
+
+#ifndef GZ_SUFFIX
+#  define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN      16384        /* read buffer size */
+#define BUFLENW     (BUFLEN * 3) /* write buffer size */
+#define MAX_NAME_LEN 1024
+
+#ifndef WITH_GZFILEOP
+/* without WITH_GZFILEOP, create simplified gz* functions using deflate and inflate */
+
+#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
+#  include <unistd.h>       /* for unlink() */
+#endif
+
+void *myalloc (void *, unsigned, unsigned);
+void myfree (void *, void *);
+
+void *myalloc(void *q, unsigned n, unsigned m)
+{
+    (void)q;
+#ifndef UNALIGNED_OK
+    return memalign(16, n * m);
+#else
+    return calloc(n, m);
+#endif
+}
+
+void myfree(void *q, void *p)
+{
+    (void)q;
+    free(p);
+}
+
+typedef struct gzFile_s {
+    FILE *file;
+    int write;
+    int err;
+    const char *msg;
+    PREFIX3(stream) strm;
+    unsigned char *buf;
+} *gzFile;
+
+gzFile PREFIX(gzopen)(const char *, const char *);
+gzFile PREFIX(gzdopen)(int, const char *);
+gzFile gz_open (const char *, int, const char *);
+
+gzFile PREFIX(gzopen)(const char *path, const char *mode)
+{
+    return gz_open(path, -1, mode);
+}
+
+gzFile PREFIX(gzdopen)(int fd, const char *mode)
+{
+    return gz_open(NULL, fd, mode);
+}
+
+gzFile gz_open(const char *path, int fd, const char *mode)
+{
+    gzFile gz;
+    int ret;
+    int level = Z_DEFAULT_COMPRESSION;
+    const char *plevel = mode;
+
+    gz = malloc(sizeof(struct gzFile_s));
+    if (gz == NULL)
+        return NULL;
+    gz->write = strchr(mode, 'w') != NULL;
+    gz->strm.zalloc = myalloc;
+    gz->strm.zfree = myfree;
+    gz->strm.opaque = NULL;
+    gz->buf = malloc(gz->write ? BUFLENW : BUFLEN);
+
+    if (gz->buf == NULL) {
+        free(gz);
+        return NULL;
+    }
+
+    while (*plevel) {
+        if (*plevel >= '0' && *plevel <= '9') {
+           level = *plevel - '0';
+           break;
+        }
+        plevel++;
+    }
+    if (gz->write)
+        ret = PREFIX(deflateInit2)(&(gz->strm), level, 8, 15 + 16, 8, 0);
+    else {
+        gz->strm.next_in = NULL;
+        gz->strm.avail_in = 0;
+        ret = PREFIX(inflateInit2)(&(gz->strm), 15 + 16);
+    }
+    if (ret != Z_OK) {
+        free(gz);
+        return NULL;
+    }
+    gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
+                              fopen(path, gz->write ? "wb" : "rb");
+    if (gz->file == NULL) {
+        gz->write ? PREFIX(deflateEnd)(&(gz->strm)) : PREFIX(inflateEnd)(&(gz->strm));
+        free(gz);
+        return NULL;
+    }
+    gz->err = 0;
+    gz->msg = "";
+    return gz;
+}
+
+int PREFIX(gzwrite)(gzFile, const void *, unsigned);
+
+int PREFIX(gzwrite)(gzFile gz, const void *buf, unsigned len)
+{
+    PREFIX3(stream) *strm;
+
+    if (gz == NULL || !gz->write)
+        return 0;
+    strm = &(gz->strm);
+    strm->next_in = (void *)buf;
+    strm->avail_in = len;
+    do {
+        strm->next_out = gz->buf;
+        strm->avail_out = BUFLENW;
+        (void)PREFIX(deflate)(strm, Z_NO_FLUSH);
+        fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
+    } while (strm->avail_out == 0);
+    return len;
+}
+
+int PREFIX(gzread)(gzFile, void *, unsigned);
+
+int PREFIX(gzread)(gzFile gz, void *buf, unsigned len)
+{
+    PREFIX3(stream) *strm;
+
+    if (gz == NULL || gz->write || gz->err)
+        return 0;
+    strm = &(gz->strm);
+    strm->next_out = buf;
+    strm->avail_out = len;
+    do {
+        if (strm->avail_in == 0)
+        {
+            strm->next_in = gz->buf;
+            strm->avail_in = (uint32_t)fread(gz->buf, 1, BUFLEN, gz->file);
+        }
+        if (strm->avail_in > 0)
+        {
+            int ret = PREFIX(inflate)(strm, Z_NO_FLUSH);
+            if (ret == Z_DATA_ERROR) {
+                gz->err = ret;
+                gz->msg = strm->msg;
+                return 0;
+            }
+            else if (ret == Z_STREAM_END)
+                PREFIX(inflateReset)(strm);
+        }
+        else
+            break;
+    } while (strm->avail_out);
+    return len - strm->avail_out;
+}
+
+int PREFIX(gzclose)(gzFile);
+
+int PREFIX(gzclose)(gzFile gz)
+{
+    PREFIX3(stream) *strm;
+
+    if (gz == NULL)
+        return Z_STREAM_ERROR;
+    strm = &(gz->strm);
+    if (gz->write) {
+        strm->next_in = NULL;
+        strm->avail_in = 0;
+        do {
+            strm->next_out = gz->buf;
+            strm->avail_out = BUFLENW;
+            (void)PREFIX(deflate)(strm, Z_FINISH);
+            fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
+        } while (strm->avail_out == 0);
+        PREFIX(deflateEnd)(strm);
+    }
+    else
+        PREFIX(inflateEnd)(strm);
+    free(gz->buf);
+    fclose(gz->file);
+    free(gz);
+    return Z_OK;
+}
+
+const char *PREFIX(gzerror)(gzFile, int *);
+
+const char *PREFIX(gzerror)(gzFile gz, int *err)
+{
+    *err = gz->err;
+    return gz->msg;
+}
+
+#endif
+
+static char *prog;
+
+void error            (const char *msg);
+void gz_compress      (FILE   *in, gzFile out);
+#ifdef USE_MMAP
+int  gz_compress_mmap (FILE   *in, gzFile out);
+#endif
+void gz_uncompress    (gzFile in, FILE   *out);
+void file_compress    (char  *file, char *mode);
+void file_uncompress  (char  *file);
+int  main             (int argc, char *argv[]);
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(const char *msg)
+{
+    fprintf(stderr, "%s: %s\n", prog, msg);
+    exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(FILE   *in, gzFile out)
+{
+    char buf[BUFLEN];
+    int len;
+    int err;
+
+#ifdef USE_MMAP
+    /* Try first compressing with mmap. If mmap fails (minigzip used in a
+     * pipe), use the normal fread loop.
+     */
+    if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+    /* Clear out the contents of buf before reading from the file to avoid
+       MemorySanitizer: use-of-uninitialized-value warnings. */
+    memset(buf, 0, sizeof(buf));
+    for (;;) {
+        len = (int)fread(buf, 1, sizeof(buf), in);
+        if (ferror(in)) {
+            perror("fread");
+            exit(1);
+        }
+        if (len == 0) break;
+
+        if (PREFIX(gzwrite)(out, buf, (unsigned)len) != len) error(PREFIX(gzerror)(out, &err));
+    }
+    fclose(in);
+    if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(FILE   *in, gzFile out)
+{
+    int len;
+    int err;
+    int ifd = fileno(in);
+    caddr_t buf;    /* mmap'ed buffer for the entire input file */
+    off_t buf_len;  /* length of the input file */
+    struct stat sb;
+
+    /* Determine the size of the file, needed for mmap: */
+    if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+    buf_len = sb.st_size;
+    if (buf_len <= 0) return Z_ERRNO;
+
+    /* Now do the actual mmap: */
+    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+    if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+    /* Compress the whole file at once: */
+    len = PREFIX(gzwrite)(out, (char *)buf, (unsigned)buf_len);
+
+    if (len != (int)buf_len) error(PREFIX(gzerror)(out, &err));
+
+    munmap(buf, buf_len);
+    fclose(in);
+    if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
+    return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(gzFile in, FILE   *out)
+{
+    char buf[BUFLENW];
+    int len;
+    int err;
+
+    for (;;) {
+        len = PREFIX(gzread)(in, buf, sizeof(buf));
+        if (len < 0) error (PREFIX(gzerror)(in, &err));
+        if (len == 0) break;
+
+        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+            error("failed fwrite");
+        }
+    }
+    if (fclose(out)) error("failed fclose");
+
+    if (PREFIX(gzclose)(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(char  *file, char  *mode)
+{
+    char outfile[MAX_NAME_LEN];
+    FILE  *in;
+    gzFile out;
+
+    if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
+        fprintf(stderr, "%s: filename too long\n", prog);
+        exit(1);
+    }
+
+    snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
+
+    in = fopen(file, "rb");
+    if (in == NULL) {
+        perror(file);
+        exit(1);
+    }
+    out = PREFIX(gzopen)(outfile, mode);
+    if (out == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+        exit(1);
+    }
+    gz_compress(in, out);
+
+    unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(char  *file)
+{
+    char buf[MAX_NAME_LEN];
+    char *infile, *outfile;
+    FILE  *out;
+    gzFile in;
+    size_t len = strlen(file);
+
+    if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
+        fprintf(stderr, "%s: filename too long\n", prog);
+        exit(1);
+    }
+
+    snprintf(buf, sizeof(buf), "%s", file);
+
+    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+        infile = file;
+        outfile = buf;
+        outfile[len-3] = '\0';
+    } else {
+        outfile = file;
+        infile = buf;
+        snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
+    }
+    in = PREFIX(gzopen)(infile, "rb");
+    if (in == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+        exit(1);
+    }
+    out = fopen(outfile, "wb");
+    if (out == NULL) {
+        perror(file);
+        exit(1);
+    }
+
+    gz_uncompress(in, out);
+
+    unlink(infile);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) {
+  char *inFileName = "/tmp/minigzip_fuzzer.out";
+  char *outFileName = "/tmp/minigzip_fuzzer.out.gz";
+  char outmode[20];
+  FILE *in;
+  char buf[BUFLEN];
+  uint32_t offset = 0;
+
+  /* Discard inputs larger than 1Mb. */
+  static size_t kMaxSize = 1024 * 1024;
+  if (dataLen < 1 || dataLen > kMaxSize)
+    return 0;
+
+  in = fopen(inFileName, "w");
+  if (fwrite(data, 1, (unsigned)dataLen, in) != dataLen)
+    error("failed fwrite");
+  if (fclose(in))
+    error("failed fclose");
+
+  memset(outmode, 0, sizeof(outmode));
+  snprintf(outmode, sizeof(outmode), "%s", "wb");
+
+  /* Compression level: [0..9]. */
+  outmode[2] = data[0] % 10;
+
+  switch (data[0] % 4) {
+  default:
+  case 0:
+    outmode[3] = 0;
+    break;
+  case 1:
+    /* compress with Z_FILTERED */
+    outmode[3] = 'f';
+    break;
+  case 2:
+    /* compress with Z_HUFFMAN_ONLY */
+    outmode[3] = 'h';
+    break;
+  case 3:
+    /* compress with Z_RLE */
+    outmode[3] = 'R';
+    break;
+  }
+
+  file_compress(inFileName, outmode);
+  file_uncompress(outFileName);
+
+  /* Check that the uncompressed file matches the input data. */
+  in = fopen(inFileName, "rb");
+  if (in == NULL) {
+    perror(inFileName);
+    exit(1);
+  }
+
+  memset(buf, 0, sizeof(buf));
+  for (;;) {
+    int len = (int)fread(buf, 1, sizeof(buf), in);
+    if (ferror(in)) {
+      perror("fread");
+      exit(1);
+    }
+    if (len == 0)
+      break;
+    assert(0 == memcmp(data + offset, buf, len));
+    offset += len;
+  }
+
+  if (fclose(in))
+    error("failed fclose");
+
+  /* This function must return 0. */
+  return 0;
+}
diff --git a/test/fuzz/standalone_fuzz_target_runner.c b/test/fuzz/standalone_fuzz_target_runner.c
new file mode 100644 (file)
index 0000000..7d9bb76
--- /dev/null
@@ -0,0 +1,35 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
+
+int main(int argc, char **argv) {
+  int i;
+  fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1);
+
+  for (i = 1; i < argc; i++) {
+    size_t len, n_read, err;
+    unsigned char *buf;
+    FILE *f = fopen(argv[i], "r+");
+    if (!f) {
+      /* Failed to open this file: it may be a directory. */
+      fprintf(stderr, "Skipping: %s\n", argv[i]);
+      continue;
+    }
+    fprintf(stderr, "Running: %s %s\n", argv[0], argv[i]);
+    fseek(f, 0, SEEK_END);
+    len = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    buf = (unsigned char *)malloc(len);
+    n_read = fread(buf, 1, len, f);
+    assert(n_read == len);
+    LLVMFuzzerTestOneInput(buf, len);
+    free(buf);
+    err = fclose(f);
+    assert(err == 0);
+    fprintf(stderr, "Done:    %s: (%d bytes)\n", argv[i], (int)n_read);
+  }
+
+  return 0;
+}