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