Coverage Report

Created: 2024-07-23 06:39

/src/zstd/tests/fuzz/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
64.1M
{
36
64.1M
    ZSTD_outBuffer buffer = { dst, 0, 0 };
37
38
64.1M
    FUZZ_ASSERT(capacity > 0);
39
64.1M
    buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, capacity));
40
64.1M
    FUZZ_ASSERT(buffer.size <= capacity);
41
42
64.1M
    return buffer;
43
64.1M
}
44
45
static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size,
46
                                  FUZZ_dataProducer_t *producer)
47
13.9M
{
48
13.9M
    ZSTD_inBuffer buffer = { *src, 0, 0 };
49
50
13.9M
    FUZZ_ASSERT(*size > 0);
51
13.9M
    buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, *size));
52
13.9M
    FUZZ_ASSERT(buffer.size <= *size);
53
13.9M
    *src += buffer.size;
54
13.9M
    *size -= buffer.size;
55
56
13.9M
    return buffer;
57
13.9M
}
58
59
static size_t compress(uint8_t *dst, size_t capacity,
60
                       const uint8_t *src, size_t srcSize,
61
                     FUZZ_dataProducer_t *producer)
62
31.1k
{
63
31.1k
    size_t dstSize = 0;
64
31.1k
    ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
65
31.1k
    FUZZ_setRandomParameters(cctx, srcSize, producer);
66
31.1k
    int maxBlockSize;
67
31.1k
    FUZZ_ZASSERT(ZSTD_CCtx_getParameter(cctx, ZSTD_c_maxBlockSize, &maxBlockSize));
68
69
13.9M
    while (srcSize > 0) {
70
13.9M
        ZSTD_inBuffer in = makeInBuffer(&src, &srcSize, producer);
71
        /* Mode controls the action. If mode == -1 we pick a new mode */
72
13.9M
        int mode = -1;
73
77.8M
        while (in.pos < in.size || mode != -1) {
74
63.9M
            ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
75
            /* Previous action finished, pick a new mode. */
76
63.9M
            if (mode == -1) mode = FUZZ_dataProducer_uint32Range(producer, 0, 9);
77
63.9M
            switch (mode) {
78
63.5M
                case 0: /* fall-through */
79
63.6M
                case 1: /* fall-through */
80
63.6M
                case 2: {
81
63.6M
                    size_t const ret =
82
63.6M
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
83
63.6M
                    FUZZ_ZASSERT(ret);
84
63.6M
                    if (ret == 0)
85
13.6M
                        mode = -1;
86
63.6M
                    break;
87
63.6M
                }
88
130k
                case 3: {
89
130k
                    size_t ret =
90
130k
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
91
130k
                    FUZZ_ZASSERT(ret);
92
                    /* Reset the compressor when the frame is finished */
93
130k
                    if (ret == 0) {
94
118k
                        ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
95
118k
                        if (FUZZ_dataProducer_uint32Range(producer, 0, 7) == 0) {
96
4.18k
                            size_t const remaining = in.size - in.pos;
97
4.18k
                            FUZZ_setRandomParameters(cctx, remaining, producer);
98
                            /* Always use the same maxBlockSize */
99
4.18k
                            FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, maxBlockSize));
100
4.18k
                        }
101
118k
                        mode = -1;
102
118k
                    }
103
130k
                    break;
104
130k
                }
105
130k
                case 4: {
106
9.36k
                    ZSTD_inBuffer nullIn = { NULL, 0, 0 };
107
9.36k
                    ZSTD_outBuffer nullOut = { NULL, 0, 0 };
108
9.36k
                    size_t const ret = ZSTD_compressStream2(cctx, &nullOut, &nullIn, ZSTD_e_continue);
109
9.36k
                    FUZZ_ZASSERT(ret);
110
9.36k
                }
111
                /* fall-through */
112
86.4k
                default: {
113
86.4k
                    size_t const ret =
114
86.4k
                        ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
115
86.4k
                    FUZZ_ZASSERT(ret);
116
86.4k
                    mode = -1;
117
86.4k
                }
118
63.9M
            }
119
63.9M
            dst += out.pos;
120
63.9M
            dstSize += out.pos;
121
63.9M
            capacity -= out.pos;
122
63.9M
        }
123
13.9M
    }
