commit
95c4a6e2c8
|
@ -0,0 +1,108 @@
|
|||
# ##########################################################################
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This Makefile is validated for Linux, and macOS targets
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
# ##########################################################################
|
||||
|
||||
CFLAGS ?= -O3
|
||||
CXXFLAGS ?= -O3
|
||||
|
||||
ZSTDDIR = ../../lib
|
||||
PRGDIR = ../../programs
|
||||
|
||||
FUZZ_CPPFLAGS := -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \
|
||||
-I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) \
|
||||
-DZSTD_DEBUG=1 -DMEM_FORCE_MEMORY_ACCESS=0 \
|
||||
-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION $(CPPFLAGS)
|
||||
FUZZ_CFLAGS := -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \
|
||||
-Wstrict-prototypes -Wundef -Wformat-security \
|
||||
-Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \
|
||||
-Wredundant-decls \
|
||||
-g -fno-omit-frame-pointer $(CFLAGS)
|
||||
FUZZ_CXXFLAGS := -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \
|
||||
-Wstrict-aliasing=1 -Wswitch-enum \
|
||||
-Wdeclaration-after-statement -Wstrict-prototypes -Wundef \
|
||||
-Wformat-security -Wvla -Wformat=2 -Winit-self -Wfloat-equal \
|
||||
-Wwrite-strings -Wredundant-decls \
|
||||
-g -fno-omit-frame-pointer -std=c++11 $(CXXFLAGS)
|
||||
FUZZ_LDFLAGS := $(LDFLAGS)
|
||||
FUZZ_ARFLAGS := $(ARFLAGS)
|
||||
FUZZ_TARGET_FLAGS = $(FUZZ_CPPFLAGS) $(FUZZ_CXXFLAGS) $(FUZZ_LDFLAGS)
|
||||
|
||||
FUZZ_HEADERS := fuzz_helpers.h fuzz.h
|
||||
|
||||
ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c
|
||||
ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c
|
||||
ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c
|
||||
ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES)
|
||||
|
||||
ZSTD_OBJ := $(patsubst %.c,%.o, $(wildcard $(ZSTD_FILES)))
|
||||
|
||||
LIBFUZZER ?= -lFuzzer
|
||||
|
||||
.PHONY: default all clean
|
||||
|
||||
default: all
|
||||
|
||||
all: round_trip simple_decompress
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $^ -c -o $@
|
||||
|
||||
simple_round_trip: $(FUZZ_HEADERS) $(ZSTD_OBJ) simple_round_trip.o
|
||||
$(CXX) $(FUZZ_TARGET_FLAGS) $(ZSTD_OBJ) simple_round_trip.o $(LIBFUZZER) -o $@
|
||||
|
||||
stream_round_trip: $(FUZZ_HEADERS) $(ZSTD_OBJ) stream_round_trip.o
|
||||
$(CXX) $(FUZZ_TARGET_FLAGS) $(ZSTD_OBJ) stream_round_trip.o $(LIBFUZZER) -o $@
|
||||
|
||||
simple_decompress: $(FUZZ_HEADERS) $(ZSTD_OBJ) simple_decompress.o
|
||||
$(CXX) $(FUZZ_TARGET_FLAGS) $(ZSTD_OBJ) simple_decompress.o $(LIBFUZZER) -o $@
|
||||
|
||||
stream_decompress: $(FUZZ_HEADERS) $(ZSTD_OBJ) stream_decompress.o
|
||||
$(CXX) $(FUZZ_TARGET_FLAGS) $(ZSTD_OBJ) stream_decompress.o $(LIBFUZZER) -o $@
|
||||
|
||||
libregression.a: $(FUZZ_HEADERS) $(PRGDIR)/util.h regression_driver.o
|
||||
$(AR) $(FUZZ_ARFLAGS) $@ regression_driver.o
|
||||
|
||||
%-regression: libregression.a
|
||||
$(RM) $*
|
||||
$(MAKE) $* LDFLAGS="$(FUZZ_LDFLAGS) -L." LIBFUZZER=-lregression
|
||||
|
||||
%-regression-test: %-regression
|
||||
./$* corpora/$*
|
||||
|
||||
regression-test: \
|
||||
simple_round_trip-regression-test \
|
||||
stream_round_trip-regression-test \
|
||||
simple_decompress-regression-test \
|
||||
stream_decompress-regression-test
|
||||
|
||||
%-msan: clean
|
||||
$(MAKE) $* CFLAGS="-fsanitize=memory $(FUZZ_CFLAGS)" \
|
||||
CXXFLAGS="-fsanitize=memory $(FUZZ_CXXFLAGS)"
|
||||
|
||||
UASAN_FLAGS := -fsanitize=address,undefined -fno-sanitize-recover=undefined \
|
||||
-fno-sanitize=pointer-overflow
|
||||
%-uasan: clean
|
||||
$(MAKE) $* CFLAGS="$(FUZZ_CFLAGS) $(UASAN_FLAGS)" \
|
||||
CXXFLAGS="$(FUZZ_CXXFLAGS) $(UASAN_FLAGS)"
|
||||
|
||||
# Install libfuzzer (not usable for MSAN testing)
|
||||
# Provided for convienence. To use this library run make libFuzzer and
|
||||
# set LDFLAGS=-L.
|
||||
.PHONY: libFuzzer
|
||||
libFuzzer:
|
||||
@$(RM) -rf Fuzzer
|
||||
@git clone https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer
|
||||
@./Fuzzer/build.sh
|
||||
|
||||
clean:
|
||||
@$(MAKE) -C $(ZSTDDIR) clean
|
||||
@$(RM) -f *.a *.o
|
||||
@$(RM) -f simple_round_trip stream_round_trip simple_decompress stream_decompress
|
|
@ -0,0 +1,34 @@
|
|||
# Fuzzing
|
||||
|
||||
Each fuzzing target can be built with multiple engines.
|
||||
|
||||
## LibFuzzer
|
||||
|
||||
You can install `libFuzzer` with `make libFuzzer`. Then you can make each target
|
||||
with `make target LDFLAGS=-L. CC=clang CXX=clang++`.
|
||||
|
||||
## AFL
|
||||
|
||||
The regression driver also serves as a binary for `afl-fuzz`. You can make each
|
||||
target with one of these commands:
|
||||
|
||||
```
|
||||
make target-regression CC=afl-clang CXX=afl-clang++
|
||||
AFL_MSAN=1 make target-regression-msan CC=afl-clang CXX=afl-clang++
|
||||
AFL_ASAN=1 make target-regression-uasan CC=afl-clang CXX=afl-clang++
|
||||
```
|
||||
|
||||
Then run as `./target @@`.
|
||||
|
||||
## Regression Testing
|
||||
|
||||
Each fuzz target has a corpus checked into the repo under `fuzz/corpora/`.
|
||||
You can run regression tests on the corpora to ensure that inputs which
|
||||
previously exposed bugs still pass. You can make these targets to run the
|
||||
regression tests with different sanitizers.
|
||||
|
||||
```
|
||||
make regression-test
|
||||
make regression-test-msan
|
||||
make regression-test-uasan
|
||||
```
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fuzz target interface.
|
||||
* Fuzz targets have some common parameters passed as macros during compilation.
|
||||
* Check the documentation for each individual fuzzer for more parameters.
|
||||
*
|
||||
* @param STATEFULL_FUZZING:
|
||||
* Define this to reuse state between fuzzer runs. This can be useful to
|
||||
* test code paths which are only executed when contexts are reused.
|
||||
* WARNING: Makes reproducing crashes much harder.
|
||||
* Default: Not defined.
|
||||
* @param FUZZ_RNG_SEED_SIZE:
|
||||
* The number of bytes of the source to look at when constructing a seed
|
||||
* for the deterministic RNG.
|
||||
* Default: 128.
|
||||
* @param ZSTD_DEBUG:
|
||||
* This is a parameter for the zstd library. Defining `ZSTD_DEBUG=1`
|
||||
* enables assert() statements in the zstd library. Higher levels enable
|
||||
* logging, so aren't recommended. Defining `ZSTD_DEBUG=1` is
|
||||
* recommended.
|
||||
* @param MEM_FORCE_MEMORY_ACCESS:
|
||||
* This flag controls how the zstd library accesses unaligned memory.
|
||||
* It can be undefined, or 0 through 2. If it is undefined, it selects
|
||||
* the method to use based on the compiler. If testing with UBSAN set
|
||||
* MEM_FORCE_MEMORY_ACCESS=0 to use the standard compliant method.
|
||||
* @param FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
* This is the canonical flag to enable deterministic builds for fuzzing.
|
||||
* Changes to zstd for fuzzing are gated behind this define.
|
||||
* It is recommended to define this when building zstd for fuzzing.
|
||||
*/
|
||||
|
||||
#ifndef FUZZ_H
|
||||
#define FUZZ_H
|
||||
|
||||
#ifndef FUZZ_RNG_SEED_SIZE
|
||||
# define FUZZ_RNG_SEED_SIZE 128
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper functions for fuzzing.
|
||||
*/
|
||||
|
||||
#ifndef FUZZ_HELPERS_H
|
||||
#define FUZZ_HELPERS_H
|
||||
|
||||
#include "fuzz.h"
|
||||
#include "xxhash.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define FUZZ_QUOTE_IMPL(str) #str
|
||||
#define FUZZ_QUOTE(str) FUZZ_QUOTE_IMPL(str)
|
||||
|
||||
/**
|
||||
* Asserts for fuzzing that are always enabled.
|
||||
*/
|
||||
#define FUZZ_ASSERT_MSG(cond, msg) \
|
||||
((cond) ? (void)0 \
|
||||
: (fprintf(stderr, "%s: %u: Assertion: `%s' failed. %s\n", __FILE__, \
|
||||
__LINE__, FUZZ_QUOTE(cond), (msg)), \
|
||||
abort()))
|
||||
#define FUZZ_ASSERT(cond) FUZZ_ASSERT_MSG((cond), "");
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define FUZZ_STATIC static __inline __attribute__((unused))
|
||||
#elif defined(__cplusplus) || \
|
||||
(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
#define FUZZ_STATIC static inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define FUZZ_STATIC static __inline
|
||||
#else
|
||||
#define FUZZ_STATIC static
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Determininistically constructs a seed based on the fuzz input.
|
||||
* Only looks at the first FUZZ_RNG_SEED_SIZE bytes of the input.
|
||||
*/
|
||||
FUZZ_STATIC uint32_t FUZZ_seed(const uint8_t *src, size_t size) {
|
||||
size_t const toHash = MIN(FUZZ_RNG_SEED_SIZE, size);
|
||||
return XXH32(src, toHash, 0);
|
||||
}
|
||||
|
||||
#define FUZZ_rotl32(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
|
||||
FUZZ_STATIC uint32_t FUZZ_rand(uint32_t *state) {
|
||||
static const uint32_t prime1 = 2654435761U;
|
||||
static const uint32_t prime2 = 2246822519U;
|
||||
uint32_t rand32 = *state;
|
||||
rand32 *= prime1;
|
||||
rand32 += prime2;
|
||||
rand32 = FUZZ_rotl32(rand32, 13);
|
||||
*state = rand32;
|
||||
return rand32 >> 5;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include "fuzz.h"
|
||||
#include "fuzz_helpers.h"
|
||||
#include "util.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char const **argv) {
|
||||
size_t const kMaxFileSize = (size_t)1 << 20;
|
||||
int const kFollowLinks = 1;
|
||||
char *fileNamesBuf = NULL;
|
||||
char const **files = argv + 1;
|
||||
unsigned numFiles = argc - 1;
|
||||
uint8_t *buffer = NULL;
|
||||
size_t bufferSize = 0;
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
#ifdef UTIL_HAS_CREATEFILELIST
|
||||
files = UTIL_createFileList(files, numFiles, &fileNamesBuf, &numFiles,
|
||||
kFollowLinks);
|
||||
FUZZ_ASSERT(files);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < numFiles; ++i) {
|
||||
char const *fileName = files[i];
|
||||
size_t const fileSize = UTIL_getFileSize(fileName);
|
||||
size_t readSize;
|
||||
FILE *file;
|
||||
|
||||
/* Check that it is a regular file, and that the fileSize is valid */
|
||||
FUZZ_ASSERT_MSG(UTIL_isRegFile(fileName), fileName);
|
||||
FUZZ_ASSERT_MSG(fileSize <= kMaxFileSize, fileName);
|
||||
/* Ensure we have a large enough buffer allocated */
|
||||
if (fileSize > bufferSize) {
|
||||
free(buffer);
|
||||
buffer = (uint8_t *)malloc(fileSize);
|
||||
FUZZ_ASSERT_MSG(buffer, fileName);
|
||||
bufferSize = fileSize;
|
||||
}
|
||||
/* Open the file */
|
||||
file = fopen(fileName, "rb");
|
||||
FUZZ_ASSERT_MSG(file, fileName);
|
||||
/* Read the file */
|
||||
readSize = fread(buffer, 1, fileSize, file);
|
||||
FUZZ_ASSERT_MSG(readSize == fileSize, fileName);
|
||||
/* Close the file */
|
||||
fclose(file);
|
||||
/* Run the fuzz target */
|
||||
LLVMFuzzerTestOneInput(buffer, fileSize);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
free(buffer);
|
||||
#ifdef UTIL_HAS_CREATEFILELIST
|
||||
UTIL_freeFileList(files, fileNamesBuf);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This fuzz target attempts to decompress the fuzzed data with the simple
|
||||
* decompression function to ensure the decompressor never crashes.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "fuzz_helpers.h"
|
||||
#include "zstd.h"
|
||||
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
static void* rBuf = NULL;
|
||||
static size_t bufSize = 0;
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
size_t const neededBufSize = MAX(20 * size, (size_t)256 << 10);
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBufSize > bufSize) {
|
||||
free(rBuf);
|
||||
rBuf = malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(rBuf);
|
||||
}
|
||||
if (!dctx) {
|
||||
dctx = ZSTD_createDCtx();
|
||||
FUZZ_ASSERT(dctx);
|
||||
}
|
||||
ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, src, size);
|
||||
|
||||
#ifndef STATEFULL_FUZZING
|
||||
ZSTD_freeDCtx(dctx); dctx = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This fuzz target performs a zstd round-trip test (compress & decompress),
|
||||
* compares the result with the original, and calls abort() on corruption.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "fuzz_helpers.h"
|
||||
#include "zstd.h"
|
||||
|
||||
static const int kMaxClevel = 19;
|
||||
|
||||
static ZSTD_CCtx *cctx = NULL;
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
static void* cBuf = NULL;
|
||||
static void* rBuf = NULL;
|
||||
static size_t bufSize = 0;
|
||||
static uint32_t seed;
|
||||
|
||||
static size_t roundTripTest(void *result, size_t resultCapacity,
|
||||
void *compressed, size_t compressedCapacity,
|
||||
const void *src, size_t srcSize)
|
||||
{
|
||||
int const cLevel = FUZZ_rand(&seed) % kMaxClevel;
|
||||
size_t const cSize = ZSTD_compressCCtx(cctx, compressed, compressedCapacity,
|
||||
src, srcSize, cLevel);
|
||||
if (ZSTD_isError(cSize)) {
|
||||
fprintf(stderr, "Compression error: %s\n", ZSTD_getErrorName(cSize));
|
||||
return cSize;
|
||||
}
|
||||
return ZSTD_decompressDCtx(dctx, result, resultCapacity, compressed, cSize);
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
size_t const neededBufSize = ZSTD_compressBound(size);
|
||||
|
||||
seed = FUZZ_seed(src, size);
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBufSize > bufSize) {
|
||||
free(cBuf);
|
||||
free(rBuf);
|
||||
cBuf = malloc(neededBufSize);
|
||||
rBuf = malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(cBuf && rBuf);
|
||||
}
|
||||
if (!cctx) {
|
||||
cctx = ZSTD_createCCtx();
|
||||
FUZZ_ASSERT(cctx);
|
||||
}
|
||||
if (!dctx) {
|
||||
dctx = ZSTD_createDCtx();
|
||||
FUZZ_ASSERT(dctx);
|
||||
}
|
||||
|
||||
{
|
||||
size_t const result =
|
||||
roundTripTest(rBuf, neededBufSize, cBuf, neededBufSize, src, size);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(result), ZSTD_getErrorName(result));
|
||||
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
#ifndef STATEFULL_FUZZING
|
||||
ZSTD_freeCCtx(cctx); cctx = NULL;
|
||||
ZSTD_freeDCtx(dctx); dctx = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This fuzz target attempts to decompress the fuzzed data with the simple
|
||||
* decompression function to ensure the decompressor never crashes.
|
||||
*/
|
||||
|
||||
#define ZSTD_STATIC_LINKING_ONLY
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "fuzz_helpers.h"
|
||||
#include "zstd.h"
|
||||
|
||||
static size_t const kBufSize = ZSTD_BLOCKSIZE_ABSOLUTEMAX;
|
||||
|
||||
static ZSTD_DStream *dstream = NULL;
|
||||
static void* buf = NULL;
|
||||
uint32_t seed;
|
||||
|
||||
static ZSTD_outBuffer makeOutBuffer(void)
|
||||
{
|
||||
ZSTD_outBuffer buffer = { buf, 0, 0 };
|
||||
|
||||
buffer.size = (FUZZ_rand(&seed) % kBufSize) + 1;
|
||||
FUZZ_ASSERT(buffer.size <= kBufSize);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size)
|
||||
{
|
||||
ZSTD_inBuffer buffer = { *src, 0, 0 };
|
||||
|
||||
FUZZ_ASSERT(*size > 0);
|
||||
buffer.size = (FUZZ_rand(&seed) % *size) + 1;
|
||||
FUZZ_ASSERT(buffer.size <= *size);
|
||||
*src += buffer.size;
|
||||
*size -= buffer.size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
seed = FUZZ_seed(src, size);
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (!buf) {
|
||||
buf = malloc(kBufSize);
|
||||
FUZZ_ASSERT(buf);
|
||||
}
|
||||
|
||||
if (!dstream) {
|
||||
dstream = ZSTD_createDStream();
|
||||
FUZZ_ASSERT(dstream);
|
||||
FUZZ_ASSERT(!ZSTD_isError(ZSTD_initDStream(dstream)));
|
||||
} else {
|
||||
FUZZ_ASSERT(!ZSTD_isError(ZSTD_resetDStream(dstream)));
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
ZSTD_inBuffer in = makeInBuffer(&src, &size);
|
||||
while (in.pos != in.size) {
|
||||
ZSTD_outBuffer out = makeOutBuffer();
|
||||
size_t const rc = ZSTD_decompressStream(dstream, &out, &in);
|
||||
if (ZSTD_isError(rc)) goto error;
|
||||
if (rc == 0) FUZZ_ASSERT(!ZSTD_isError(ZSTD_resetDStream(dstream)));
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
#ifndef STATEFULL_FUZZING
|
||||
ZSTD_freeDStream(dstream); dstream = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This fuzz target performs a zstd round-trip test (compress & decompress),
|
||||
* compares the result with the original, and calls abort() on corruption.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "fuzz_helpers.h"
|
||||
#include "zstd.h"
|
||||
|
||||
static const int kMaxClevel = 19;
|
||||
|
||||
static ZSTD_CStream *cstream = NULL;
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
static uint8_t* cBuf = NULL;
|
||||
static uint8_t* rBuf = NULL;
|
||||
static size_t bufSize = 0;
|
||||
static uint32_t seed;
|
||||
|
||||
static ZSTD_outBuffer makeOutBuffer(uint8_t *dst, size_t capacity)
|
||||
{
|
||||
ZSTD_outBuffer buffer = { dst, 0, 0 };
|
||||
|
||||
FUZZ_ASSERT(capacity > 0);
|
||||
buffer.size = (FUZZ_rand(&seed) % capacity) + 1;
|
||||
FUZZ_ASSERT(buffer.size <= capacity);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size)
|
||||
{
|
||||
ZSTD_inBuffer buffer = { *src, 0, 0 };
|
||||
|
||||
FUZZ_ASSERT(*size > 0);
|
||||
buffer.size = (FUZZ_rand(&seed) % *size) + 1;
|
||||
FUZZ_ASSERT(buffer.size <= *size);
|
||||
*src += buffer.size;
|
||||
*size -= buffer.size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static size_t compress(uint8_t *dst, size_t capacity,
|
||||
const uint8_t *src, size_t srcSize)
|
||||
{
|
||||
int cLevel = FUZZ_rand(&seed) % kMaxClevel;
|
||||
size_t dstSize = 0;
|
||||
FUZZ_ASSERT(!ZSTD_isError(ZSTD_initCStream(cstream, cLevel)));
|
||||
|
||||
while (srcSize > 0) {
|
||||
ZSTD_inBuffer in = makeInBuffer(&src, &srcSize);
|
||||
/* Mode controls the action. If mode == -1 we pick a new mode */
|
||||
int mode = -1;
|
||||
while (in.pos < in.size) {
|
||||
ZSTD_outBuffer out = makeOutBuffer(dst, capacity);
|
||||
/* Previous action finished, pick a new mode. */
|
||||
if (mode == -1) mode = FUZZ_rand(&seed) % 10;
|
||||
switch (mode) {
|
||||
case 0: /* fall-though */
|
||||
case 1: /* fall-though */
|
||||
case 2: {
|
||||
size_t const ret = ZSTD_flushStream(cstream, &out);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(ret), ZSTD_getErrorName(ret));
|
||||
if (ret == 0) mode = -1;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
size_t ret = ZSTD_endStream(cstream, &out);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(ret), ZSTD_getErrorName(ret));
|
||||
/* Reset the compressor when the frame is finished */
|
||||
if (ret == 0) {
|
||||
cLevel = FUZZ_rand(&seed) % kMaxClevel;
|
||||
ret = ZSTD_initCStream(cstream, cLevel);
|
||||
FUZZ_ASSERT(!ZSTD_isError(ret));
|
||||
mode = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
size_t const ret = ZSTD_compressStream(cstream, &out, &in);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(ret), ZSTD_getErrorName(ret));
|
||||
mode = -1;
|
||||
}
|
||||
}
|
||||
dst += out.pos;
|
||||
dstSize += out.pos;
|
||||
capacity -= out.pos;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
ZSTD_outBuffer out = makeOutBuffer(dst, capacity);
|
||||
size_t const ret = ZSTD_endStream(cstream, &out);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(ret), ZSTD_getErrorName(ret));
|
||||
|
||||
dst += out.pos;
|
||||
dstSize += out.pos;
|
||||
capacity -= out.pos;
|
||||
if (ret == 0) break;
|
||||
}
|
||||
return dstSize;
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
size_t const neededBufSize = ZSTD_compressBound(size) * 2;
|
||||
|
||||
seed = FUZZ_seed(src, size);
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBufSize > bufSize) {
|
||||
free(cBuf);
|
||||
free(rBuf);
|
||||
cBuf = (uint8_t*)malloc(neededBufSize);
|
||||
rBuf = (uint8_t*)malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(cBuf && rBuf);
|
||||
}
|
||||
if (!cstream) {
|
||||
cstream = ZSTD_createCStream();
|
||||
FUZZ_ASSERT(cstream);
|
||||
}
|
||||
if (!dctx) {
|
||||
dctx = ZSTD_createDCtx();
|
||||
FUZZ_ASSERT(dctx);
|
||||
}
|
||||
|
||||
{
|
||||
size_t const cSize = compress(cBuf, neededBufSize, src, size);
|
||||
size_t const rSize =
|
||||
ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
|
||||
FUZZ_ASSERT_MSG(!ZSTD_isError(rSize), ZSTD_getErrorName(rSize));
|
||||
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
|
||||
#ifndef STATEFULL_FUZZING
|
||||
ZSTD_freeCStream(cstream); cstream = NULL;
|
||||
ZSTD_freeDCtx(dctx); dctx = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue