summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Terrell <terrelln@fb.com>2019-07-18 00:33:31 (GMT)
committerNick Terrell <terrelln@fb.com>2019-07-18 19:29:15 (GMT)
commit9b258abd93e19dece681ea59db9c31e1e27635ff (patch)
treec9acde136d70af257af23989360fdb1fd2039098
parent40943ba0c90483dbe229299c39d048ae79f08246 (diff)
downloadlz4-9b258abd93e19dece681ea59db9c31e1e27635ff.zip
lz4-9b258abd93e19dece681ea59db9c31e1e27635ff.tar.gz
lz4-9b258abd93e19dece681ea59db9c31e1e27635ff.tar.bz2
[fuzz] Add a streaming round trip fuzzer
-rw-r--r--ossfuzz/round_trip_stream_fuzzer.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/ossfuzz/round_trip_stream_fuzzer.c b/ossfuzz/round_trip_stream_fuzzer.c
new file mode 100644
index 0000000..4facfc9
--- /dev/null
+++ b/ossfuzz/round_trip_stream_fuzzer.c
@@ -0,0 +1,217 @@
+/**
+ * This fuzz target performs a lz4 streaming 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"
+#define LZ4_STATIC_LINKING_ONLY
+#include "lz4.h"
+
+typedef struct {
+ char const* buf;
+ size_t size;
+ size_t pos;
+} const_cursor_t;
+
+typedef struct {
+ char* buf;
+ size_t size;
+ size_t pos;
+} cursor_t;
+
+typedef struct {
+ LZ4_stream_t* cstream;
+ LZ4_streamDecode_t* dstream;
+ const_cursor_t data;
+ cursor_t compressed;
+ cursor_t roundTrip;
+ uint32_t seed;
+} state_t;
+
+cursor_t cursor_create(size_t size)
+{
+ cursor_t cursor;
+ cursor.buf = (char*)malloc(size);
+ cursor.size = size;
+ cursor.pos = 0;
+ FUZZ_ASSERT(cursor.buf);
+ return cursor;
+}
+
+void cursor_free(cursor_t cursor)
+{
+ free(cursor.buf);
+}
+
+state_t state_create(char const* data, size_t size, uint32_t seed)
+{
+ state_t state;
+
+ state.seed = seed;
+
+ state.data.buf = (char const*)data;
+ state.data.size = size;
+ state.data.pos = 0;
+
+ /* Extra margin because we are streaming. */
+ state.compressed = cursor_create(1024 + 2 * LZ4_compressBound(size));
+ state.roundTrip = cursor_create(size);
+
+ state.cstream = LZ4_createStream();
+ FUZZ_ASSERT(state.cstream);
+ state.dstream = LZ4_createStreamDecode();
+ FUZZ_ASSERT(state.dstream);
+
+ return state;
+}
+
+void state_free(state_t state)
+{
+ cursor_free(state.compressed);
+ cursor_free(state.roundTrip);
+ LZ4_freeStream(state.cstream);
+ LZ4_freeStreamDecode(state.dstream);
+}
+
+static void state_reset(state_t* state, uint32_t seed)
+{
+ LZ4_resetStream_fast(state->cstream);
+ LZ4_setStreamDecode(state->dstream, NULL, 0);
+ state->data.pos = 0;
+ state->compressed.pos = 0;
+ state->roundTrip.pos = 0;
+ state->seed = seed;
+}
+
+static void state_decompress(state_t* state, char const* src, int srcSize)
+{
+ char* dst = state->roundTrip.buf + state->roundTrip.pos;
+ int const dstCapacity = state->roundTrip.size - state->roundTrip.pos;
+ int const dSize = LZ4_decompress_safe_continue(state->dstream, src, dst,
+ srcSize, dstCapacity);
+ FUZZ_ASSERT(dSize >= 0);
+ state->roundTrip.pos += dSize;
+}
+
+static void state_checkRoundTrip(state_t const* state)
+{
+ char const* data = state->data.buf;
+ size_t const size = state->data.size;
+ FUZZ_ASSERT_MSG(size == state->roundTrip.pos, "Incorrect size!");
+ FUZZ_ASSERT_MSG(!memcmp(data, state->roundTrip.buf, size), "Corruption!");
+}
+
+/**
+ * Picks a dictionary size and trims the dictionary off of the data.
+ */
+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;
+ return dictSize;
+}
+
+static void state_prefixRoundTrip(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_fast_continue(state->cstream, src, dst,
+ srcSize, dstCapacity, 0);
+ FUZZ_ASSERT(cSize > 0);
+ state->data.pos += srcSize;
+ state->compressed.pos += cSize;
+ state_decompress(state, dst, cSize);
+ }
+}
+
+static void state_extDictRoundTrip(state_t* state)
+{
+ int i = 0;
+ cursor_t data2 = cursor_create(state->data.size);
+ 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_fast_continue(state->cstream, src, dst,
+ srcSize, dstCapacity, 0);
+ FUZZ_ASSERT(cSize > 0);
+ state->data.pos += srcSize;
+ state->compressed.pos += cSize;
+ state_decompress(state, dst, cSize);
+ }
+ cursor_free(data2);
+}
+
+static void state_randomRoundTrip(state_t* state)
+{
+ if (FUZZ_rand32(&state->seed, 0, 1)) {
+ state_prefixRoundTrip(state);
+ } else {
+ state_extDictRoundTrip(state);
+ }
+}
+
+static void state_loadDictRoundTrip(state_t* state)
+{
+ char const* dict = state->compressed.buf;
+ size_t const dictSize = state_trimDict(state);
+ LZ4_loadDict(state->cstream, dict, dictSize);
+ LZ4_setStreamDecode(state->dstream, dict, dictSize);
+ state_randomRoundTrip(state);
+}
+
+static void state_attachDictRoundTrip(state_t* state)
+{
+ char const* dict = state->compressed.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);
+ LZ4_freeStream(dictStream);
+}
+
+typedef void (*round_trip_t)(state_t* state);
+
+round_trip_t roundTrips[] = {
+ &state_prefixRoundTrip,
+ &state_extDictRoundTrip,
+ &state_loadDictRoundTrip,
+ &state_attachDictRoundTrip,
+};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ uint32_t seed = FUZZ_seed(&data, &size);
+ state_t state = state_create((char const*)data, size, seed);
+ const int n = sizeof(roundTrips) / sizeof(round_trip_t);
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ roundTrips[i](&state);
+ state_checkRoundTrip(&state);
+ state_reset(&state, seed);
+ }
+
+ state_free(state);
+
+ return 0;
+}