From: sebpop Date: Wed, 7 Nov 2018 09:05:20 +0000 (-0600) Subject: integration of oss-fuzz in make test #204 (#206) X-Git-Tag: 1.9.9-b1~607 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4999e84a04f62bb2f3f0c8cf0fd869f9dcdf29e4;p=thirdparty%2Fzlib-ng.git integration of oss-fuzz in make test #204 (#206) 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 --- diff --git a/.gitignore b/.gitignore index 257e4df27..fd9985eda 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ /CVE-2003-0107 .DS_Store +*_fuzzer *.obj *.exe *.pdb diff --git a/CMakeLists.txt b/CMakeLists.txt index dc416e006..3f5cd005d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Makefile.in b/Makefile.in index 2ee049a0b..b5dda1b16 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/configure b/configure index 61c6282c6..cb7e27d6b 100755 --- 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 < test/Makefile # create zlib.pc with the configure results diff --git a/test/Makefile.in b/test/Makefile.in index a78f4daf4..772f8e81a 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -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 index 000000000..e7003dd8c --- /dev/null +++ b/test/fuzz/checksum_fuzzer.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..e2ccf7177 --- /dev/null +++ b/test/fuzz/compress_fuzzer.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..b19101173 --- /dev/null +++ b/test/fuzz/example_dict_fuzzer.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..f9f2d3646 --- /dev/null +++ b/test/fuzz/example_flush_fuzzer.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..1e8fedc22 --- /dev/null +++ b/test/fuzz/example_large_fuzzer.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..d965c2db8 --- /dev/null +++ b/test/fuzz/example_small_fuzzer.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..375bab588 --- /dev/null +++ b/test/fuzz/minigzip_fuzzer.c @@ -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 +#include +#include +#include + +#ifdef USE_MMAP +# include +# include +# include +#endif + +#ifndef UNALIGNED_OK +# include +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +# include +# include +# 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 /* 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 */ + +/* 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 index 000000000..7d9bb7667 --- /dev/null +++ b/test/fuzz/standalone_fuzz_target_runner.c @@ -0,0 +1,35 @@ +#include +#include +#include + +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; +}