From 1738b50443d1446c9c80ebd7c9272427f2644809 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Fri, 10 Jun 2022 19:45:21 +0000 Subject: fuzz-test: add fuzz test for uncompressed api add a fuzzing test for uncompressed frame api Signed-off-by: Alexander Mohr --- lib/lz4frame.c | 3 +- ossfuzz/Makefile | 1 + ossfuzz/fuzz.h | 5 - ossfuzz/round_trip_frame_uncompressed_fuzzer.c | 127 +++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 ossfuzz/round_trip_frame_uncompressed_fuzzer.c diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 0c78a1f..c7e0595 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -129,8 +129,7 @@ static int g_debuglog_enable = 1; **************************************/ #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include -#include -typedef uint8_t BYTE; + typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; diff --git a/ossfuzz/Makefile b/ossfuzz/Makefile index 2ec1675..deb2938 100644 --- a/ossfuzz/Makefile +++ b/ossfuzz/Makefile @@ -45,6 +45,7 @@ FUZZERS := \ round_trip_hc_fuzzer \ compress_frame_fuzzer \ round_trip_frame_fuzzer \ + round_trip_frame_uncompressed_fuzzer \ decompress_frame_fuzzer .PHONY: all diff --git a/ossfuzz/fuzz.h b/ossfuzz/fuzz.h index 917a304..eefac63 100644 --- a/ossfuzz/fuzz.h +++ b/ossfuzz/fuzz.h @@ -41,11 +41,6 @@ extern "C" { int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size); -/** - * Test if injection of uncompressed data into a stream is working properly - */ -int LLVMFuzzerUncompressedDataInjection(const uint8_t *data, size_t size) - #ifdef __cplusplus } #endif diff --git a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c new file mode 100644 index 0000000..631d149 --- /dev/null +++ b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c @@ -0,0 +1,127 @@ +/** + * This fuzz target performs a lz4 round-trip test (compress & decompress), + * compares the result with the original, and calls abort() on corruption. + */ + +#include +#include +#include +#include + +#include "fuzz_data_producer.h" +#include "fuzz_helpers.h" +#include "lz4.h" +#include "lz4_helpers.h" +#include "lz4frame.h" +#include "lz4frame_static.h" + +static void decompress_data(LZ4F_dctx *dctx, void *src, void *dst, + size_t dstCapacity, size_t readSize) { + size_t ret = 1; + const void *srcPtr = (const char *)src; + void *dstPtr = (char *)dst; + const void *const srcEnd = (const char *)srcPtr + readSize; + + while (ret != 0) { + while (srcPtr < srcEnd && ret != 0) { + /* Any data within dst has been flushed at this stage */ + size_t dstSize = dstCapacity; + size_t srcSize = (const char *)srcEnd - (const char *)srcPtr; + ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize, + /* LZ4F_decompressOptions_t */ NULL); + FUZZ_ASSERT(!LZ4F_isError(ret)); + + /* Update input */ + srcPtr = (const char *)srcPtr + srcSize; + dstPtr = (char *)dstPtr + dstSize; + } + + FUZZ_ASSERT(srcPtr <= srcEnd); + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); + LZ4F_preferences_t const prefs = FUZZ_dataProducer_preferences(producer); + size = FUZZ_dataProducer_remainingBytes(producer); + + uint8_t *uncompressedData = malloc(size); + size_t uncompressedOffset = rand() % (size + 1); + + FUZZ_dataProducer_t *uncompressedProducer = + FUZZ_dataProducer_create(uncompressedData, size); + size_t uncompressedSize = + FUZZ_dataProducer_remainingBytes(uncompressedProducer); + + size_t const dstCapacity = + LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) + + uncompressedSize; + char *const dst = (char *)malloc(dstCapacity); + size_t rtCapacity = dstCapacity; + char *const rt = (char *)malloc(rtCapacity); + + FUZZ_ASSERT(dst); + FUZZ_ASSERT(rt); + + /* Compression must succeed and round trip correctly. */ + LZ4F_compressionContext_t ctx; + size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + FUZZ_ASSERT(!LZ4F_isError(ctxCreation)); + + size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs); + FUZZ_ASSERT(!LZ4F_isError(headerSize)); + size_t compressedSize = headerSize; + + /* Compress data before uncompressed offset */ + size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, + data, uncompressedOffset, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Add uncompressed data */ + lz4Return = LZ4F_uncompressedUpdate(ctx, dst + compressedSize, dstCapacity, + uncompressedData, uncompressedSize, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Compress data after uncompressed offset */ + lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, + data + uncompressedOffset, + size - uncompressedOffset, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Finish compression */ + lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + LZ4F_decompressOptions_t opts; + memset(&opts, 0, sizeof(opts)); + opts.stableDst = 1; + LZ4F_dctx *dctx; + LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); + FUZZ_ASSERT(dctx); + + decompress_data(dctx, dst, rt, rtCapacity, compressedSize); + + LZ4F_freeDecompressionContext(dctx); + + char *const expectedData = (char *)malloc(size + uncompressedSize); + memcpy(expectedData, data, uncompressedOffset); + memcpy(expectedData + uncompressedOffset, uncompressedData, uncompressedSize); + memcpy(expectedData + uncompressedOffset + uncompressedSize, + data + uncompressedOffset, size - uncompressedOffset); + + FUZZ_ASSERT_MSG(!memcmp(expectedData, rt, size), "Corruption!"); + free(expectedData); + + free(dst); + free(rt); + free(uncompressedData); + + FUZZ_dataProducer_free(producer); + FUZZ_dataProducer_free(uncompressedProducer); + LZ4F_freeCompressionContext(ctx); + return 0; +} -- cgit v0.12