1 /*
2 * Copyright (c) Facebook, Inc.
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
26 ZSTD_CCtx *cctx = NULL;
27 static ZSTD_DCtx *dctx = NULL;
28 static uint8_t* cBuf = NULL;
29 static uint8_t* rBuf = NULL;
30 static size_t bufSize = 0;
31
makeOutBuffer(uint8_t * dst,size_t capacity,FUZZ_dataProducer_t * producer)32 static ZSTD_outBuffer makeOutBuffer(uint8_t *dst, size_t capacity,
33 FUZZ_dataProducer_t *producer)
34 {
35 ZSTD_outBuffer buffer = { dst, 0, 0 };
36
37 FUZZ_ASSERT(capacity > 0);
38 buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, capacity));
39 FUZZ_ASSERT(buffer.size <= capacity);
40
41 return buffer;
42 }
43
makeInBuffer(const uint8_t ** src,size_t * size,FUZZ_dataProducer_t * producer)44 static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size,
45 FUZZ_dataProducer_t *producer)
46 {
47 ZSTD_inBuffer buffer = { *src, 0, 0 };
48
49 FUZZ_ASSERT(*size > 0);
50 buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, *size));
51 FUZZ_ASSERT(buffer.size <= *size);
52 *src += buffer.size;
53 *size -= buffer.size;
54
55 return buffer;
56 }
57
compress(uint8_t * dst,size_t capacity,const uint8_t * src,size_t srcSize,const uint8_t * dict,size_t dictSize,FUZZ_dataProducer_t * producer,int refPrefix,ZSTD_dictContentType_e dictContentType)58 static size_t compress(uint8_t *dst, size_t capacity,
59 const uint8_t *src, size_t srcSize,
60 const uint8_t* dict, size_t dictSize,
61 FUZZ_dataProducer_t *producer, int refPrefix,
62 ZSTD_dictContentType_e dictContentType)
63 {
64 size_t dstSize = 0;
65 ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
66 FUZZ_setRandomParameters(cctx, srcSize, producer);
67
68 /* Disable checksum so we can use sizes smaller than compress bound. */
69 FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 0));
70 if (refPrefix)
71 FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced(
72 cctx, dict, dictSize,
73 dictContentType));
74 else
75 FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced(
76 cctx, dict, dictSize,
77 (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
78 dictContentType));
79
80 while (srcSize > 0) {
81 ZSTD_inBuffer in = makeInBuffer(&src, &srcSize, producer);
82 /* Mode controls the action. If mode == -1 we pick a new mode */
83 int mode = -1;
84 while (in.pos < in.size || mode != -1) {
85 ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
86 /* Previous action finished, pick a new mode. */
87 if (mode == -1) mode = FUZZ_dataProducer_uint32Range(producer, 0, 9);
88 switch (mode) {
89 case 0: /* fall-through */
90 case 1: /* fall-through */
91 case 2: {
92 size_t const ret =
93 ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
94 FUZZ_ZASSERT(ret);
95 if (ret == 0)
96 mode = -1;
97 break;
98 }
99 case 3: {
100 size_t ret =
101 ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
102 FUZZ_ZASSERT(ret);
103 /* Reset the compressor when the frame is finished */
104 if (ret == 0) {
105 ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
106 if (FUZZ_dataProducer_uint32Range(producer, 0, 7) == 0) {
107 size_t const remaining = in.size - in.pos;
108 FUZZ_setRandomParameters(cctx, remaining, producer);
109 }
110 mode = -1;
111 }
112 break;
113 }
114 case 4: {
115 ZSTD_inBuffer nullIn = { NULL, 0, 0 };
116 ZSTD_outBuffer nullOut = { NULL, 0, 0 };
117 size_t const ret = ZSTD_compressStream2(cctx, &nullOut, &nullIn, ZSTD_e_continue);
118 FUZZ_ZASSERT(ret);
119 }
120 /* fall-through */
121 default: {
122 size_t const ret =
123 ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
124 FUZZ_ZASSERT(ret);
125 mode = -1;
126 }
127 }
128 dst += out.pos;
129 dstSize += out.pos;
130 capacity -= out.pos;
131 }
132 }
133 for (;;) {
134 ZSTD_inBuffer in = {NULL, 0, 0};
135 ZSTD_outBuffer out = makeOutBuffer(dst, capacity, producer);
136 size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
137 FUZZ_ZASSERT(ret);
138
139 dst += out.pos;
140 dstSize += out.pos;
141 capacity -= out.pos;
142 if (ret == 0)
143 break;
144 }
145 return dstSize;
146 }
147
LLVMFuzzerTestOneInput(const uint8_t * src,size_t size)148 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
149 {
150 size_t neededBufSize;
151
152 /* Give a random portion of src data to the producer, to use for
153 parameter generation. The rest will be used for (de)compression */
154 FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
155 size = FUZZ_dataProducer_reserveDataPrefix(producer);
156
157 neededBufSize = ZSTD_compressBound(size) * 15;
158
159 /* Allocate all buffers and contexts if not already allocated */
160 if (neededBufSize > bufSize) {
161 free(cBuf);
162 free(rBuf);
163 cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
164 rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
165 bufSize = neededBufSize;
166 }
167 if (!cctx) {
168 cctx = ZSTD_createCCtx();
169 FUZZ_ASSERT(cctx);
170 }
171 if (!dctx) {
172 dctx = ZSTD_createDCtx();
173 FUZZ_ASSERT(dctx);
174 }
175
176 {
177 ZSTD_dictContentType_e dictContentType = FUZZ_dataProducer_uint32Range(producer, 0, 2);
178 FUZZ_dict_t dict = FUZZ_train(src, size, producer);
179 int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0;
180
181 size_t const cSize = compress(cBuf, neededBufSize, src, size, dict.buff, dict.size, producer, refPrefix, dictContentType);
182
183 if (refPrefix)
184 FUZZ_ZASSERT(ZSTD_DCtx_refPrefix_advanced(
185 dctx, dict.buff, dict.size,
186 dictContentType));
187 else
188 FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary_advanced(
189 dctx, dict.buff, dict.size,
190 (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1),
191 dictContentType));
192 size_t const rSize =
193 ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
194 FUZZ_ZASSERT(rSize);
195 FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
196 FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
197 free(dict.buff);
198 }
199
200 FUZZ_dataProducer_free(producer);
201 #ifndef STATEFUL_FUZZING
202 ZSTD_freeCCtx(cctx); cctx = NULL;
203 ZSTD_freeDCtx(dctx); dctx = NULL;
204 #endif
205 return 0;
206 }
207