diff options
Diffstat (limited to 'ossfuzz')
-rw-r--r-- | ossfuzz/.gitignore | 8 | ||||
-rw-r--r-- | ossfuzz/Makefile | 1 | ||||
-rw-r--r-- | ossfuzz/decompress_fuzzer.c | 20 | ||||
-rw-r--r-- | ossfuzz/fuzz_helpers.h | 5 | ||||
-rw-r--r-- | ossfuzz/round_trip_frame_uncompressed_fuzzer.c | 134 | ||||
-rw-r--r-- | ossfuzz/round_trip_fuzzer.c | 72 |
6 files changed, 230 insertions, 10 deletions
diff --git a/ossfuzz/.gitignore b/ossfuzz/.gitignore new file mode 100644 index 0000000..0ef0d2b --- /dev/null +++ b/ossfuzz/.gitignore @@ -0,0 +1,8 @@ + +# build artefacts +round_trip_frame_uncompressed_fuzzer + +# test artefacts + +# local tests + 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/decompress_fuzzer.c b/ossfuzz/decompress_fuzzer.c index 6f48e30..490b3fd 100644 --- a/ossfuzz/decompress_fuzzer.c +++ b/ossfuzz/decompress_fuzzer.c @@ -39,7 +39,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* No dictionary. */ LZ4_decompress_safe_usingDict((char const*)data, dst, size, dstCapacity, NULL, 0); - /* Small external dictonary. */ + /* Small external dictionary. */ LZ4_decompress_safe_usingDict((char const*)data, dst, size, dstCapacity, smallDict, smallDictSize); /* Large external dictionary. */ @@ -49,11 +49,27 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) LZ4_decompress_safe_usingDict((char const*)dataAfterDict, dst, size, dstCapacity, smallDict, smallDictSize); /* Large prefix. */ - LZ4_decompress_safe_usingDict((char const*)data, dst, size, + LZ4_decompress_safe_usingDict((char const*)dataAfterDict, dst, size, dstCapacity, largeDict, largeDictSize); /* Partial decompression. */ LZ4_decompress_safe_partial((char const*)data, dst, size, dstCapacity, dstCapacity); + /* Partial decompression using each possible dictionary configuration. */ + /* Partial decompression with no dictionary. */ + LZ4_decompress_safe_partial_usingDict((char const*)data, dst, size, + dstCapacity, dstCapacity, NULL, 0); + /* Partial decompression with small external dictionary. */ + LZ4_decompress_safe_partial_usingDict((char const*)data, dst, size, + dstCapacity, dstCapacity, smallDict, smallDictSize); + /* Partial decompression with large external dictionary. */ + LZ4_decompress_safe_partial_usingDict((char const*)data, dst, size, + dstCapacity, dstCapacity, largeDict, largeDictSize); + /* Partial decompression with small prefix. */ + LZ4_decompress_safe_partial_usingDict((char const*)dataAfterDict, dst, size, + dstCapacity, dstCapacity, smallDict, smallDictSize); + /* Partial decompression wtih large prefix. */ + LZ4_decompress_safe_partial_usingDict((char const*)dataAfterDict, dst, size, + dstCapacity, dstCapacity, largeDict, largeDictSize); free(dst); free(dict); FUZZ_dataProducer_free(producer); diff --git a/ossfuzz/fuzz_helpers.h b/ossfuzz/fuzz_helpers.h index c4a8645..efd9acf 100644 --- a/ossfuzz/fuzz_helpers.h +++ b/ossfuzz/fuzz_helpers.h @@ -4,7 +4,8 @@ * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found - * in the COPYING file in the root directory of this source tree). + * in the COPYING file in the root directory of this source tree), + * meaning you may select, at your option, one of the above-listed licenses. */ /** @@ -81,7 +82,7 @@ FUZZ_STATIC uint32_t FUZZ_rand(uint32_t *state) { return rand32 >> 5; } -/* Returns a random numer in the range [min, max]. */ +/* Returns a random number in the range [min, max]. */ FUZZ_STATIC uint32_t FUZZ_rand32(uint32_t *state, uint32_t min, uint32_t max) { uint32_t random = FUZZ_rand(state); return min + (random % (max - min + 1)); diff --git a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c new file mode 100644 index 0000000..76a99d2 --- /dev/null +++ b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c @@ -0,0 +1,134 @@ +/** + * 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_data_producer.h" +#include "fuzz_helpers.h" +#include "lz4.h" +#include "lz4_helpers.h" +#include "lz4frame.h" +#include "lz4frame_static.h" + +static void decompress(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); + } +} + +static void compress_round_trip(const uint8_t *data, size_t size, + FUZZ_dataProducer_t *producer, LZ4F_preferences_t const prefs) { + + // Choose random uncompressed offset start and end by producing seeds from random data, calculate the remaining + // data size that will be used for compression later and use the seeds to actually calculate the offsets + size_t const uncompressedOffsetSeed = FUZZ_dataProducer_retrieve32(producer); + size_t const uncompressedEndOffsetSeed = FUZZ_dataProducer_retrieve32(producer); + size = FUZZ_dataProducer_remainingBytes(producer); + + size_t const uncompressedOffset = FUZZ_getRange_from_uint32(uncompressedOffsetSeed, 0, size); + size_t const uncompressedEndOffset = FUZZ_getRange_from_uint32(uncompressedEndOffsetSeed, uncompressedOffset, size); + size_t const uncompressedSize = uncompressedEndOffset - uncompressedOffset; + FUZZ_ASSERT(uncompressedOffset <= uncompressedEndOffset); + FUZZ_ASSERT(uncompressedEndOffset <= size); + + const uint8_t *const uncompressedData = data + uncompressedOffset; + + 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 + uncompressedEndOffset, + size - uncompressedEndOffset, 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(dctx, dst, rt, rtCapacity, compressedSize); + + LZ4F_freeDecompressionContext(dctx); + + FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!"); + + free(dst); + free(rt); + + FUZZ_dataProducer_free(producer); + LZ4F_freeCompressionContext(ctx); +} + +static void compress_independent_block_mode(const uint8_t *data, size_t size) { + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); + LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); + prefs.frameInfo.blockMode = LZ4F_blockIndependent; + compress_round_trip(data, size, producer, prefs); +} + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + compress_independent_block_mode(data, size); + return 0; +} diff --git a/ossfuzz/round_trip_fuzzer.c b/ossfuzz/round_trip_fuzzer.c index 6307058..6236201 100644 --- a/ossfuzz/round_trip_fuzzer.c +++ b/ossfuzz/round_trip_fuzzer.c @@ -20,11 +20,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) size_t const partialCapacity = FUZZ_getRange_from_uint32(partialCapacitySeed, 0, size); size_t const dstCapacity = LZ4_compressBound(size); - - char* const dst = (char*)malloc(dstCapacity); + size_t const largeSize = 64 * 1024 - 1; + size_t const smallSize = 1024; + char* const dstPlusLargePrefix = (char*)malloc(dstCapacity + largeSize); + FUZZ_ASSERT(dstPlusLargePrefix); + char* const dstPlusSmallPrefix = dstPlusLargePrefix + largeSize - smallSize; + char* const largeDict = (char*)malloc(largeSize); + FUZZ_ASSERT(largeDict); + char* const smallDict = largeDict + largeSize - smallSize; + char* const dst = dstPlusLargePrefix + largeSize; char* const rt = (char*)malloc(size); - - FUZZ_ASSERT(dst); FUZZ_ASSERT(rt); /* Compression must succeed and round trip correctly. */ @@ -47,9 +52,64 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); free(partial); } + /* Partial decompression using dict with no dict. */ + { + char* const partial = (char*)malloc(partialCapacity); + FUZZ_ASSERT(partial); + int const partialSize = LZ4_decompress_safe_partial_usingDict( + dst, partial, dstSize, partialCapacity, partialCapacity, NULL, 0); + FUZZ_ASSERT(partialSize >= 0); + FUZZ_ASSERT_MSG(partialSize == partialCapacity, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); + free(partial); + } + /* Partial decompression using dict with small prefix as dict */ + { + char* const partial = (char*)malloc(partialCapacity); + FUZZ_ASSERT(partial); + int const partialSize = LZ4_decompress_safe_partial_usingDict( + dst, partial, dstSize, partialCapacity, partialCapacity, dstPlusSmallPrefix, smallSize); + FUZZ_ASSERT(partialSize >= 0); + FUZZ_ASSERT_MSG(partialSize == partialCapacity, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); + free(partial); + } + /* Partial decompression using dict with large prefix as dict */ + { + char* const partial = (char*)malloc(partialCapacity); + FUZZ_ASSERT(partial); + int const partialSize = LZ4_decompress_safe_partial_usingDict( + dst, partial, dstSize, partialCapacity, partialCapacity, dstPlusLargePrefix, largeSize); + FUZZ_ASSERT(partialSize >= 0); + FUZZ_ASSERT_MSG(partialSize == partialCapacity, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); + free(partial); + } + /* Partial decompression using dict with small external dict */ + { + char* const partial = (char*)malloc(partialCapacity); + FUZZ_ASSERT(partial); + int const partialSize = LZ4_decompress_safe_partial_usingDict( + dst, partial, dstSize, partialCapacity, partialCapacity, smallDict, smallSize); + FUZZ_ASSERT(partialSize >= 0); + FUZZ_ASSERT_MSG(partialSize == partialCapacity, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); + free(partial); + } + /* Partial decompression using dict with large external dict */ + { + char* const partial = (char*)malloc(partialCapacity); + FUZZ_ASSERT(partial); + int const partialSize = LZ4_decompress_safe_partial_usingDict( + dst, partial, dstSize, partialCapacity, partialCapacity, largeDict, largeSize); + FUZZ_ASSERT(partialSize >= 0); + FUZZ_ASSERT_MSG(partialSize == partialCapacity, "Incorrect size"); + FUZZ_ASSERT_MSG(!memcmp(data, partial, partialSize), "Corruption!"); + free(partial); + } - - free(dst); + free(dstPlusLargePrefix); + free(largeDict); free(rt); FUZZ_dataProducer_free(producer); |