diff options
author | Nick Terrell <terrelln@fb.com> | 2019-07-18 01:31:44 (GMT) |
---|---|---|
committer | Nick Terrell <terrelln@fb.com> | 2019-07-18 19:29:15 (GMT) |
commit | 675ef9a9fc9899667d0fe9b7b2a66e402e870a6d (patch) | |
tree | c078d8a714009521b976ff07bbf77802f48e5c86 | |
parent | 399a80d48e4352aeae99eb5cbd799bcee6978a3d (diff) | |
download | lz4-675ef9a9fc9899667d0fe9b7b2a66e402e870a6d.zip lz4-675ef9a9fc9899667d0fe9b7b2a66e402e870a6d.tar.gz lz4-675ef9a9fc9899667d0fe9b7b2a66e402e870a6d.tar.bz2 |
[fuzz] Add HC fuzzers for round trip, compress, and streaming
-rw-r--r-- | ossfuzz/Makefile | 4 | ||||
-rw-r--r-- | ossfuzz/compress_hc_fuzzer.c | 57 | ||||
-rw-r--r-- | ossfuzz/fuzz_helpers.h | 9 | ||||
-rw-r--r-- | ossfuzz/round_trip_hc_fuzzer.c | 39 | ||||
-rw-r--r-- | ossfuzz/round_trip_stream_fuzzer.c | 107 |
5 files changed, 202 insertions, 14 deletions
diff --git a/ossfuzz/Makefile b/ossfuzz/Makefile index 2bb40ec..9974b81 100644 --- a/ossfuzz/Makefile +++ b/ossfuzz/Makefile @@ -39,7 +39,9 @@ FUZZERS := \ compress_fuzzer \ decompress_fuzzer \ round_trip_fuzzer \ - round_trip_stream_fuzzer + round_trip_stream_fuzzer \ + compress_hc_fuzzer \ + round_trip_hc_fuzzer all: $(FUZZERS) diff --git a/ossfuzz/compress_hc_fuzzer.c b/ossfuzz/compress_hc_fuzzer.c new file mode 100644 index 0000000..4841367 --- /dev/null +++ b/ossfuzz/compress_hc_fuzzer.c @@ -0,0 +1,57 @@ +/** + * This fuzz target attempts to compress the fuzzed data with the simple + * compression function with an output buffer that may be too small to + * ensure that the compressor never crashes. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "fuzz_helpers.h" +#include "lz4.h" +#include "lz4hc.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint32_t seed = FUZZ_seed(&data, &size); + size_t const dstCapacity = FUZZ_rand32(&seed, 0, LZ4_compressBound(size)); + char* const dst = (char*)malloc(dstCapacity); + char* const rt = (char*)malloc(size); + int const level = FUZZ_rand32(&seed, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); + + FUZZ_ASSERT(dst); + FUZZ_ASSERT(rt); + + /* If compression succeeds it must round trip correctly. */ + { + int const dstSize = LZ4_compress_HC((const char*)data, dst, size, + dstCapacity, level); + if (dstSize > 0) { + int const rtSize = LZ4_decompress_safe(dst, rt, dstSize, size); + FUZZ_ASSERT_MSG(rtSize == size, "Incorrect regenerated size"); + FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!"); + } + } + + if (dstCapacity > 0) { + /* Compression succeeds and must round trip correctly. */ + void* state = malloc(LZ4_sizeofStateHC()); + FUZZ_ASSERT(state); + int compressedSize = size; + int const dstSize = LZ4_compress_HC_destSize(state, (const char*)data, + dst, &compressedSize, + dstCapacity, level); + FUZZ_ASSERT(dstSize > 0); + int const rtSize = LZ4_decompress_safe(dst, rt, dstSize, size); + FUZZ_ASSERT_MSG(rtSize == compressedSize, "Incorrect regenerated size"); + FUZZ_ASSERT_MSG(!memcmp(data, rt, compressedSize), "Corruption!"); + free(state); + } + + free(dst); + free(rt); + + return 0; +} diff --git a/ossfuzz/fuzz_helpers.h b/ossfuzz/fuzz_helpers.h index 626f209..c4a8645 100644 --- a/ossfuzz/fuzz_helpers.h +++ b/ossfuzz/fuzz_helpers.h @@ -24,8 +24,13 @@ extern "C" { #endif -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define LZ4_COMMONDEFS_ONLY +#ifndef LZ4_SRC_INCLUDED +#include "lz4.c" /* LZ4_count, constants, mem */ +#endif + +#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) diff --git a/ossfuzz/round_trip_hc_fuzzer.c b/ossfuzz/round_trip_hc_fuzzer.c new file mode 100644 index 0000000..325cdf0 --- /dev/null +++ b/ossfuzz/round_trip_hc_fuzzer.c @@ -0,0 +1,39 @@ +/** + * This fuzz target performs a lz4 round-trip test (compress & decompress), + * compares the result with the original, and calls abort() on corruption. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "fuzz_helpers.h" +#include "lz4.h" +#include "lz4hc.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + uint32_t seed = FUZZ_seed(&data, &size); + size_t const dstCapacity = LZ4_compressBound(size); + char* const dst = (char*)malloc(dstCapacity); + char* const rt = (char*)malloc(size); + int const level = FUZZ_rand32(&seed, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); + + FUZZ_ASSERT(dst); + FUZZ_ASSERT(rt); + + /* Compression must succeed and round trip correctly. */ + int const dstSize = LZ4_compress_HC((const char*)data, dst, size, + dstCapacity, level); + FUZZ_ASSERT(dstSize > 0); + + int const rtSize = LZ4_decompress_safe(dst, rt, dstSize, size); + FUZZ_ASSERT_MSG(rtSize == size, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!"); + + free(dst); + free(rt); + + return 0; +} diff --git a/ossfuzz/round_trip_stream_fuzzer.c b/ossfuzz/round_trip_stream_fuzzer.c index 4facfc9..abfcd2d 100644 --- a/ossfuzz/round_trip_stream_fuzzer.c +++ b/ossfuzz/round_trip_stream_fuzzer.c @@ -12,6 +12,8 @@ #include "fuzz_helpers.h" #define LZ4_STATIC_LINKING_ONLY #include "lz4.h" +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" typedef struct { char const* buf; @@ -27,11 +29,13 @@ typedef struct { typedef struct { LZ4_stream_t* cstream; + LZ4_streamHC_t* cstreamHC; LZ4_streamDecode_t* dstream; const_cursor_t data; cursor_t compressed; cursor_t roundTrip; uint32_t seed; + int level; } state_t; cursor_t cursor_create(size_t size) @@ -44,6 +48,8 @@ cursor_t cursor_create(size_t size) return cursor; } +typedef void (*round_trip_t)(state_t* state); + void cursor_free(cursor_t cursor) { free(cursor.buf); @@ -65,6 +71,8 @@ state_t state_create(char const* data, size_t size, uint32_t seed) state.cstream = LZ4_createStream(); FUZZ_ASSERT(state.cstream); + state.cstreamHC = LZ4_createStreamHC(); + FUZZ_ASSERT(state.cstream); state.dstream = LZ4_createStreamDecode(); FUZZ_ASSERT(state.dstream); @@ -76,12 +84,15 @@ void state_free(state_t state) cursor_free(state.compressed); cursor_free(state.roundTrip); LZ4_freeStream(state.cstream); + LZ4_freeStreamHC(state.cstreamHC); LZ4_freeStreamDecode(state.dstream); } static void state_reset(state_t* state, uint32_t seed) { + state->level = FUZZ_rand32(&seed, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX); LZ4_resetStream_fast(state->cstream); + LZ4_resetStreamHC_fast(state->cstreamHC, state->level); LZ4_setStreamDecode(state->dstream, NULL, 0); state->data.pos = 0; state->compressed.pos = 0; @@ -109,14 +120,19 @@ static void state_checkRoundTrip(state_t const* state) /** * Picks a dictionary size and trims the dictionary off of the data. + * We copy the dictionary to the roundTrip so our validation passes. */ static size_t state_trimDict(state_t* state) { /* 64 KB is the max dict size, allow slightly beyond that to test trim. */ uint32_t maxDictSize = MIN(70 * 1024, state->data.size); size_t const dictSize = FUZZ_rand32(&state->seed, 0, maxDictSize); - FUZZ_ASSERT(state->compressed.pos == 0); - state->compressed.pos += dictSize; + DEBUGLOG(2, "dictSize = %zu", dictSize); + FUZZ_ASSERT(state->data.pos == 0); + FUZZ_ASSERT(state->roundTrip.pos == 0); + memcpy(state->roundTrip.buf, state->data.buf, dictSize); + state->data.pos += dictSize; + state->roundTrip.pos += dictSize; return dictSize; } @@ -159,43 +175,111 @@ static void state_extDictRoundTrip(state_t* state) cursor_free(data2); } -static void state_randomRoundTrip(state_t* state) +static void state_randomRoundTrip(state_t* state, round_trip_t rt0, + round_trip_t rt1) { if (FUZZ_rand32(&state->seed, 0, 1)) { - state_prefixRoundTrip(state); + rt0(state); } else { - state_extDictRoundTrip(state); + rt1(state); } } static void state_loadDictRoundTrip(state_t* state) { - char const* dict = state->compressed.buf; + char const* dict = state->data.buf; size_t const dictSize = state_trimDict(state); LZ4_loadDict(state->cstream, dict, dictSize); LZ4_setStreamDecode(state->dstream, dict, dictSize); - state_randomRoundTrip(state); + state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip); } static void state_attachDictRoundTrip(state_t* state) { - char const* dict = state->compressed.buf; + char const* dict = state->data.buf; size_t const dictSize = state_trimDict(state); LZ4_stream_t* dictStream = LZ4_createStream(); LZ4_loadDict(dictStream, dict, dictSize); LZ4_attach_dictionary(state->cstream, dictStream); LZ4_setStreamDecode(state->dstream, dict, dictSize); - state_randomRoundTrip(state); + state_randomRoundTrip(state, state_prefixRoundTrip, state_extDictRoundTrip); LZ4_freeStream(dictStream); } -typedef void (*round_trip_t)(state_t* state); +static void state_prefixHCRoundTrip(state_t* state) +{ + while (state->data.pos != state->data.size) { + char const* src = state->data.buf + state->data.pos; + char* dst = state->compressed.buf + state->compressed.pos; + int const srcRemaining = state->data.size - state->data.pos; + int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining); + int const dstCapacity = state->compressed.size - state->compressed.pos; + int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst, + srcSize, dstCapacity); + FUZZ_ASSERT(cSize > 0); + state->data.pos += srcSize; + state->compressed.pos += cSize; + state_decompress(state, dst, cSize); + } +} + +static void state_extDictHCRoundTrip(state_t* state) +{ + int i = 0; + cursor_t data2 = cursor_create(state->data.size); + DEBUGLOG(2, "extDictHC"); + memcpy(data2.buf, state->data.buf, state->data.size); + while (state->data.pos != state->data.size) { + char const* data = (i++ & 1) ? state->data.buf : data2.buf; + char const* src = data + state->data.pos; + char* dst = state->compressed.buf + state->compressed.pos; + int const srcRemaining = state->data.size - state->data.pos; + int const srcSize = FUZZ_rand32(&state->seed, 0, srcRemaining); + int const dstCapacity = state->compressed.size - state->compressed.pos; + int const cSize = LZ4_compress_HC_continue(state->cstreamHC, src, dst, + srcSize, dstCapacity); + FUZZ_ASSERT(cSize > 0); + DEBUGLOG(2, "srcSize = %d", srcSize); + state->data.pos += srcSize; + state->compressed.pos += cSize; + state_decompress(state, dst, cSize); + } + cursor_free(data2); +} + +static void state_loadDictHCRoundTrip(state_t* state) +{ + char const* dict = state->data.buf; + size_t const dictSize = state_trimDict(state); + LZ4_loadDictHC(state->cstreamHC, dict, dictSize); + LZ4_setStreamDecode(state->dstream, dict, dictSize); + state_randomRoundTrip(state, state_prefixHCRoundTrip, + state_extDictHCRoundTrip); +} + +static void state_attachDictHCRoundTrip(state_t* state) +{ + char const* dict = state->data.buf; + size_t const dictSize = state_trimDict(state); + LZ4_streamHC_t* dictStream = LZ4_createStreamHC(); + LZ4_setCompressionLevel(dictStream, state->level); + LZ4_loadDictHC(dictStream, dict, dictSize); + LZ4_attach_HC_dictionary(state->cstreamHC, dictStream); + LZ4_setStreamDecode(state->dstream, dict, dictSize); + state_randomRoundTrip(state, state_prefixHCRoundTrip, + state_extDictHCRoundTrip); + LZ4_freeStreamHC(dictStream); +} round_trip_t roundTrips[] = { &state_prefixRoundTrip, &state_extDictRoundTrip, &state_loadDictRoundTrip, &state_attachDictRoundTrip, + &state_prefixHCRoundTrip, + &state_extDictHCRoundTrip, + &state_loadDictHCRoundTrip, + &state_attachDictHCRoundTrip, }; int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) @@ -206,9 +290,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) int i; for (i = 0; i < n; ++i) { + DEBUGLOG(2, "Round trip %d", i); + state_reset(&state, seed); roundTrips[i](&state); state_checkRoundTrip(&state); - state_reset(&state, seed); } state_free(state); |