summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ossfuzz/Makefile4
-rw-r--r--ossfuzz/compress_hc_fuzzer.c57
-rw-r--r--ossfuzz/fuzz_helpers.h9
-rw-r--r--ossfuzz/round_trip_hc_fuzzer.c39
-rw-r--r--ossfuzz/round_trip_stream_fuzzer.c107
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);