Coverage Report

Created: 2025-08-29 06:50

/src/zstd/tests/fuzz/dictionary_stream_round_trip.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under both the BSD-style license (found in the
6
 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7
 * in the COPYING file in the root directory of this source tree).
8
 * You may select, at your option, one of the above-listed licenses.
9
 */
10
11
/**
12
 * This fuzz target performs a zstd round-trip test (compress & decompress),
13
 * compares the result with the original, and calls abort() on corruption.
14
 */
15
16
#define ZSTD_STATIC_LINKING_ONLY
17
18
#include <stddef.h>
19
#include <stdlib.h>
20
#include <stdio.h>
21
#include <string.h>
22
#include "fuzz_helpers.h"
23
#include "zstd_helpers.h"
24
#include "fuzz_data_producer.h"
25
#include "fuzz_third_party_seq_prod.h"
26
27
ZSTD_CCtx *cctx = NULL;
28
static ZSTD_DCtx *dctx = NULL;
29
static uint8_t* cBuf = NULL;
30
static uint8_t* rBuf = NULL;
31
static size_t bufSize = 0;
32
33
static ZSTD_outBuffer makeOutBuffer(uint8_t *dst, size_t capacity,
34
                                    FUZZ_dataProducer_t *producer)
35
92.0M
{
36
92.0M
    ZSTD_outBuffer buffer = { dst, 0, 0 };
37
38
92.0M
    FUZZ_ASSERT(capacity > 0);
39
92.0M
    buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, capacity));
40
92.0M
    FUZZ_ASSERT(buffer.size <= capacity);
41
42
92.0M
    return buffer;
43
92.0M
}
44
45
static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size,
46
                                  FUZZ_dataProducer_t *producer)
47
23.0M
{
48
23.0M
    ZSTD_inBuffer buffer = { *src, 0, 0 };
49
50
23.0M
    FUZZ_ASSERT(*size > 0);
51
23.0M
    buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, *size));
52
23.0M
    FUZZ_ASSERT(buffer.size <= *size);
53
23.0M
    *src += buffer.size;
54
23.0M
    *size -= buffer.size;
55
56
23.0M
    return buffer;
57
23.0M
}
58
59
static size_t compress(uint8_t *dst, size_t capacity,
60
                        const uint8_t *src, size_t srcSize,
61
                        const uint8_t* dict, size_t dictSize,
62
                        FUZZ_dataProducer_t *producer, int refPrefix,
63
                        ZSTD_dictContentType_e dictContentType)
64
21.4k
{
65
21.4k
    size_t dstSize = 0;
66
21.4k
    ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
67
21.4k
    FUZZ_setRandomParameters(cctx, srcSize, producer);
68
69
    /* Disable checksum so we can use sizes smaller than compress bound. */
70
21.4k
    FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
71
21.4k
    if (refPrefix)
72
21.4k
        FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
73
14.7k
            cctx, dict, dictSize,
74
14.7k
            dictContentType));
75
14.7k
    else
76
21.4k
        FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
77
21.4k
            cctx, dict, dictSize,
78
21.4k
            (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
79
21.4k
            dictContentType));
80
81
23.0M
    while (srcSize > 0) {
82
23.0M
        ZSTD_inBuffer in = makeInBuffer(&src, &srcSize, producer);
83
        /* Mode controls the action. If mode == -1 we pick a new mode */
84
23.0M
        int mode = -1;
85
115M
        while (in.pos < in.size || mode != -1) {
86
92.0M
            ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
87
            /* Previous action finished, pick a new mode. */
88
92.0M
            if (mode == -1) mode = FUZZ_dataProducer_uint32Range(producer, 0, 9);
89
92.0M
            switch (mode) {
90
91.6M
                case 0: /* fall-through */
91
91.7M
                case 1: /* fall-through */
92
91.7M
                case 2: {
93
91.7M
                    size_t const ret =
94
91.7M
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
95
91.7M
                    FUZZ_ZASSERT(ret);
96
91.7M
                    if (ret == 0)
97
22.7M
                        mode = -1;
98
91.7M
                    break;
99
91.7M
                }
100
185k
                case 3: {
101
185k
                    size_t ret =
102
185k
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
103
185k
                    FUZZ_ZASSERT(ret);
104
                    /* Reset the compressor when the frame is finished */
105
185k
                    if (ret == 0) {
106
181k
                        ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
107
181k
                        if (FUZZ_dataProducer_uint32Range(producer, 0, 7) == 0) {
108
3.93k
                            size_t const remaining = in.size - in.pos;
109
3.93k
                            FUZZ_setRandomParameters(cctx, remaining, producer);
110
3.93k
                        }
111
181k
                        mode = -1;
112
181k
                    }
113
185k
                    break;
114
185k
                }
115
10.5k
                case 4: {
116
10.5k
                    ZSTD_inBuffer nullIn = { NULL, 0, 0 };
117
10.5k
                    ZSTD_outBuffer nullOut = { NULL, 0, 0 };
118
10.5k
                    size_t const ret = ZSTD_compressStream2(cctx, &nullOut, &nullIn, ZSTD_e_continue);
119
10.5k
                    FUZZ_ZASSERT(ret);
120
10.5k
                }
121
                /* fall-through */
122
58.8k
                default: {
123
58.8k
                    size_t const ret =
124
58.8k
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
125
58.8k
                    FUZZ_ZASSERT(ret);
126
58.8k
                    mode = -1;
127
58.8k
                }
128
92.0M
            }
129
92.0M
            dst += out.pos;
130
92.0M
            dstSize += out.pos;
131
92.0M
            capacity -= out.pos;
132
92.0M
        }
133
23.0M
    }
