1/**
2 * This fuzz target performs a lz4 round-trip test (compress & decompress),
3 * compares the result with the original, and calls abort() on corruption.
4 */
5
6#include <stddef.h>
7#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "fuzz_data_producer.h"
12#include "fuzz_helpers.h"
13#include "lz4.h"
14#include "lz4_helpers.h"
15#include "lz4frame.h"
16#include "lz4frame_static.h"
17
18static void decompress(LZ4F_dctx *dctx, void *src, void *dst,
19                       size_t dstCapacity, size_t readSize) {
20    size_t ret = 1;
21    const void *srcPtr = (const char *) src;
22    void *dstPtr = (char *) dst;
23    const void *const srcEnd = (const char *) srcPtr + readSize;
24
25    while (ret != 0) {
26        while (srcPtr < srcEnd && ret != 0) {
27            /* Any data within dst has been flushed at this stage */
28            size_t dstSize = dstCapacity;
29            size_t srcSize = (const char *) srcEnd - (const char *) srcPtr;
30            ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize,
31                    /* LZ4F_decompressOptions_t */ NULL);
32            FUZZ_ASSERT(!LZ4F_isError(ret));
33
34            /* Update input */
35            srcPtr = (const char *) srcPtr + srcSize;
36            dstPtr = (char *) dstPtr + dstSize;
37        }
38
39        FUZZ_ASSERT(srcPtr <= srcEnd);
40    }
41}
42
43static void compress_round_trip(const uint8_t *data, size_t size,
44                                FUZZ_dataProducer_t *producer, LZ4F_preferences_t const prefs) {
45
46    // Choose random uncompressed offset start and end by producing seeds from random data, calculate the remaining
47    // data size that will be used for compression later and use the seeds to actually calculate the offsets
48    size_t const uncompressedOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
49    size_t const uncompressedEndOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
50    size = FUZZ_dataProducer_remainingBytes(producer);
51
52    size_t const uncompressedOffset = FUZZ_getRange_from_uint32(uncompressedOffsetSeed, 0, size);
53    size_t const uncompressedEndOffset = FUZZ_getRange_from_uint32(uncompressedEndOffsetSeed, uncompressedOffset, size);
54    size_t const uncompressedSize = uncompressedEndOffset - uncompressedOffset;
55    FUZZ_ASSERT(uncompressedOffset <= uncompressedEndOffset);
56    FUZZ_ASSERT(uncompressedEndOffset <= size);
57
58    const uint8_t *const uncompressedData = data + uncompressedOffset;
59
60    size_t const dstCapacity =
61            LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) +
62            uncompressedSize;
63    char *const dst = (char *) malloc(dstCapacity);
64    size_t rtCapacity = dstCapacity;
65    char *const rt = (char *) malloc(rtCapacity);
66
67    FUZZ_ASSERT(dst);
68    FUZZ_ASSERT(rt);
69
70    /* Compression must succeed and round trip correctly. */
71    LZ4F_compressionContext_t ctx;
72    size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
73    FUZZ_ASSERT(!LZ4F_isError(ctxCreation));
74
75    size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs);
76    FUZZ_ASSERT(!LZ4F_isError(headerSize));
77    size_t compressedSize = headerSize;
78
79    /* Compress data before uncompressed offset */
80    size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity,
81                                           data, uncompressedOffset, NULL);
82    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
83    compressedSize += lz4Return;
84
85    /* Add uncompressed data */
86    lz4Return = LZ4F_uncompressedUpdate(ctx, dst + compressedSize, dstCapacity,
87                                        uncompressedData, uncompressedSize, NULL);
88    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
89    compressedSize += lz4Return;
90
91    /* Compress data after uncompressed offset */
92    lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity,
93                                    data + uncompressedEndOffset,
94                                    size - uncompressedEndOffset, NULL);
95    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
96    compressedSize += lz4Return;
97
98    /* Finish compression */
99    lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity, NULL);
100    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
101    compressedSize += lz4Return;
102
103    LZ4F_decompressOptions_t opts;
104    memset(&opts, 0, sizeof(opts));
105    opts.stableDst = 1;
106    LZ4F_dctx *dctx;
107    LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
108    FUZZ_ASSERT(dctx);
109
110    decompress(dctx, dst, rt, rtCapacity, compressedSize);
111
112    LZ4F_freeDecompressionContext(dctx);
113
114    FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!");
115
116    free(dst);
117    free(rt);
118
119    FUZZ_dataProducer_free(producer);
120    LZ4F_freeCompressionContext(ctx);
121}
122
123static void compress_independent_block_mode(const uint8_t *data, size_t size) {
124    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size);
125    LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer);
126    prefs.frameInfo.blockMode = LZ4F_blockIndependent;
127    compress_round_trip(data, size, producer, prefs);
128}
129
130
131int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
132    compress_independent_block_mode(data, size);
133    return 0;
134}
135