[fuzz] Add HC fuzzers for round trip, compress, and streaming
This commit is contained in:
parent
399a80d48e
commit
675ef9a9fc
@ -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)
|
||||
|
||||
|
57
ossfuzz/compress_hc_fuzzer.c
Normal file
57
ossfuzz/compress_hc_fuzzer.c
Normal file
@ -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;
|
||||
}
|
@ -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)
|
||||
|
39
ossfuzz/round_trip_hc_fuzzer.c
Normal file
39
ossfuzz/round_trip_hc_fuzzer.c
Normal file
@ -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;
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user