134
63.1k
    for (;;) {
135
63.1k
        ZSTD_inBuffer in = {NULL, 0, 0};
136
63.1k
        ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
137
63.1k
        size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
138
63.1k
        FUZZ_ZASSERT(ret);
139
140
63.1k
        dst += out.pos;
141
63.1k
        dstSize += out.pos;
142
63.1k
        capacity -= out.pos;
143
63.1k
        if (ret == 0)
144
21.4k
            break;
145
63.1k
    }
146
21.4k
    return dstSize;
147
21.4k
}
148
149
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
150
21.4k
{
151
21.4k
    FUZZ_SEQ_PROD_SETUP();
152
21.4k
    size_t neededBufSize;
153
154
    /* Give a random portion of src data to the producer, to use for
155
    parameter generation. The rest will be used for (de)compression */
156
21.4k
    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
157
21.4k
    size = FUZZ_dataProducer_reserveDataPrefix(producer);
158
159
21.4k
    neededBufSize = ZSTD_compressBound(size) * 15;
160
161
    /* Allocate all buffers and contexts if not already allocated */
162
21.4k
    if (neededBufSize > bufSize) {
163
1.08k
        free(cBuf);
164
1.08k
        free(rBuf);
165
1.08k
        cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
166
1.08k
        rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
167
1.08k
        bufSize = neededBufSize;
168
1.08k
    }
169
21.4k
    if (!cctx) {
170
21.4k
        cctx = ZSTD_createCCtx();
171
21.4k
        FUZZ_ASSERT(cctx);
172
21.4k
    }
173
21.4k
    if (!dctx) {
174
21.4k
        dctx = ZSTD_createDCtx();
175
21.4k
        FUZZ_ASSERT(dctx);
176
21.4k
    }
177
178
21.4k
    {
179
21.4k
        ZSTD_dictContentType_e dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
180
21.4k
        FUZZ_dict_t dict = FUZZ_train(src, size, producer);
181
21.4k
        int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
182
183
21.4k
        size_t const cSize = compress(cBuf, neededBufSize, src, size, dict.buff, dict.size, producer, refPrefix, dictContentType);
184
185
21.4k
        if (refPrefix)
186
21.4k
            FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
187
14.7k
                dctx, dict.buff, dict.size,
188
14.7k
                dictContentType));
189
14.7k
        else
190
21.4k
            FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
191
21.4k
                dctx, dict.buff, dict.size,
192
21.4k
                (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
193
21.4k
                dictContentType));
194
21.4k
        size_t const rSize =
195
21.4k
            ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
196
21.4k
        FUZZ_ZASSERT(rSize);
197
21.4k
        FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
198
21.4k
        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
199
21.4k
        free(dict.buff);
200
21.4k
    }
201
202
0
    FUZZ_dataProducer_free(producer);
203
21.4k
#ifndef STATEFUL_FUZZING
204
21.4k
    ZSTD_freeCCtx(cctx); cctx = NULL;
205
21.4k
    ZSTD_freeDCtx(dctx); dctx = NULL;
206
21.4k
#endif
207
21.4k
    FUZZ_SEQ_PROD_TEARDOWN();
208
21.4k
    return 0;
209
21.4k
}