Coverage Report

Created: 2026-11-12 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lz4/ossfuzz/round_trip_frame_uncompressed_fuzzer.c
Line
Count
Source
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
18
static void decompress(LZ4F_dctx *dctx, void *src, void *dst,
19
0
                       size_t dstCapacity, size_t readSize) {
20
0
    size_t ret = 1;
21
0
    const void *srcPtr = (const char *) src;
22
0
    void *dstPtr = (char *) dst;
23
0
    const void *const srcEnd = (const char *) srcPtr + readSize;
24
25
0
    while (ret != 0) {
26
0
        while (srcPtr < srcEnd && ret != 0) {
27
            /* Any data within dst has been flushed at this stage */
28
0
            size_t dstSize = dstCapacity;
29
0
            size_t srcSize = (const char *) srcEnd - (const char *) srcPtr;
30
0
            ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize,
31
0
                    /* LZ4F_decompressOptions_t */ NULL);
32
0
            FUZZ_ASSERT(!LZ4F_isError(ret));
33
34
            /* Update input */
35
0
            srcPtr = (const char *) srcPtr + srcSize;
36
0
            dstPtr = (char *) dstPtr + dstSize;
37
0
        }
38
39
0
        FUZZ_ASSERT(srcPtr <= srcEnd);
40
0
    }
41
0
}
42
43
static void compress_round_trip(const uint8_t *data, size_t size,
44
0
                                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
0
    size_t const uncompressedOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
49
0
    size_t const uncompressedEndOffsetSeed = FUZZ_dataProducer_retrieve32(producer);
50
0
    size = FUZZ_dataProducer_remainingBytes(producer);
51
52
0
    size_t const uncompressedOffset = FUZZ_getRange_from_uint32(uncompressedOffsetSeed, 0, size);
53
0
    size_t const uncompressedEndOffset = FUZZ_getRange_from_uint32(uncompressedEndOffsetSeed, uncompressedOffset, size);
54
0
    size_t const uncompressedSize = uncompressedEndOffset - uncompressedOffset;
55
0
    FUZZ_ASSERT(uncompressedOffset <= uncompressedEndOffset);
56
0
    FUZZ_ASSERT(uncompressedEndOffset <= size);
57
58
0
    const uint8_t *const uncompressedData = data + uncompressedOffset;
59
60
0
    size_t const dstCapacity =
61
0
            LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) +
62
0
            uncompressedSize;
63
0
    char *const dst = (char *) malloc(dstCapacity);
64
0
    size_t rtCapacity = dstCapacity;
65
0
    char *const rt = (char *) malloc(rtCapacity);
66
67
0
    FUZZ_ASSERT(dst);
68
0
    FUZZ_ASSERT(rt);
69
70
    /* Compression must succeed and round trip correctly. */
71
0
    LZ4F_compressionContext_t ctx;
72
0
    size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
73
0
    FUZZ_ASSERT(!LZ4F_isError(ctxCreation));
74
75
0
    size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs);
76
0
    FUZZ_ASSERT(!LZ4F_isError(headerSize));
77
0
    size_t compressedSize = headerSize;
78
79
    /* Compress data before uncompressed offset */
80
0
    size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity - compressedSize,
81
0
                                           data, uncompressedOffset, NULL);
82
0
    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
83
0
    compressedSize += lz4Return;
84
85
    /* Add uncompressed data */
86
0
    LZ4F_compressOptions_t uncOpts;
87
0
    memset(&uncOpts, 0, sizeof(uncOpts));
88
0
    uncOpts.uncompressed = 1;
89
0
    lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity - compressedSize,
90
0
                                    uncompressedData, uncompressedSize, &uncOpts);
91
0
    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
92
0
    compressedSize += lz4Return;
93
94
    /* Compress data after uncompressed offset */
95
0
    lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity - compressedSize,
96
0
                                    data + uncompressedEndOffset,
97
0
                                    size - uncompressedEndOffset, NULL);
98
0
    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
99
0
    compressedSize += lz4Return;
100
101
    /* Finish compression */
102
0
    lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity - compressedSize, NULL);
103
0
    FUZZ_ASSERT(!LZ4F_isError(lz4Return));
104
0
    compressedSize += lz4Return;
105
106
0
    LZ4F_decompressOptions_t opts;
107
0
    memset(&opts, 0, sizeof(opts));
108
0
    opts.stableDst = 1;
109
0
    LZ4F_dctx *dctx;
110
0
    LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
111
0
    FUZZ_ASSERT(dctx);
112
113
0
    decompress(dctx, dst, rt, rtCapacity, compressedSize);
114
115
0
    LZ4F_freeDecompressionContext(dctx);
116
117
0
    FUZZ_ASSERT_MSG(!memcmp(data, rt, size), "Corruption!");
118
119
0
    free(dst);
120
0
    free(rt);
121
122
0
    FUZZ_dataProducer_free(producer);
123
0
    LZ4F_freeCompressionContext(ctx);
124
0
}
125
126
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
127
    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size);
128
    LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer);   /* linked or independent blocks */
129
    prefs.nbWorkers = FUZZ_dataProducer_range32(producer, 0, 3);
130
    compress_round_trip(data, size, producer, prefs);
131
    return 0;
132
}