124
239k
    for (;;) {
125
239k
        ZSTD_inBuffer in = {NULL, 0, 0};
126
239k
        ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
127
239k
        size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
128
239k
        FUZZ_ZASSERT(ret);
129
130
239k
        dst += out.pos;
131
239k
        dstSize += out.pos;
132
239k
        capacity -= out.pos;
133
239k
        if (ret == 0)
134
31.1k
            break;
135
239k
    }
136
31.1k
    return dstSize;
137
31.1k
}
138
139
static size_t decompress(void* dst, size_t dstCapacity, void const* src, size_t srcSize, FUZZ_dataProducer_t* producer)
140
31.1k
{
141
31.1k
    ZSTD_inBuffer in = {src, srcSize, 0};
142
31.1k
    ZSTD_outBuffer out = {dst, dstCapacity, 0};
143
31.1k
    int maxBlockSize;
144
31.1k
    FUZZ_ZASSERT(ZSTD_CCtx_getParameter(cctx, ZSTD_c_maxBlockSize, &maxBlockSize));
145
31.1k
    if (FUZZ_dataProducer_uint32Range(producer, 0, 1)) {
146
7.58k
        FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_maxBlockSize, maxBlockSize));
147
7.58k
    }
148
181k
    while (in.pos < in.size) {
149
150k
        size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
150
150k
        FUZZ_ZASSERT(ret);
151
150k
        FUZZ_ASSERT(ret == 0);
152
150k
    }
153
31.1k
    return out.pos;
154
31.1k
}
155
156
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
157
31.1k
{
158
31.1k
    FUZZ_SEQ_PROD_SETUP();
159
31.1k
    size_t neededBufSize;
160
161
    /* Give a random portion of src data to the producer, to use for
162
    parameter generation. The rest will be used for (de)compression */
163
31.1k
    FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
164
31.1k
    size = FUZZ_dataProducer_reserveDataPrefix(producer);
165
166
31.1k
    neededBufSize = ZSTD_compressBound(size) * 15;
167
168
    /* Allocate all buffers and contexts if not already allocated */
169
31.1k
    if (neededBufSize > bufSize) {
170
2.23k
        free(cBuf);
171
2.23k
        free(rBuf);
172
2.23k
        cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
173
2.23k
        rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
174
2.23k
        bufSize = neededBufSize;
175
2.23k
    }
176
31.1k
    if (!cctx) {
177
31.1k
        cctx = ZSTD_createCCtx();
178
31.1k
        FUZZ_ASSERT(cctx);
179
31.1k
    }
180
31.1k
    if (!dctx) {
181
31.1k
        dctx = ZSTD_createDCtx();
182
31.1k
        FUZZ_ASSERT(dctx);
183
31.1k
    }
184
185
31.1k
    {
186
31.1k
        size_t const cSize = compress(cBuf, neededBufSize, src, size, producer);
187
31.1k
        size_t const rSize = decompress(rBuf, neededBufSize, cBuf, cSize, producer);
188
31.1k
        FUZZ_ZASSERT(rSize);
189
31.1k
        FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
190
31.1k
        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
191
192
        /* Test in-place decompression (note the macro doesn't work in this case) */
193
31.1k
        {
194
31.1k
            size_t const margin = ZSTD_decompressionMargin(cBuf, cSize);
195
31.1k
            size_t const outputSize = size + margin;
196
31.1k
            char* const output = (char*)FUZZ_malloc(outputSize);
197
31.1k
            char* const input = output + outputSize - cSize;
198
31.1k
            size_t dSize;
199
31.1k
            FUZZ_ASSERT(outputSize >= cSize);
200
31.1k
            memcpy(input, cBuf, cSize);
201
202
31.1k
            dSize = ZSTD_decompressDCtx(dctx, output, outputSize, input, cSize);
203
31.1k
            FUZZ_ZASSERT(dSize);
204
31.1k
            FUZZ_ASSERT_MSG(dSize == size, "Incorrect regenerated size");
205
31.1k
            FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, output, size), "Corruption!");
206
207
31.1k
            free(output);
208
31.1k
        }
209
31.1k
    }
210
211
0
    FUZZ_dataProducer_free(producer);
212
31.1k
#ifndef STATEFUL_FUZZING
213
31.1k
    ZSTD_freeCCtx(cctx); cctx = NULL;
214
31.1k
    ZSTD_freeDCtx(dctx); dctx = NULL;
215
31.1k
#endif
216
31.1k
    FUZZ_SEQ_PROD_TEARDOWN();
217
31.1k
    return 0;
218
31.1k
}